diff --git a/__fixtures__/example-todo-main/web/src/graphql/graphql.ts b/__fixtures__/example-todo-main/web/src/graphql/graphql.ts index 6973b81d64ef..aac6283994fd 100644 --- a/__fixtures__/example-todo-main/web/src/graphql/graphql.ts +++ b/__fixtures__/example-todo-main/web/src/graphql/graphql.ts @@ -15,6 +15,7 @@ export type Scalars = { Int: { input: number; output: number; } Float: { input: number; output: number; } BigInt: { input: any; output: any; } + Byte: { input: any; output: any; } Date: { input: any; output: any; } DateTime: { input: any; output: any; } JSON: { input: any; output: any; } diff --git a/docs/docs/realtime.md b/docs/docs/realtime.md index 39ef6fd5a2c9..f56b2880c48a 100644 --- a/docs/docs/realtime.md +++ b/docs/docs/realtime.md @@ -260,7 +260,7 @@ subscription CountdownFromInterval { } ``` -This example showcases how a subscription can yields its own response. +This example showcases how a subscription yields its own response. ## Live Queries diff --git a/packages/api-server/src/watch.ts b/packages/api-server/src/watch.ts index 29fd4aedf5ed..a4aa51a4fe12 100644 --- a/packages/api-server/src/watch.ts +++ b/packages/api-server/src/watch.ts @@ -32,6 +32,7 @@ const argv = yargs(hideBin(process.argv)) description: 'Debugging port', type: 'number', }) + // `port` is not used when server-file is used .option('port', { alias: 'p', description: 'Port', @@ -133,15 +134,13 @@ const buildAndRestart = async ({ // Check if experimental server file exists const serverFile = resolveFile(`${rwjsPaths.api.dist}/server`) if (serverFile) { - const separator = chalk.hex('#ff845e')( - '------------------------------------------------------------------' - ) + const separator = chalk.hex('#ff845e')('-'.repeat(79)) console.log( [ separator, `🧪 ${chalk.green('Experimental Feature')} 🧪`, separator, - 'Using the experimental API server file at api/dist/server.js', + 'Using the experimental API server file at api/dist/server.js (in watch mode)', separator, ].join('\n') ) diff --git a/packages/cli/src/commands/__tests__/dev.test.js b/packages/cli/src/commands/__tests__/dev.test.js index 7de47c38266a..595e453b7644 100644 --- a/packages/cli/src/commands/__tests__/dev.test.js +++ b/packages/cli/src/commands/__tests__/dev.test.js @@ -25,9 +25,12 @@ jest.mock('@redwoodjs/internal/dist/dev', () => { }) jest.mock('@redwoodjs/project-config', () => { + const actualProjectConfig = jest.requireActual('@redwoodjs/project-config') + return { getConfig: jest.fn(), getConfigPath: () => '/mocked/project/redwood.toml', + resolveFile: actualProjectConfig.resolveFile, getPaths: () => { return { api: { @@ -104,8 +107,8 @@ describe('yarn rw dev', () => { 'yarn cross-env NODE_ENV=development rw-vite-dev' ) - expect(apiCommand.command).toMatchInlineSnapshot( - `"yarn cross-env NODE_ENV=development NODE_OPTIONS="--enable-source-maps" yarn nodemon --quiet --watch "/mocked/project/redwood.toml" --exec "yarn rw-api-server-watch --port 8911 --debug-port 18911 | rw-log-formatter""` + expect(apiCommand.command.replace(/\s+/g, ' ')).toEqual( + 'yarn cross-env NODE_ENV=development NODE_OPTIONS="--enable-source-maps" yarn nodemon --quiet --watch "/mocked/project/redwood.toml" --exec "yarn rw-api-server-watch --port 8911 --debug-port 18911 | rw-log-formatter"' ) expect(generateCommand.command).toEqual('yarn rw-gen-watch') @@ -143,8 +146,8 @@ describe('yarn rw dev', () => { 'yarn cross-env NODE_ENV=development rw-dev-fe' ) - expect(apiCommand.command).toMatchInlineSnapshot( - `"yarn cross-env NODE_ENV=development NODE_OPTIONS="--enable-source-maps" yarn nodemon --quiet --watch "/mocked/project/redwood.toml" --exec "yarn rw-api-server-watch --port 8911 --debug-port 18911 | rw-log-formatter""` + expect(apiCommand.command.replace(/\s+/g, ' ')).toEqual( + 'yarn cross-env NODE_ENV=development NODE_OPTIONS="--enable-source-maps" yarn nodemon --quiet --watch "/mocked/project/redwood.toml" --exec "yarn rw-api-server-watch --port 8911 --debug-port 18911 | rw-log-formatter"' ) expect(generateCommand.command).toEqual('yarn rw-gen-watch') @@ -175,7 +178,7 @@ describe('yarn rw dev', () => { const apiCommand = find(concurrentlyArgs, { name: 'api' }) - expect(apiCommand.command).toContain( + expect(apiCommand.command.replace(/\s+/g, ' ')).toContain( 'yarn rw-api-server-watch --port 8911 --debug-port 90909090' ) }) diff --git a/packages/cli/src/commands/devHandler.js b/packages/cli/src/commands/devHandler.js index e4ed83085460..16ab1e2b71cd 100644 --- a/packages/cli/src/commands/devHandler.js +++ b/packages/cli/src/commands/devHandler.js @@ -5,7 +5,11 @@ import fs from 'fs-extra' import { recordTelemetryAttributes } from '@redwoodjs/cli-helpers' import { shutdownPort } from '@redwoodjs/internal/dist/dev' -import { getConfig, getConfigPath } from '@redwoodjs/project-config' +import { + getConfig, + getConfigPath, + resolveFile, +} from '@redwoodjs/project-config' import { errorTelemetry } from '@redwoodjs/telemetry' import { getPaths } from '../lib' @@ -32,6 +36,9 @@ export const handler = async ({ const rwjsPaths = getPaths() + // Check if experimental server file exists + const serverFile = resolveFile(`${rwjsPaths.api.dist}/server`) + // Starting values of ports from config (redwood.toml) let apiPreferredPort = parseInt(getConfig().api.port) let webPreferredPort = parseInt(getConfig().web.port) @@ -42,8 +49,10 @@ export const handler = async ({ let webAvailablePort = webPreferredPort let webPortChangeNeeded = false - // Check api port - if (side.includes('api')) { + // Check api port, unless there's a serverFile. If there is a serverFile, we + // don't know what port will end up being used in the end. It's up to the + // author of the server file to decide and handle that + if (side.includes('api') && !serverFile) { apiAvailablePort = await getFreePort(apiPreferredPort) if (apiAvailablePort === -1) { exitWithError(undefined, { @@ -76,17 +85,23 @@ export const handler = async ({ // Check for port conflict and exit with message if found if (apiPortChangeNeeded || webPortChangeNeeded) { - let message = `The currently configured ports for the development server are unavailable. Suggested changes to your ports, which can be changed in redwood.toml, are:\n` - message += apiPortChangeNeeded - ? ` - API to use port ${apiAvailablePort} instead of your currently configured ${apiPreferredPort}\n` - : `` - message += webPortChangeNeeded - ? ` - Web to use port ${webAvailablePort} instead of your currently configured ${webPreferredPort}\n` - : `` - message += `\nCannot run the development server until your configured ports are changed or become available.` - exitWithError(undefined, { - message, - }) + const message = [ + 'The currently configured ports for the development server are', + 'unavailable. Suggested changes to your ports, which can be changed in', + 'redwood.toml, are:\n', + apiPortChangeNeeded && ` - API to use port ${apiAvailablePort} instead`, + apiPortChangeNeeded && 'of your currently configured', + apiPortChangeNeeded && `${apiPreferredPort}\n`, + webPortChangeNeeded && ` - Web to use port ${webAvailablePort} instead`, + webPortChangeNeeded && 'of your currently configured', + webPortChangeNeeded && `${webPreferredPort}\n`, + '\nCannot run the development server until your configured ports are', + 'changed or become available.', + ] + .filter(Boolean) + .join(' ') + + exitWithError(undefined, { message }) } if (side.includes('api')) { @@ -104,13 +119,17 @@ export const handler = async ({ console.error(c.error(e.message)) } - try { - await shutdownPort(apiAvailablePort) - } catch (e) { - errorTelemetry(process.argv, `Error shutting down "api": ${e.message}`) - console.error( - `Error whilst shutting down "api" port: ${c.error(e.message)}` - ) + // Again, if a server file is configured, we don't know what port it'll end + // up using + if (!serverFile) { + try { + await shutdownPort(apiAvailablePort) + } catch (e) { + errorTelemetry(process.argv, `Error shutting down "api": ${e.message}`) + console.error( + `Error whilst shutting down "api" port: ${c.error(e.message)}` + ) + } } } @@ -142,7 +161,7 @@ export const handler = async ({ return `--debug-port ${apiDebugPortInToml}` } - // Dont pass in debug port flag, unless configured + // Don't pass in debug port flag, unless configured return '' } @@ -178,7 +197,16 @@ export const handler = async ({ const jobs = { api: { name: 'api', - command: `yarn cross-env NODE_ENV=development NODE_OPTIONS="${getDevNodeOptions()}" yarn nodemon --quiet --watch "${redwoodConfigPath}" --exec "yarn rw-api-server-watch --port ${apiAvailablePort} ${getApiDebugFlag()} | rw-log-formatter"`, + command: [ + `yarn cross-env NODE_ENV=development NODE_OPTIONS="${getDevNodeOptions()}"`, + ' yarn nodemon', + ' --quiet', + ` --watch "${redwoodConfigPath}"`, + ' --exec "yarn rw-api-server-watch', + ` --port ${apiAvailablePort}`, + ` ${getApiDebugFlag()}`, + ' | rw-log-formatter"', + ].join(' '), prefixColor: 'cyan', runWhen: () => fs.existsSync(rwjsPaths.api.src), }, diff --git a/packages/cli/src/commands/generate/__tests__/helpers.test.js b/packages/cli/src/commands/generate/__tests__/helpers.test.js index 4ab7310e733f..5ba670cb4772 100644 --- a/packages/cli/src/commands/generate/__tests__/helpers.test.js +++ b/packages/cli/src/commands/generate/__tests__/helpers.test.js @@ -541,6 +541,10 @@ describe('mapPrismaScalarToPagePropTsType', () => { expect(helpers.mapPrismaScalarToPagePropTsType('DateTime')).toBe('string') }) + it('maps scalar type Bytes to TS type Buffer', () => { + expect(helpers.mapPrismaScalarToPagePropTsType('Bytes')).toBe('Buffer') + }) + it('maps all other type not-known to TS to unknown', () => { expect(helpers.mapPrismaScalarToPagePropTsType('Json')).toBe('unknown') }) diff --git a/packages/cli/src/commands/generate/helpers.js b/packages/cli/src/commands/generate/helpers.js index e3d48ff99c7d..c17bb6eb2985 100644 --- a/packages/cli/src/commands/generate/helpers.js +++ b/packages/cli/src/commands/generate/helpers.js @@ -298,7 +298,7 @@ export const mapRouteParamTypeToTsType = (paramType) => { return routeParamToTsType[paramType] || 'unknown' } -/** @type {(scalarType: 'String' | 'Boolean' | 'Int' | 'BigInt' | 'Float' | 'Decimal' | 'DateTime' ) => string } **/ +/** @type {(scalarType: 'String' | 'Boolean' | 'Int' | 'BigInt' | 'Float' | 'Decimal' | 'DateTime' | 'Bytes' ) => string } **/ export const mapPrismaScalarToPagePropTsType = (scalarType) => { const prismaScalarToTsType = { String: 'string', @@ -308,6 +308,7 @@ export const mapPrismaScalarToPagePropTsType = (scalarType) => { Float: 'number', Decimal: 'number', DateTime: 'string', + Bytes: 'Buffer', } return prismaScalarToTsType[scalarType] || 'unknown' } diff --git a/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap b/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap index 4121cc48665d..bbe1df8bc373 100644 --- a/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap +++ b/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap @@ -728,6 +728,63 @@ exports[`with graphql documentations in javascript mode creates a multi word sdl " `; +exports[`with graphql documentations in javascript mode creates a sdl file with Byte definitions 1`] = ` +"export const schema = gql\` + """ + Representation of Key. + """ + type Key { + "Description for id." + id: Int! + + "Description for publicKey." + publicKey: Byte! + } + + """ + About queries + """ + type Query { + "Fetch Keys." + keys: [Key!]! @requireAuth + + "Fetch a Key by id." + key(id: Int!): Key @requireAuth + } + + """ + Autogenerated input type of InputKey. + """ + input CreateKeyInput { + "Description for publicKey." + publicKey: Byte! + } + + """ + Autogenerated input type of UpdateKey. + """ + input UpdateKeyInput { + "Description for publicKey." + publicKey: Byte + } + + """ + About mutations + """ + type Mutation { + "Creates a new Key." + createKey(input: CreateKeyInput!): Key! @requireAuth + + "Updates an existing Key." + updateKey(id: Int!, input: UpdateKeyInput!): Key! @requireAuth + + "Deletes an existing Key." + deleteKey(id: Int!): Key! @requireAuth + } +\` +" +`; + exports[`with graphql documentations in javascript mode creates a sdl file with enum definitions 1`] = ` "export const schema = gql\` """ @@ -1163,6 +1220,63 @@ exports[`with graphql documentations in typescript mode creates a multi word sdl " `; +exports[`with graphql documentations in typescript mode creates a sdl file with Byte definitions 1`] = ` +"export const schema = gql\` + """ + Representation of Key. + """ + type Key { + "Description for id." + id: Int! + + "Description for publicKey." + publicKey: Byte! + } + + """ + About queries + """ + type Query { + "Fetch Keys." + keys: [Key!]! @requireAuth + + "Fetch a Key by id." + key(id: Int!): Key @requireAuth + } + + """ + Autogenerated input type of InputKey. + """ + input CreateKeyInput { + "Description for publicKey." + publicKey: Byte! + } + + """ + Autogenerated input type of UpdateKey. + """ + input UpdateKeyInput { + "Description for publicKey." + publicKey: Byte + } + + """ + About mutations + """ + type Mutation { + "Creates a new Key." + createKey(input: CreateKeyInput!): Key! @requireAuth + + "Updates an existing Key." + updateKey(id: Int!, input: UpdateKeyInput!): Key! @requireAuth + + "Deletes an existing Key." + deleteKey(id: Int!): Key! @requireAuth + } +\` +" +`; + exports[`with graphql documentations in typescript mode creates a sdl file with enum definitions 1`] = ` "export const schema = gql\` """ @@ -1526,6 +1640,35 @@ exports[`without graphql documentations in javascript mode creates a multi word " `; +exports[`without graphql documentations in javascript mode creates a sdl file with Byte definitions 1`] = ` +"export const schema = gql\` + type Key { + id: Int! + publicKey: Byte! + } + + type Query { + keys: [Key!]! @requireAuth + key(id: Int!): Key @requireAuth + } + + input CreateKeyInput { + publicKey: Byte! + } + + input UpdateKeyInput { + publicKey: Byte + } + + type Mutation { + createKey(input: CreateKeyInput!): Key! @requireAuth + updateKey(id: Int!, input: UpdateKeyInput!): Key! @requireAuth + deleteKey(id: Int!): Key! @requireAuth + } +\` +" +`; + exports[`without graphql documentations in javascript mode creates a sdl file with enum definitions 1`] = ` "export const schema = gql\` type Shoe { @@ -1734,6 +1877,35 @@ exports[`without graphql documentations in typescript mode creates a multi word " `; +exports[`without graphql documentations in typescript mode creates a sdl file with Byte definitions 1`] = ` +"export const schema = gql\` + type Key { + id: Int! + publicKey: Byte! + } + + type Query { + keys: [Key!]! @requireAuth + key(id: Int!): Key @requireAuth + } + + input CreateKeyInput { + publicKey: Byte! + } + + input UpdateKeyInput { + publicKey: Byte + } + + type Mutation { + createKey(input: CreateKeyInput!): Key! @requireAuth + updateKey(id: Int!, input: UpdateKeyInput!): Key! @requireAuth + deleteKey(id: Int!): Key! @requireAuth + } +\` +" +`; + exports[`without graphql documentations in typescript mode creates a sdl file with enum definitions 1`] = ` "export const schema = gql\` type Shoe { diff --git a/packages/cli/src/commands/generate/sdl/__tests__/fixtures/schema.prisma b/packages/cli/src/commands/generate/sdl/__tests__/fixtures/schema.prisma index 86d854f9895d..d31245c497a1 100644 --- a/packages/cli/src/commands/generate/sdl/__tests__/fixtures/schema.prisma +++ b/packages/cli/src/commands/generate/sdl/__tests__/fixtures/schema.prisma @@ -53,6 +53,11 @@ model Photo { metadata Json } +model Key { + id Int @id @default(autoincrement()) + publicKey Bytes +} + /// A list of allowed colors. enum Color { RED diff --git a/packages/cli/src/commands/generate/sdl/__tests__/sdl.test.js b/packages/cli/src/commands/generate/sdl/__tests__/sdl.test.js index 081aebe7ba19..77ec6be07efc 100644 --- a/packages/cli/src/commands/generate/sdl/__tests__/sdl.test.js +++ b/packages/cli/src/commands/generate/sdl/__tests__/sdl.test.js @@ -203,6 +203,21 @@ const itCreatesAnSDLFileWithJsonDefinitions = (baseArgs = {}) => { }) } +const itCreatesAnSDLFileWithByteDefinitions = (baseArgs = {}) => { + test('creates a sdl file with Byte definitions', async () => { + const files = await sdl.files({ + ...baseArgs, + name: 'Key', + crud: true, + }) + const ext = extensionForBaseArgs(baseArgs) + + expect( + files[path.normalize(`/path/to/project/api/src/graphql/keys.sdl.${ext}`)] + ).toMatchSnapshot() + }) +} + describe('without graphql documentations', () => { describe('in javascript mode', () => { const baseArgs = { ...getDefaultArgs(sdl.defaults), tests: true } @@ -215,6 +230,7 @@ describe('without graphql documentations', () => { itCreateAMultiWordSDLFileWithCRUD(baseArgs) itCreatesAnSDLFileWithEnumDefinitions(baseArgs) itCreatesAnSDLFileWithJsonDefinitions(baseArgs) + itCreatesAnSDLFileWithByteDefinitions(baseArgs) }) describe('in typescript mode', () => { @@ -232,6 +248,7 @@ describe('without graphql documentations', () => { itCreateAMultiWordSDLFileWithCRUD(baseArgs) itCreatesAnSDLFileWithEnumDefinitions(baseArgs) itCreatesAnSDLFileWithJsonDefinitions(baseArgs) + itCreatesAnSDLFileWithByteDefinitions(baseArgs) }) }) @@ -251,6 +268,7 @@ describe('with graphql documentations', () => { itCreateAMultiWordSDLFileWithCRUD(baseArgs) itCreatesAnSDLFileWithEnumDefinitions(baseArgs) itCreatesAnSDLFileWithJsonDefinitions(baseArgs) + itCreatesAnSDLFileWithByteDefinitions(baseArgs) }) describe('in typescript mode', () => { @@ -269,20 +287,11 @@ describe('with graphql documentations', () => { itCreateAMultiWordSDLFileWithCRUD(baseArgs) itCreatesAnSDLFileWithEnumDefinitions(baseArgs) itCreatesAnSDLFileWithJsonDefinitions(baseArgs) + itCreatesAnSDLFileWithByteDefinitions(baseArgs) }) }) describe('handler', () => { - beforeEach(() => { - jest.spyOn(console, 'info').mockImplementation(() => {}) - jest.spyOn(console, 'log').mockImplementation(() => {}) - }) - - afterEach(() => { - console.info.mockRestore() - console.log.mockRestore() - }) - const canBeCalledWithGivenModelName = (letterCase, model) => { test(`can be called with ${letterCase} model name`, async () => { const spy = jest.spyOn(fs, 'writeFileSync') diff --git a/packages/cli/src/commands/generate/sdl/sdl.js b/packages/cli/src/commands/generate/sdl/sdl.js index 60344bba965a..53625a5fdaf1 100644 --- a/packages/cli/src/commands/generate/sdl/sdl.js +++ b/packages/cli/src/commands/generate/sdl/sdl.js @@ -69,13 +69,14 @@ const modelFieldToSDL = ({ field.kind === 'object' ? idType(types[field.type]) : field.type } - const dictionary = { + const prismaTypeToGraphqlType = { Json: 'JSON', Decimal: 'Float', + Bytes: 'Byte', } const fieldContent = `${field.name}: ${field.isList ? '[' : ''}${ - dictionary[field.type] || field.type + prismaTypeToGraphqlType[field.type] || field.type }${field.isList ? ']' : ''}${ (field.isRequired && required) | field.isList ? '!' : '' }` @@ -344,7 +345,11 @@ export const handler = async ({ }, }, ].filter(Boolean), - { rendererOptions: { collapseSubtasks: false }, exitOnError: true } + { + rendererOptions: { collapseSubtasks: false }, + exitOnError: true, + silentRendererCondition: process.env.NODE_ENV === 'test', + } ) if (rollback && !force) { diff --git a/packages/cli/src/commands/generate/service/service.js b/packages/cli/src/commands/generate/service/service.js index 3f4d7377493a..b3df5afac1cd 100644 --- a/packages/cli/src/commands/generate/service/service.js +++ b/packages/cli/src/commands/generate/service/service.js @@ -44,6 +44,11 @@ export const parseSchema = async (model) => { export const scenarioFieldValue = (field) => { const randFloat = Math.random() * 10000000 const randInt = parseInt(Math.random() * 10000000) + const randIntArray = [ + parseInt(Math.random() * 300), + parseInt(Math.random() * 300), + parseInt(Math.random() * 300), + ] switch (field.type) { case 'BigInt': @@ -61,6 +66,8 @@ export const scenarioFieldValue = (field) => { return { foo: 'bar' } case 'String': return field.isUnique ? `String${randInt}` : 'String' + case 'Bytes': + return `Buffer.from([${randIntArray}])` default: { if (field.kind === 'enum' && field.enumValues[0]) { return field.enumValues[0].dbName || field.enumValues[0].name @@ -125,8 +132,9 @@ export const buildScenario = async (model) => { Object.keys(scenarioData).forEach((key) => { const value = scenarioData[key] + // Support BigInt if (value && typeof value === 'string' && value.match(/^\d+n$/)) { - scenarioData[key] = `${value.substr(0, value.length - 1)}n` + scenarioData[key] = `${value.slice(0, value.length - 1)}n` } }) @@ -141,17 +149,20 @@ export const buildScenario = async (model) => { export const buildStringifiedScenario = async (model) => { const scenario = await buildScenario(model) - return JSON.stringify(scenario, (key, value) => { + const jsonString = JSON.stringify(scenario, (_key, value) => { if (typeof value === 'bigint') { return value.toString() } if (typeof value === 'string' && value.match(/^\d+n$/)) { - return Number(value.substr(0, value.length - 1)) + return Number(value.slice(0, value.length - 1)) } return value }) + + // Not all values can be represented as JSON, like function invocations + return jsonString.replace(/"Buffer\.from\(([^)]+)\)"/g, 'Buffer.from($1)') } export const fieldTypes = async (model) => { diff --git a/packages/cli/src/commands/generate/service/templates/test.ts.template b/packages/cli/src/commands/generate/service/templates/test.ts.template index e85bfda525df..e073d8952be7 100644 --- a/packages/cli/src/commands/generate/service/templates/test.ts.template +++ b/packages/cli/src/commands/generate/service/templates/test.ts.template @@ -15,18 +15,21 @@ return `new Prisma.Decimal(${obj})` } - return JSON.stringify(obj).replace(/['"].*?['"]/g, (string) => { + const jsonString = JSON.stringify(obj).replace(/['"].*?['"]/g, (string) => { if (string.match(/scenario\./)) { return string.replace(/['"]/g, '') } // BigInt if (string.match(/^\"\d+n\"$/)) { - return string.substr(1, string.length - 2) + return string.slice(1, string.length - 1) } return string }) + + // Not all values can be represented as JSON, like function invocations + return jsonString.replace(/"Buffer\.from\(([^)]+)\)"/g, 'Buffer.from($1)') } %> <% if (prismaImport) { %>import { Prisma, ${prismaModel} } from '@prisma/client'<% } else { %>import type { ${prismaModel} } from '@prisma/client'<% } %> diff --git a/packages/graphql-server/src/rootSchema.ts b/packages/graphql-server/src/rootSchema.ts index 7332d49b94ed..4010e7d87d99 100644 --- a/packages/graphql-server/src/rootSchema.ts +++ b/packages/graphql-server/src/rootSchema.ts @@ -6,6 +6,7 @@ import { DateTimeResolver, JSONResolver, JSONObjectResolver, + ByteResolver, } from 'graphql-scalars' import gql from 'graphql-tag' @@ -29,6 +30,7 @@ export const schema = gql` scalar DateTime scalar JSON scalar JSONObject + scalar Byte """ The RedwoodJS Root Schema @@ -61,6 +63,7 @@ export interface Resolvers { JSON: typeof JSONResolver JSONObject: typeof JSONObjectResolver Query: Record + Byte: typeof ByteResolver } export const resolvers: Resolvers = { @@ -79,4 +82,5 @@ export const resolvers: Resolvers = { }, }), }, + Byte: ByteResolver, } diff --git a/packages/internal/src/__tests__/__snapshots__/graphqlCodeGen.test.ts.snap b/packages/internal/src/__tests__/__snapshots__/graphqlCodeGen.test.ts.snap index 7faa93c8b756..844786338004 100644 --- a/packages/internal/src/__tests__/__snapshots__/graphqlCodeGen.test.ts.snap +++ b/packages/internal/src/__tests__/__snapshots__/graphqlCodeGen.test.ts.snap @@ -33,6 +33,7 @@ export type Scalars = { Int: number; Float: number; BigInt: number; + Byte: Buffer; Date: Date | string; DateTime: Date | string; JSON: Prisma.JsonValue; @@ -160,6 +161,7 @@ export type DirectiveResolverFn; Boolean: ResolverTypeWrapper; + Byte: ResolverTypeWrapper; Date: ResolverTypeWrapper; DateTime: ResolverTypeWrapper; Int: ResolverTypeWrapper; @@ -177,6 +179,7 @@ export type ResolversTypes = { export type ResolversParentTypes = { BigInt: Scalars['BigInt']; Boolean: Scalars['Boolean']; + Byte: Scalars['Byte']; Date: Scalars['Date']; DateTime: Scalars['DateTime']; Int: Scalars['Int']; @@ -204,6 +207,10 @@ export interface BigIntScalarConfig extends GraphQLScalarTypeConfig { + name: 'Byte'; +} + export interface DateScalarConfig extends GraphQLScalarTypeConfig { name: 'Date'; } @@ -280,6 +287,7 @@ export type TodoRelationResolvers = { BigInt: GraphQLScalarType; + Byte: GraphQLScalarType; Date: GraphQLScalarType; DateTime: GraphQLScalarType; JSON: GraphQLScalarType; @@ -313,6 +321,7 @@ export type Scalars = { Int: number; Float: number; BigInt: number; + Byte: Buffer; Date: string; DateTime: string; JSON: Prisma.JsonValue; diff --git a/packages/internal/src/__tests__/__snapshots__/graphqlSchema.test.ts.snap b/packages/internal/src/__tests__/__snapshots__/graphqlSchema.test.ts.snap index 316f6ec48c7d..4bcc745101a9 100644 --- a/packages/internal/src/__tests__/__snapshots__/graphqlSchema.test.ts.snap +++ b/packages/internal/src/__tests__/__snapshots__/graphqlSchema.test.ts.snap @@ -7,6 +7,8 @@ directive @skipAuth on FIELD_DEFINITION scalar BigInt +scalar Byte + scalar Date scalar DateTime @@ -76,6 +78,8 @@ directive @skipAuth on FIELD_DEFINITION scalar BigInt +scalar Byte + scalar Date scalar DateTime diff --git a/packages/internal/src/__tests__/graphqlCodeGen.test.ts b/packages/internal/src/__tests__/graphqlCodeGen.test.ts index e968b14e1abd..d87613c518fc 100644 --- a/packages/internal/src/__tests__/graphqlCodeGen.test.ts +++ b/packages/internal/src/__tests__/graphqlCodeGen.test.ts @@ -83,6 +83,7 @@ test('Generate gql typedefs api', async () => { // Check that JSON types are imported from prisma expect(data).toContain('JSON: Prisma.JsonValue;') expect(data).toContain('JSONObject: Prisma.JsonObject;') + expect(data).toContain('Byte: Buffer;') // Check that prisma model imports are added to the top of the file expect(data).toContain( diff --git a/packages/internal/src/__tests__/graphqlSchema.test.ts b/packages/internal/src/__tests__/graphqlSchema.test.ts index 0dd639575c7f..7a1cece65722 100644 --- a/packages/internal/src/__tests__/graphqlSchema.test.ts +++ b/packages/internal/src/__tests__/graphqlSchema.test.ts @@ -73,10 +73,6 @@ test('Returns error message when schema loading fails', async () => { const [schemaLoadingError] = errors - console.log({ - errors, - }) - expect(schemaLoadingError.message).toEqual( [ 'Schema loading failed. Unknown type: "Shelf".', diff --git a/packages/internal/src/generate/generate.ts b/packages/internal/src/generate/generate.ts index 51d3020a4d8a..847e02dec82b 100644 --- a/packages/internal/src/generate/generate.ts +++ b/packages/internal/src/generate/generate.ts @@ -8,6 +8,7 @@ import { generatePossibleTypes } from './possibleTypes' import { generateTypeDefs } from './typeDefinitions' export const generate = async () => { + console.log('internal generate') const config = getConfig() const { schemaPath, errors: generateGraphQLSchemaErrors } = await generateGraphQLSchema() diff --git a/packages/internal/src/generate/graphqlCodeGen.ts b/packages/internal/src/generate/graphqlCodeGen.ts index a5e44b7d4fa9..90dafd158133 100644 --- a/packages/internal/src/generate/graphqlCodeGen.ts +++ b/packages/internal/src/generate/graphqlCodeGen.ts @@ -80,7 +80,7 @@ export const generateTypeDefGraphQLApi = async (): Promise => { codegenPlugin: addPlugin, }, { - name: 'print-mapped-moddels', + name: 'print-mapped-models', options: {}, codegenPlugin: printMappedModelsPlugin, }, @@ -285,6 +285,7 @@ function getPluginConfig(side: CodegenSide) { JSON: 'Prisma.JsonValue', JSONObject: 'Prisma.JsonObject', Time: side === CodegenSide.WEB ? 'string' : 'Date | string', + Byte: 'Buffer', }, // prevent type names being PetQueryQuery, RW generators already append // Query/Mutation/etc