From 51eb60806ea48eda2f9f2ae0d2421e67a936ab72 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Mon, 26 Aug 2024 10:52:58 +0200 Subject: [PATCH] Convert modern GraphQL description into comments that are supported by AppSync --- .../fixtures/schemas/multiple/post.graphql | 9 ++- src/__tests__/schema.test.ts | 36 +++++++---- src/resources/Schema.ts | 64 +++++++++++++++---- 3 files changed, 84 insertions(+), 25 deletions(-) diff --git a/src/__tests__/fixtures/schemas/multiple/post.graphql b/src/__tests__/fixtures/schemas/multiple/post.graphql index 7af0864d..1fbb96fa 100644 --- a/src/__tests__/fixtures/schemas/multiple/post.graphql +++ b/src/__tests__/fixtures/schemas/multiple/post.graphql @@ -1,8 +1,15 @@ extend type Query { - getPost(id: ID!): Post! + getPost( + "This is an inline description" + id: ID! + ): Post! } extend type Mutation { + """ + This is a description + that includes multiple lines + """ createPost(post: PostInput!): Post! } diff --git a/src/__tests__/schema.test.ts b/src/__tests__/schema.test.ts index deae80fe..8adc193d 100644 --- a/src/__tests__/schema.test.ts +++ b/src/__tests__/schema.test.ts @@ -31,9 +31,7 @@ describe('schema', () => { createUser(post: UserInput!): User! } - \\"\\"\\" - A User - \\"\\"\\" + #A User type User { id: ID! name: String! @@ -43,6 +41,7 @@ describe('schema', () => { input UserInput { name: String! } + ", }, "Type": "AWS::AppSync::GraphQLSchema", @@ -60,6 +59,8 @@ describe('schema', () => { ]); expect(schema.generateSchema()).toMatchInlineSnapshot(` "type Mutation { + #This is a description + #that includes multiple lines createPost(post: PostInput!): Post! createUser(post: UserInput!): User! } @@ -71,13 +72,16 @@ describe('schema', () => { updatedAt: AWSDateTime! } - \\"\\"\\"This is a description\\"\\"\\" + #This is a description input PostInput { title: String! } type Query { - getPost(id: ID!): Post! + getPost( + #This is an inline description + id: ID! + ): Post! getUser: User! } @@ -91,7 +95,8 @@ describe('schema', () => { input UserInput { name: String! - }" + } + " `); }); @@ -102,6 +107,8 @@ describe('schema', () => { ]); expect(schema.generateSchema()).toMatchInlineSnapshot(` "type Mutation { + #This is a description + #that includes multiple lines createPost(post: PostInput!): Post! createUser(post: UserInput!): User! } @@ -113,13 +120,16 @@ describe('schema', () => { updatedAt: AWSDateTime! } - \\"\\"\\"This is a description\\"\\"\\" + #This is a description input PostInput { title: String! } type Query { - getPost(id: ID!): Post! + getPost( + #This is an inline description + id: ID! + ): Post! getUser: User! } @@ -133,7 +143,8 @@ describe('schema', () => { input UserInput { name: String! - }" + } + " `); }); @@ -153,7 +164,7 @@ describe('schema', () => { `); }); - it('should return single files schemas as-is', () => { + it('should return single files schemas with converted descriptions', () => { const api = new Api(given.appSyncConfig(), plugin); const schema = new Schema(api, [ 'src/__tests__/fixtures/schemas/single/schema.graphql', @@ -167,9 +178,7 @@ describe('schema', () => { createUser(post: UserInput!): User! } - \\"\\"\\" - A User - \\"\\"\\" + #A User type User { id: ID! name: String! @@ -179,6 +188,7 @@ describe('schema', () => { input UserInput { name: String! } + " `); }); diff --git a/src/resources/Schema.ts b/src/resources/Schema.ts index 1c958d13..9dd9b2b2 100644 --- a/src/resources/Schema.ts +++ b/src/resources/Schema.ts @@ -5,7 +5,6 @@ import { CfnResources } from '../types/cloudFormation'; import { Api } from './Api'; import { flatten } from 'lodash'; import { parse, print } from 'graphql'; -import ServerlessError from 'serverless/lib/serverless-error'; import { validateSDL } from 'graphql/validation/validate'; import { mergeTypeDefs } from '@graphql-tools/merge'; @@ -47,7 +46,7 @@ export class Schema { }; } - valdiateSchema(schema: string) { + validateSchema(schema: string) { const errors = validateSDL(parse(schema)); if (errors.length > 0) { throw new this.api.plugin.serverless.classes.Error( @@ -57,6 +56,47 @@ export class Schema { } } + // AppSync does not support descriptions from June 2018 spec + // https://spec.graphql.org/June2018/#sec-Descriptions + // so they need to be converted to comments, the space after the # will also be included + // by AppSync in the generated description so we remove it + convertDescriptions(schema: string): string { + const lines = schema.split('\n'); + const singleLineComment = /^(? *)"(?[^"]+?)"$/; + const singleLineMultilineComment = /^(? *)"""(?.+?)"""$/; + const multilineCommentDelimiter = /^(? *)"""$/; + + let inComment = false; + let result = ''; + + for (const line of lines) { + switch (true) { + case singleLineComment.test(line): + result += `${line.match(singleLineComment)?.groups?.indent}#${ + line.match(singleLineComment)?.groups?.comment + }\n`; + break; + case singleLineMultilineComment.test(line): + result += `${ + line.match(singleLineMultilineComment)?.groups?.indent + }#${line.match(singleLineMultilineComment)?.groups?.comment}\n`; + break; + case multilineCommentDelimiter.test(line): + inComment = !inComment; + break; + case inComment: + result += `${ + line.match(/^(? *)/)?.groups?.indent + }#${line.trimStart()}\n`; + break; + default: + result += line + '\n'; + } + } + + return result; + } + generateSchema() { const schemaFiles = flatten(globby.sync(this.schemas)); @@ -67,23 +107,25 @@ export class Schema { ); }); - this.valdiateSchema(AWS_TYPES + '\n' + schemas.join('\n')); + this.validateSchema(AWS_TYPES + '\n' + schemas.join('\n')); // Return single files as-is. if (schemas.length === 1) { - return schemas[0]; + return this.convertDescriptions(schemas[0]); } // AppSync does not support Object extensions // https://spec.graphql.org/October2021/#sec-Object-Extensions // Merge the schemas - return print( - mergeTypeDefs(schemas, { - forceSchemaDefinition: false, - useSchemaDefinition: false, - sort: true, - throwOnConflict: true, - }), + return this.convertDescriptions( + print( + mergeTypeDefs(schemas, { + forceSchemaDefinition: false, + useSchemaDefinition: false, + sort: true, + throwOnConflict: true, + }), + ), ); } }