Skip to content

Commit

Permalink
chore(uploads): Reorganise, change uploads package to storage (#11411)
Browse files Browse the repository at this point in the history
  • Loading branch information
dac09 authored Sep 2, 2024
1 parent 4c7199d commit f9978fc
Show file tree
Hide file tree
Showing 29 changed files with 209 additions and 164 deletions.
38 changes: 38 additions & 0 deletions .changesets/11411.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
- chore(uploads): Reorganise, change uploads package to storage (#11411) by @dac09

This PR does the following:

- renames `@redwoodjs/uploads` -> `@redwoodjs/storage`
- Renames `processors` -> `savers`

They now look like this:

```ts
// reads much nicer ✅
const processedInput = await saveFiles.forProfile(input)

// avatar is a File becomes a string
// processedInput.avatar =
// '/basePath/uploads/profile-avatar-01J6ACDPWAER8B1SAXPKQK5YA1.png'

const profile = await db.profile.update({
data: processedInput,
where: { id },
})
```

even though when you export it, it looks weird

```ts
// api/src/lib/uploads.ts
const { saveFiles, storagePrismaExtension } = setupStorage({
//...
})

export { saveFiles, storagePrismaExtension }
```

The naming convention is:
`for<Model>` - takes inputs in the shape of a Prisma data for that model but with Files instead of strings. The files get saved to storage, and a key/path is replaced in the value.

`inList` - does the same, but only takes an array of File, and returns an array of stored paths/keys
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ module.exports = {
'packages/babel-config/src/__tests__/__fixtures__/**/*',
'packages/codemods/**/__testfixtures__/**/*',
'packages/cli/**/__testfixtures__/**/*',
'packages/uploads/src/__tests__/prisma-client/*',
'packages/storage/src/__tests__/prisma-client/*',
],
rules: {
curly: 'error',
Expand Down
File renamed without changes.
35 changes: 17 additions & 18 deletions packages/uploads/README.md → packages/storage/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# `@redwoodjs/uploads`
# `@redwoodjs/storage`

This package houses

Expand All @@ -14,13 +14,12 @@ In `api/src/uploads.ts` - setup uploads - processors, storage and the prisma ext

```ts
// api/src/lib/uploads.ts
nua
import { setupUploads, UploadsConfig } from '@redwoodjs/storage'
import { FileSystemStorage } from '@redwoodjs/storage/FileSystemStorage'
import { UrlSigner } from '@redwoodjs/storage/UrlSigner'

import { UploadsConfig } from '@redwoodjs/uploads'
import { setupUploads } from '@redwoodjs/uploads'
import { FileSystemStorage } from '@redwoodjs/uploads/FileSystemStorage'
import { UrlSigner } from '@redwoodjs/uploads/signedUrl'

const uploadConfig: UploadsConfig = {
const uploadsConfig: UploadsConfig = {
// 👇 prisma model
profile: {
// 👇 pass in fields that are going to be File uploads
Expand All @@ -30,7 +29,7 @@ const uploadConfig: UploadsConfig = {
}

// 👇 exporting these allows you access elsewhere on the api side
export const storage = new FileSystemStorage({
export const fsStorage = new FileSystemStorage({
baseDir: './uploads',
})

Expand All @@ -40,13 +39,13 @@ export const urlSigner = new UrlSigner({
endpoint: '/signedUrl',
})

const { uploadsProcessors, prismaExtension, fileListProcessor } = setupUploads(
uploadConfig,
storage,
const { saveFiles, storagePrismaExtension } = setupStorage({
uploadsConfig,
storageAdapter: fsStorage,
urlSigner,
)
})

export { uploadsProcessors, prismaExtension, fileListProcessor }
export { saveFiles, storagePrismaExtension }
```

### Configuring db to use the prisma extension
Expand All @@ -59,7 +58,7 @@ import { PrismaClient } from '@prisma/client'
import { emitLogLevels, handlePrismaLogging } from '@redwoodjs/api/logger'

import { logger } from './logger'
import { prismaExtension } from './uploads'
import { storagePrismaExtension } from './uploads'

// 👇 Notice here we create prisma client, and don't export it yet
export const prismaClient = new PrismaClient({
Expand All @@ -73,7 +72,7 @@ handlePrismaLogging({
})

// 👇 Export db after adding uploads extension
export const db = prismaClient.$extends(prismaExtension)
export const db = prismaClient.$extends(storagePrismaExtension)
```

## Using Prisma extension
Expand All @@ -99,9 +98,9 @@ export const profile: QueryResolvers['profile'] = async ({ id }) => {
}
```

## Using processors
## Using `saveFiles`

In your services, you can use the preconfigured "processors" to convert Files to strings for Prisma to save into the database. The processors, and storage adapters determine where the file is saved.
In your services, you can use the preconfigured "processors" - exported as `saveFiles` to convert Files to paths on storage, for Prisma to save into the database. The processors, and storage adapters determine where the file is saved.

```ts
// api/src/services/profiles/profiles.ts
Expand All @@ -110,7 +109,7 @@ export const updateProfile: MutationResolvers['updateProfile'] = async ({
id,
input,
}) => {
const processedInput = await uploadsProcessors.processProfileUploads(input)
const processedInput = await saveFiles.forProfile(input)

// This becomes a string 👇
// The configuration on where it was saved is passed when we setup uploads in src/lib/uploads.ts
Expand Down
File renamed without changes.
File renamed without changes.
32 changes: 13 additions & 19 deletions packages/uploads/package.json → packages/storage/package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "@redwoodjs/uploads",
"name": "@redwoodjs/storage",
"version": "7.0.0",
"repository": {
"type": "git",
"url": "git+https://github.com/redwoodjs/redwood.git",
"directory": "packages/uploads"
"directory": "packages/storage"
},
"license": "MIT",
"type": "module",
Expand All @@ -20,26 +20,20 @@
}
},
"./FileSystemStorage": {
"require": "./dist/cjs/FileSystemStorage.js",
"import": "./dist/FileSystemStorage.js"
"require": "./dist/cjs/adapters/FileSystemStorage/FileSystemStorage.js",
"import": "./dist/adapters/FileSystemStorage/FileSystemStorage.js"
},
"./MemoryStorage": {
"require": "./dist/cjs/MemoryStorage.js",
"import": "./dist/MemoryStorage.js"
"require": "./dist/cjs/adapters/MemoryStorage/MemoryStorage.js",
"import": "./dist/adapters/MemoryStorage/MemoryStorage.js"
},
"./signedUrl": {
"require": "./dist/cjs/signedUrls.js",
"import": "./dist/signedUrls.js"
"./BaseStorageAdapter": {
"require": "./dist/cjs/adapters/BaseStorageAdapter.js",
"import": "./dist/adapters/BaseStorageAdapter.js"
},
"./prisma": {
"require": {
"types": "./dist/cjs/prismaExtension.d.ts",
"default": "./dist/cjs/prismaExtension.js"
},
"import": {
"types": "./dist/prismaExtension.d.ts",
"default": "./dist/prismaExtension.js"
}
"./UrlSigner": {
"require": "./dist/cjs/UrlSigner.js",
"import": "./dist/UrlSigner.js"
}
},
"files": [
Expand All @@ -48,7 +42,7 @@
],
"scripts": {
"build": "yarn setup:test && tsx ./build.mts",
"build:pack": "yarn pack -o redwoodjs-uploads.tgz",
"build:pack": "yarn pack -o redwoodjs-storage.tgz",
"build:types": "tsc --build --verbose",
"build:types-cjs": "tsc --build --verbose tsconfig.types-cjs.json",
"check:attw": "tsx attw.ts",
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { describe, it, expect } from 'vitest'

import { ensurePosixPath } from '@redwoodjs/project-config'

import { createUploadProcessors } from '../createProcessors.js'
import { MemoryStorage } from '../MemoryStorage.js'
import { MemoryStorage } from '../adapters/MemoryStorage/MemoryStorage.js'
import { createUploadSavers } from '../createSavers.js'
import type { UploadsConfig } from '../prismaExtension.js'

const memStore = new MemoryStorage({
Expand All @@ -19,12 +19,16 @@ const uploadsConfig: UploadsConfig = {
},
}

describe('Create processors', () => {
const processors = createUploadProcessors(uploadsConfig, memStore)
describe('Create savers', () => {
const fileToStorage = createUploadSavers(uploadsConfig, memStore)

it('should create processors with CapitalCased model name', () => {
expect(processors.processDumboUploads).toBeDefined()
expect(processors.processDummyUploads).toBeDefined()
it('should create savers with CapitalCased model name', () => {
expect(fileToStorage.forDumbo).toBeDefined()
expect(fileToStorage.forDummy).toBeDefined()

// These are in the schema but not in the config
expect(fileToStorage.forBook).not.toBeDefined()
expect(fileToStorage.forNoUploadFields).not.toBeDefined()
})

it('Should replace file types with location strings', async () => {
Expand All @@ -37,7 +41,7 @@ describe('Create processors', () => {
}),
}

const result = await processors.processDumboUploads(data)
const result = await fileToStorage.forDumbo(data)

// Location strings in this format: {baseDir/{model}-{field}-{ulid}.{ext}
expect(ensurePosixPath(result.firstUpload)).toMatch(
Expand All @@ -63,15 +67,15 @@ describe('Create processors', () => {
}),
}

const fileNameOverrideOnly = await processors.processDummyUploads(data, {
const fileNameOverrideOnly = await fileToStorage.forDummy(data, {
fileName: 'overridden',
})

const pathOverrideOnly = await processors.processDummyUploads(data, {
const pathOverrideOnly = await fileToStorage.forDummy(data, {
path: '/bazinga',
})

const bothOverride = await processors.processDummyUploads(data, {
const bothOverride = await fileToStorage.forDummy(data, {
path: '/bazinga',
fileName: 'overridden',
})
Expand All @@ -98,14 +102,14 @@ describe('Create processors', () => {
}),
}

const noOverride = await processors.processDummyUploads(data)
const noOverride = await fileToStorage.forDummy(data)

// No extension
expect(ensurePosixPath(noOverride.uploadField)).toMatch(
/\/memory_store_basedir\/.*[^.]+$/,
)

const withOverride = await processors.processDummyUploads(data, {
const withOverride = await fileToStorage.forDummy(data, {
fileName: 'hello',
})

Expand All @@ -119,7 +123,7 @@ describe('Create processors', () => {
// Problem is - in the database world, a string[] is not a thing
// so we need a generic way of doing this
describe('FileList processing', () => {
const processors = createUploadProcessors(uploadsConfig, memStore)
const savers = createUploadSavers(uploadsConfig, memStore)

const notPrismaData = [
new File(['Hello'], 'hello.png', {
Expand All @@ -131,7 +135,7 @@ describe('FileList processing', () => {
]

it('Should handle FileLists', async () => {
const result = await processors.processFileList(notPrismaData)
const result = await savers.inList(notPrismaData)

expect(result).toHaveLength(2)

Expand All @@ -144,7 +148,7 @@ describe('FileList processing', () => {
})

it('Should handle FileLists with SaveOptions', async () => {
const result = await processors.processFileList(notPrismaData, {
const result = await savers.inList(notPrismaData, {
path: '/bazinga_not_mem_store',
})

Expand All @@ -158,7 +162,7 @@ describe('FileList processing', () => {
})

it('Should handle empty FileLists', async () => {
const promise = processors.processFileList()
const promise = savers.inList()

await expect(promise).resolves.not.toThrow()
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { describe, it, vi, expect, beforeEach, beforeAll } from 'vitest'

import { ensurePosixPath } from '@redwoodjs/project-config'

import { FileSystemStorage } from '../FileSystemStorage.js'
import { setupUploads } from '../index.js'
import { FileSystemStorage } from '../adapters/FileSystemStorage/FileSystemStorage.js'
import { setupStorage } from '../index.js'
import type { UploadsConfig } from '../prismaExtension.js'

// @MARK: use the local prisma client in the test
Expand All @@ -31,7 +31,7 @@ vi.mock('node:fs', () => ({
}))

describe('Query extensions', () => {
const uploadConfig: UploadsConfig = {
const uploadsConfig: UploadsConfig = {
dummy: {
fields: 'uploadField',
},
Expand All @@ -40,14 +40,14 @@ describe('Query extensions', () => {
},
}

const { prismaExtension, uploadsProcessors } = setupUploads(
uploadConfig,
new FileSystemStorage({
const { storagePrismaExtension, saveFiles } = setupStorage({
uploadsConfig: uploadsConfig,
storageAdapter: new FileSystemStorage({
baseDir: '/tmp',
}),
)
})

const prismaClient = new PrismaClient().$extends(prismaExtension)
const prismaClient = new PrismaClient().$extends(storagePrismaExtension)

beforeEach(() => {
vi.resetAllMocks()
Expand All @@ -59,7 +59,7 @@ describe('Query extensions', () => {

describe('create', () => {
it('create will save files', async () => {
const processedData = await uploadsProcessors.processDummyUploads({
const processedData = await saveFiles.forDummy({
uploadField: sampleFile,
})

Expand Down
Loading

0 comments on commit f9978fc

Please sign in to comment.