diff --git a/packages/appsync-modelgen-plugin/API.md b/packages/appsync-modelgen-plugin/API.md index f9b62d480..75ebc58c6 100644 --- a/packages/appsync-modelgen-plugin/API.md +++ b/packages/appsync-modelgen-plugin/API.md @@ -133,6 +133,8 @@ export type ModelIntrospectionSchema = { mutations?: SchemaMutations; subscriptions?: SchemaSubscriptions; inputs?: SchemaInputs; + generations?: SchemaGenerations; + conversations?: SchemaConversationRoutes; }; // Warning: (ae-forgotten-export) The symbol "RawAppSyncModelConfig" needs to be exported by the entry point index.d.ts @@ -153,6 +155,31 @@ export type PrimaryKeyInfo = { // @public (undocumented) export type ScalarType = 'ID' | 'String' | 'Int' | 'Float' | 'AWSDate' | 'AWSTime' | 'AWSDateTime' | 'AWSTimestamp' | 'AWSEmail' | 'AWSURL' | 'AWSIPAddress' | 'Boolean' | 'AWSJSON' | 'AWSPhone'; +// @public (undocumented) +export type SchemaConversation = { + modelName: string; +}; + +// @public (undocumented) +export type SchemaConversationMessage = { + modelName: string; + subscribe: SchemaSubscription; + send: SchemaMutation; +}; + +// @public (undocumented) +export type SchemaConversationRoute = { + name: string; + models: SchemaModels; + nonModels: SchemaNonModels; + enums: SchemaEnums; + conversation: SchemaConversation; + message: SchemaConversationMessage; +}; + +// @public (undocumented) +export type SchemaConversationRoutes = Record; + // @public (undocumented) export type SchemaEnum = { name: string; @@ -162,6 +189,9 @@ export type SchemaEnum = { // @public (undocumented) export type SchemaEnums = Record; +// @public (undocumented) +export type SchemaGenerations = SchemaQueries; + // @public (undocumented) export type SchemaInputs = Record; diff --git a/packages/appsync-modelgen-plugin/schemas/introspection/1/ModelIntrospectionSchema.json b/packages/appsync-modelgen-plugin/schemas/introspection/1/ModelIntrospectionSchema.json index d92926be2..da7a985d4 100644 --- a/packages/appsync-modelgen-plugin/schemas/introspection/1/ModelIntrospectionSchema.json +++ b/packages/appsync-modelgen-plugin/schemas/introspection/1/ModelIntrospectionSchema.json @@ -26,6 +26,12 @@ }, "inputs": { "$ref": "#/definitions/SchemaInputs" + }, + "generations": { + "$ref": "#/definitions/SchemaGenerations" + }, + "conversations": { + "$ref": "#/definitions/SchemaConversationRoutes" } }, "required": [ @@ -532,6 +538,82 @@ ], "additionalProperties": false, "description": "Input Definition" + }, + "SchemaGenerations": { + "$ref": "#/definitions/SchemaQueries" + }, + "SchemaConversationRoutes": { + "$ref": "#/definitions/Record%3Cstring%2CSchemaConversationRoute%3E" + }, + "Record": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/SchemaConversationRoute" + } + }, + "SchemaConversationRoute": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "models": { + "$ref": "#/definitions/SchemaModels" + }, + "nonModels": { + "$ref": "#/definitions/SchemaNonModels" + }, + "enums": { + "$ref": "#/definitions/SchemaEnums" + }, + "conversation": { + "$ref": "#/definitions/SchemaConversation" + }, + "message": { + "$ref": "#/definitions/SchemaConversationMessage" + } + }, + "required": [ + "name", + "models", + "nonModels", + "enums", + "conversation", + "message" + ], + "additionalProperties": false + }, + "SchemaConversation": { + "type": "object", + "properties": { + "modelName": { + "type": "string" + } + }, + "required": [ + "modelName" + ], + "additionalProperties": false + }, + "SchemaConversationMessage": { + "type": "object", + "properties": { + "modelName": { + "type": "string" + }, + "subscribe": { + "$ref": "#/definitions/SchemaSubscription" + }, + "send": { + "$ref": "#/definitions/SchemaMutation" + } + }, + "required": [ + "modelName", + "subscribe", + "send" + ], + "additionalProperties": false } } } diff --git a/packages/appsync-modelgen-plugin/src/__tests__/visitors/__snapshots__/appsync-model-introspection-visitor.test.ts.snap b/packages/appsync-modelgen-plugin/src/__tests__/visitors/__snapshots__/appsync-model-introspection-visitor.test.ts.snap index 9b7dc4dfb..ddd5fd212 100644 --- a/packages/appsync-modelgen-plugin/src/__tests__/visitors/__snapshots__/appsync-model-introspection-visitor.test.ts.snap +++ b/packages/appsync-modelgen-plugin/src/__tests__/visitors/__snapshots__/appsync-model-introspection-visitor.test.ts.snap @@ -1,5 +1,368 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Conversation Route Introspection Visitor Metadata snapshot should generate correct model intropection file validated by JSON schema 1`] = ` +"{ + \\"version\\": 1, + \\"models\\": {}, + \\"enums\\": { + \\"ConversationParticipantRole\\": { + \\"name\\": \\"ConversationParticipantRole\\", + \\"values\\": [ + \\"user\\", + \\"assistant\\" + ] + } + }, + \\"nonModels\\": { + \\"ContentBlock\\": { + \\"name\\": \\"ContentBlock\\", + \\"fields\\": { + \\"type\\": { + \\"name\\": \\"type\\", + \\"isArray\\": false, + \\"type\\": \\"String\\", + \\"isRequired\\": false, + \\"attributes\\": [] + } + } + }, + \\"ToolConfiguration\\": { + \\"name\\": \\"ToolConfiguration\\", + \\"fields\\": { + \\"type\\": { + \\"name\\": \\"type\\", + \\"isArray\\": false, + \\"type\\": \\"String\\", + \\"isRequired\\": false, + \\"attributes\\": [] + } + } + } + }, + \\"conversations\\": { + \\"pirateChat\\": { + \\"name\\": \\"pirateChat\\", + \\"models\\": { + \\"ConversationPirateChat\\": { + \\"name\\": \\"ConversationPirateChat\\", + \\"fields\\": { + \\"id\\": { + \\"name\\": \\"id\\", + \\"type\\": \\"ID\\", + \\"attributes\\": [], + \\"isArray\\": false, + \\"isRequired\\": true + }, + \\"name\\": { + \\"name\\": \\"name\\", + \\"type\\": \\"String\\", + \\"attributes\\": [], + \\"isArray\\": false, + \\"isRequired\\": false + }, + \\"metadata\\": { + \\"name\\": \\"metadata\\", + \\"type\\": \\"AWSJSON\\", + \\"attributes\\": [], + \\"isArray\\": false, + \\"isRequired\\": false + }, + \\"messages\\": { + \\"name\\": \\"messages\\", + \\"type\\": { + \\"model\\": \\"ConversationMessagePirateChat\\" + }, + \\"attributes\\": [], + \\"isArray\\": true, + \\"isRequired\\": false, + \\"isArrayNullable\\": true, + \\"association\\": { + \\"connectionType\\": \\"HAS_MANY\\", + \\"associatedWith\\": [ + \\"conversationId\\" + ] + } + }, + \\"createdAt\\": { + \\"name\\": \\"createdAt\\", + \\"type\\": \\"AWSDateTime\\", + \\"attributes\\": [], + \\"isArray\\": false, + \\"isRequired\\": false, + \\"isReadOnly\\": true + }, + \\"updatedAt\\": { + \\"name\\": \\"updatedAt\\", + \\"type\\": \\"AWSDateTime\\", + \\"attributes\\": [], + \\"isArray\\": false, + \\"isRequired\\": false, + \\"isReadOnly\\": true + } + }, + \\"syncable\\": true, + \\"pluralName\\": \\"ConversationPirateChats\\", + \\"attributes\\": [ + { + \\"type\\": \\"model\\", + \\"properties\\": { + \\"subscriptions\\": { + \\"level\\": \\"off\\" + }, + \\"mutations\\": { + \\"update\\": null + } + } + }, + { + \\"type\\": \\"auth\\", + \\"properties\\": { + \\"rules\\": [ + { + \\"provider\\": \\"userPools\\", + \\"ownerField\\": \\"owner\\", + \\"allow\\": \\"owner\\", + \\"identityClaim\\": \\"cognito:username\\", + \\"operations\\": [ + \\"create\\", + \\"update\\", + \\"delete\\", + \\"read\\" + ] + } + ] + } + } + ], + \\"primaryKeyInfo\\": { + \\"isCustomPrimaryKey\\": false, + \\"primaryKeyFieldName\\": \\"id\\", + \\"sortKeyFieldNames\\": [] + } + }, + \\"ConversationMessagePirateChat\\": { + \\"name\\": \\"ConversationMessagePirateChat\\", + \\"fields\\": { + \\"id\\": { + \\"name\\": \\"id\\", + \\"type\\": \\"ID\\", + \\"attributes\\": [], + \\"isArray\\": false, + \\"isRequired\\": true + }, + \\"conversationId\\": { + \\"name\\": \\"conversationId\\", + \\"type\\": \\"ID\\", + \\"attributes\\": [], + \\"isArray\\": false, + \\"isRequired\\": true + }, + \\"conversation\\": { + \\"name\\": \\"conversation\\", + \\"type\\": { + \\"model\\": \\"ConversationPirateChat\\" + }, + \\"attributes\\": [], + \\"isArray\\": false, + \\"isRequired\\": false, + \\"association\\": { + \\"connectionType\\": \\"BELONGS_TO\\", + \\"targetNames\\": [ + \\"conversationId\\" + ] + } + }, + \\"role\\": { + \\"name\\": \\"role\\", + \\"type\\": { + \\"enum\\": \\"ConversationParticipantRole\\" + }, + \\"attributes\\": [], + \\"isArray\\": false, + \\"isRequired\\": false + }, + \\"content\\": { + \\"name\\": \\"content\\", + \\"type\\": { + \\"nonModel\\": \\"ContentBlock\\" + }, + \\"attributes\\": [], + \\"isArray\\": true, + \\"isRequired\\": false + }, + \\"aiContext\\": { + \\"name\\": \\"aiContext\\", + \\"type\\": \\"AWSJSON\\", + \\"attributes\\": [], + \\"isArray\\": false, + \\"isRequired\\": false + }, + \\"toolConfiguration\\": { + \\"name\\": \\"toolConfiguration\\", + \\"type\\": { + \\"nonModel\\": \\"ToolConfiguration\\" + }, + \\"attributes\\": [], + \\"isArray\\": true, + \\"isRequired\\": false, + \\"isArrayNullable\\": true + }, + \\"createdAt\\": { + \\"name\\": \\"createdAt\\", + \\"type\\": \\"AWSDateTime\\", + \\"attributes\\": [], + \\"isArray\\": false, + \\"isRequired\\": false, + \\"isReadOnly\\": true + }, + \\"updatedAt\\": { + \\"name\\": \\"updatedAt\\", + \\"type\\": \\"AWSDateTime\\", + \\"attributes\\": [], + \\"isArray\\": false, + \\"isRequired\\": false, + \\"isReadOnly\\": true + } + }, + \\"syncable\\": true, + \\"pluralName\\": \\"ConversationMessagePirateChats\\", + \\"attributes\\": [ + { + \\"type\\": \\"model\\", + \\"properties\\": { + \\"subscriptions\\": {}, + \\"mutations\\": { + \\"update\\": null + } + } + }, + { + \\"type\\": \\"auth\\", + \\"properties\\": { + \\"rules\\": [ + { + \\"provider\\": \\"userPools\\", + \\"ownerField\\": \\"owner\\", + \\"allow\\": \\"owner\\", + \\"identityClaim\\": \\"cognito:username\\", + \\"operations\\": [ + \\"create\\", + \\"update\\", + \\"delete\\", + \\"read\\" + ] + } + ] + } + } + ], + \\"primaryKeyInfo\\": { + \\"isCustomPrimaryKey\\": false, + \\"primaryKeyFieldName\\": \\"id\\", + \\"sortKeyFieldNames\\": [] + } + } + }, + \\"nonModels\\": {}, + \\"enums\\": { + \\"ConversationParticipantRole\\": { + \\"name\\": \\"ConversationParticipantRole\\", + \\"values\\": [ + \\"user\\", + \\"assistant\\" + ] + } + }, + \\"conversation\\": { + \\"modelName\\": \\"ConversationPirateChat\\" + }, + \\"message\\": { + \\"modelName\\": \\"ConversationMessagePirateChat\\", + \\"send\\": { + \\"name\\": \\"pirateChat\\", + \\"isArray\\": false, + \\"type\\": { + \\"model\\": \\"ConversationMessagePirateChat\\" + }, + \\"isRequired\\": false, + \\"arguments\\": { + \\"conversationId\\": { + \\"name\\": \\"conversationId\\", + \\"isArray\\": false, + \\"type\\": \\"ID\\", + \\"isRequired\\": false + }, + \\"content\\": { + \\"name\\": \\"content\\", + \\"isArray\\": true, + \\"type\\": { + \\"input\\": \\"ContentBlockInput\\" + }, + \\"isRequired\\": false, + \\"isArrayNullable\\": true + }, + \\"aiContext\\": { + \\"name\\": \\"aiContext\\", + \\"isArray\\": false, + \\"type\\": \\"AWSJSON\\", + \\"isRequired\\": false + }, + \\"toolConfiguration\\": { + \\"name\\": \\"toolConfiguration\\", + \\"isArray\\": false, + \\"type\\": { + \\"input\\": \\"ToolConfigurationInput\\" + }, + \\"isRequired\\": false + } + } + }, + \\"subscribe\\": { + \\"isArray\\": false, + \\"isRequired\\": false, + \\"name\\": \\"onCreateAssistantResponsePirateChat\\", + \\"type\\": { + \\"model\\": \\"ConversationMessagePirateChat\\" + }, + \\"arguments\\": { + \\"conversationId\\": { + \\"name\\": \\"conversationId\\", + \\"isArray\\": false, + \\"isRequired\\": true, + \\"type\\": \\"ID\\" + } + } + } + } + } + }, + \\"inputs\\": { + \\"ContentBlockInput\\": { + \\"name\\": \\"ContentBlockInput\\", + \\"attributes\\": { + \\"type\\": { + \\"name\\": \\"type\\", + \\"isArray\\": false, + \\"type\\": \\"String\\", + \\"isRequired\\": false + } + } + }, + \\"ToolConfigurationInput\\": { + \\"name\\": \\"ToolConfigurationInput\\", + \\"attributes\\": { + \\"type\\": { + \\"name\\": \\"type\\", + \\"isArray\\": false, + \\"type\\": \\"String\\", + \\"isRequired\\": false + } + } + } + } +}" +`; + exports[`Custom primary key tests should generate correct model intropection file validated by JSON schema and not throw error when custom PK is enabled 1`] = ` "{ \\"version\\": 1, @@ -1976,6 +2339,75 @@ exports[`Custom queries/mutations/subscriptions & input type tests should genera }" `; +exports[`Generation Route Introspection Visitor Metadata snapshot should generate correct model intropection file validated by JSON schema 1`] = ` +"{ + \\"version\\": 1, + \\"models\\": {}, + \\"enums\\": {}, + \\"nonModels\\": { + \\"Recipe\\": { + \\"name\\": \\"Recipe\\", + \\"fields\\": { + \\"name\\": { + \\"name\\": \\"name\\", + \\"isArray\\": false, + \\"type\\": \\"String\\", + \\"isRequired\\": false, + \\"attributes\\": [] + }, + \\"ingredients\\": { + \\"name\\": \\"ingredients\\", + \\"isArray\\": true, + \\"type\\": \\"String\\", + \\"isRequired\\": false, + \\"attributes\\": [], + \\"isArrayNullable\\": true + }, + \\"instructions\\": { + \\"name\\": \\"instructions\\", + \\"isArray\\": false, + \\"type\\": \\"String\\", + \\"isRequired\\": false, + \\"attributes\\": [] + } + } + } + }, + \\"generations\\": { + \\"generateRecipe\\": { + \\"name\\": \\"generateRecipe\\", + \\"isArray\\": false, + \\"type\\": { + \\"nonModel\\": \\"Recipe\\" + }, + \\"isRequired\\": false, + \\"arguments\\": { + \\"description\\": { + \\"name\\": \\"description\\", + \\"isArray\\": false, + \\"type\\": \\"String\\", + \\"isRequired\\": false + } + } + }, + \\"summarize\\": { + \\"name\\": \\"summarize\\", + \\"isArray\\": false, + \\"type\\": \\"String\\", + \\"isRequired\\": false, + \\"arguments\\": { + \\"text\\": { + \\"name\\": \\"text\\", + \\"isArray\\": false, + \\"type\\": \\"String\\", + \\"isRequired\\": false + } + } + } + } +}" +`; + exports[`Model Introspection Visitor Metadata snapshot should generate correct model intropection file validated by JSON schema 1`] = ` "{ \\"version\\": 1, diff --git a/packages/appsync-modelgen-plugin/src/__tests__/visitors/appsync-model-introspection-visitor.test.ts b/packages/appsync-modelgen-plugin/src/__tests__/visitors/appsync-model-introspection-visitor.test.ts index 49be70b1f..61e369e89 100644 --- a/packages/appsync-modelgen-plugin/src/__tests__/visitors/appsync-model-introspection-visitor.test.ts +++ b/packages/appsync-modelgen-plugin/src/__tests__/visitors/appsync-model-introspection-visitor.test.ts @@ -1,6 +1,6 @@ import { buildSchema, GraphQLSchema, parse, visit } from 'graphql'; import { METADATA_SCALAR_MAP } from '../../scalars'; -import { AppSyncDirectives, DefaultDirectives, V1Directives, DeprecatedDirective, Directive } from '@aws-amplify/graphql-directives'; +import { AppSyncDirectives, DefaultDirectives, V1Directives, DeprecatedDirective, Directive, V2Directives } from '@aws-amplify/graphql-directives'; import { scalars } from '../../scalars/supported-scalars'; import { AppSyncModelIntrospectionVisitor } from '../../visitors/appsync-model-introspection-visitor'; @@ -28,6 +28,62 @@ const getVisitor = (schema: string, settings: any = {}, directives: readonly Dir return visitor; }; +describe('Conversation Route Introspection Visitor', () => { + const schema = /* GraphQL */ ` + enum ConversationParticipantRole { + user + assistant + } + + type ContentBlock { + type: String + } + type ToolConfiguration { + type: String + } + input ContentBlockInput { + type: String + } + input ToolConfigurationInput { + type: String + } + + interface ConversationMessage { + id: ID! + conversationId: ID! + role: ConversationParticipantRole + content: [ContentBlock] + aiContext: AWSJSON + toolConfiguration: ToolConfiguration + createdAt: AWSDateTime + updatedAt: AWSDateTime + owner: String + } + + type Mutation { + pirateChat(conversationId: ID, content: [ContentBlockInput], aiContext: AWSJSON, toolConfiguration: ToolConfigurationInput): ConversationMessage + @conversation(aiModel: "Claude3Haiku", functionName: "conversation-handler") + } + `; + // TODO: Update to amplify-graphql-directives version that includes conversation directive + const conversationDirective: Directive = { + name: 'conversation', + definition: /* GraphQL */ ` + directive @conversation( + aiModel: String, + functionName: String + ) on FIELD_DEFINITION + `, + defaults: {}, + } + const visitor: AppSyncModelIntrospectionVisitor = getVisitor(schema, {}, [...DefaultDirectives, conversationDirective]); + describe('Metadata snapshot', () => { + it('should generate correct model intropection file validated by JSON schema', () => { + expect(visitor.generate()).toMatchSnapshot(); + }); + }); +}); + describe('Model Introspection Visitor', () => { const schema = /* GraphQL */ ` type SimpleModel @model { @@ -253,7 +309,7 @@ describe('Primary Key Info tests', () => { const result = JSON.parse(getVisitor(schema, { respectPrimaryKeyAttributesOnConnectionField: true }).generate()); const { models: { Like, EnthusiastLikes } } = result; expect(result).toMatchSnapshot(); - + // name expect(Like.primaryKeyInfo.primaryKeyFieldName).toEqual('name'); expect(EnthusiastLikes.fields.like.association.targetNames[0]).toEqual('likeName'); @@ -372,7 +428,7 @@ describe('Custom queries/mutations/subscriptions & input type tests', () => { } # The member types of a Union type must all be Object base types. union CustomUnion = Todo | Phone - + type Query { getAllTodo(msg: String, input: CustomInput): String echo(msg: String): String @@ -383,7 +439,7 @@ describe('Custom queries/mutations/subscriptions & input type tests', () => { echo6(customInput: CustomInput): String! echo7: [ICustom]! echo8(msg: [Float], msg2: [Int!], enumType: BillingSource, enumList: [BillingSource], inputType: [CustomInput]): [String] - echo9(msg: [Float]!, msg2: [Int!]!, enumType: BillingSource!, enumList: [BillingSource!]!, inputType: [CustomInput!]!): [String!]! + echo9(msg: [Float]!, msg2: [Int!]!, enumType: BillingSource!, enumList: [BillingSource!]!, inputType: [CustomInput!]!): [String!]! } type Mutation { mutate(msg: [String!]!): Todo @@ -410,7 +466,7 @@ describe('custom fields', () => { relatedId: ID related: RelatedLegacy @hasOne(fields: [relatedId]) } - + type RelatedLegacy @model { id: ID! primary: PrimaryLegacy @belongsTo @@ -427,7 +483,7 @@ describe('custom fields', () => { relatedId: ID related: [RelatedLegacy] @hasMany(fields: [relatedId]) } - + type RelatedLegacy @model { id: ID! primary: PrimaryLegacy @belongsTo @@ -443,7 +499,7 @@ describe('custom fields', () => { id: ID! related: RelatedLegacy @hasOne } - + type RelatedLegacy @model { id: ID! primaryId: ID! @@ -460,7 +516,7 @@ describe('custom fields', () => { id: ID! related: [RelatedLegacy] @hasMany } - + type RelatedLegacy @model { id: ID! primaryId: ID! @@ -478,7 +534,7 @@ describe('custom fields', () => { relatedId: ID related: RelatedLegacy @hasOne(fields: [relatedId]) } - + type RelatedLegacy @model { id: ID! primaryId: ID! @@ -496,7 +552,7 @@ describe('custom fields', () => { relatedId: ID related: [RelatedLegacy] @hasMany(fields: [relatedId]) } - + type RelatedLegacy @model { id: ID! primaryId: ID! @@ -554,13 +610,13 @@ describe('custom references', () => { relatedMany: [RelatedMany] @hasMany(references: ["primaryId"]) relatedOne: RelatedOne @hasOne(references: ["primaryId"]) } - + type RelatedMany @model { id: ID! @primaryKey primaryId: ID! primary: Primary @belongsTo(references: ["primaryId"]) } - + type RelatedOne @model { id: ID! @primaryKey primaryId: ID! @@ -578,7 +634,7 @@ describe('custom references', () => { bar1: Bar @hasOne(references: ["bar1Id"]) bar2: Bar @hasOne(references: ["bar2Id"]) } - + type Bar @model { id: ID! bar1Id: ID @@ -601,7 +657,7 @@ describe('custom references', () => { content: String related: [Related!] @hasMany(references: ["primaryTenantId", "primaryInstanceId", "primaryRecordId"]) } - + type Related @model { content: String primaryTenantId: ID! @@ -624,7 +680,7 @@ describe('custom references', () => { content: String related: Related @hasOne(references: ["primaryTenantId", "primaryInstanceId", "primaryRecordId"]) } - + type Related @model { content: String primaryTenantId: ID! @@ -806,3 +862,45 @@ describe('custom references', () => { .toThrowError(`Error processing @belongsTo directive on SqlRelated.primary. @hasOne or @hasMany directive with references ["primaryId"] was not found in connected model SqlPrimary`); }); }); + +describe('Generation Route Introspection Visitor', () => { + const schema = /* GraphQL */ ` + type Recipe { + name: String + ingredients: [String] + instructions: String + } + + type Query { + generateRecipe(description: String): Recipe + @generation(aiModel: "anthropic.claude-3-haiku-20240307-v1:0", systemPrompt: "You are a recipe generator.") + + summarize(text: String): String + @generation(aiModel: "anthropic.claude-3-haiku-20240307-v1:0", systemPrompt: "You are a text summarizer.") + } + `; + + const generationDirective: Directive = { + name: 'generation', + definition: /* GraphQL */ ` + directive @generation( + aiModel: String! + systemPrompt: String! + inferenceConfiguration: GenerationInferenceConfiguration + ) on FIELD_DEFINITION + + input GenerationInferenceConfiguration { + maxTokens: Int + temperature: Float + topP: Float + } + `, + defaults: {}, + } + const visitor: AppSyncModelIntrospectionVisitor = getVisitor(schema, {}, [...V2Directives, generationDirective]); + describe('Metadata snapshot', () => { + it('should generate correct model intropection file validated by JSON schema', () => { + expect(visitor.generate()).toMatchSnapshot(); + }); + }); +}); \ No newline at end of file diff --git a/packages/appsync-modelgen-plugin/src/interfaces/introspection/model-schema.ts b/packages/appsync-modelgen-plugin/src/interfaces/introspection/model-schema.ts index 146e78b99..453247b8c 100644 --- a/packages/appsync-modelgen-plugin/src/interfaces/introspection/model-schema.ts +++ b/packages/appsync-modelgen-plugin/src/interfaces/introspection/model-schema.ts @@ -19,6 +19,8 @@ mutations?: SchemaMutations; subscriptions?: SchemaSubscriptions; inputs?: SchemaInputs; + generations?: SchemaGenerations; + conversations?: SchemaConversationRoutes; }; /** * Top-level Entities on a Schema @@ -30,6 +32,27 @@ export type SchemaQueries = Record; export type SchemaMutations = Record; export type SchemaSubscriptions = Record; export type SchemaInputs = Record; +export type SchemaGenerations = SchemaQueries; +export type SchemaConversationRoutes = Record; + +export type SchemaConversationRoute = { + name: string; + models: SchemaModels; + nonModels: SchemaNonModels; + enums: SchemaEnums; + conversation: SchemaConversation; + message: SchemaConversationMessage; +} + +export type SchemaConversation = { + modelName: string; +} + +export type SchemaConversationMessage = { + modelName: string; + subscribe: SchemaSubscription; + send: SchemaMutation; +} export type SchemaModel = { name: string; diff --git a/packages/appsync-modelgen-plugin/src/utils/constants.ts b/packages/appsync-modelgen-plugin/src/utils/constants.ts index 3fe0033e6..1a05c716b 100644 --- a/packages/appsync-modelgen-plugin/src/utils/constants.ts +++ b/packages/appsync-modelgen-plugin/src/utils/constants.ts @@ -8,6 +8,8 @@ export const TransformerV2DirectiveName = { INDEX: 'index', DEFAULT: 'default', SEARCHABLE: 'searchable', + GENERATION: 'generation', + CONVERSATION: 'conversation', }; export const DEFAULT_HASH_KEY_FIELD = 'id'; export const DEFAULT_CREATED_TIME = 'createdAt'; diff --git a/packages/appsync-modelgen-plugin/src/utils/fieldUtils.ts b/packages/appsync-modelgen-plugin/src/utils/fieldUtils.ts index 5a14e7f3d..17232fe4a 100644 --- a/packages/appsync-modelgen-plugin/src/utils/fieldUtils.ts +++ b/packages/appsync-modelgen-plugin/src/utils/fieldUtils.ts @@ -1,4 +1,4 @@ -import { CodeGenDirective, CodeGenField, CodeGenModel } from '../visitors/appsync-visitor'; +import { CodeGenDirective, CodeGenField, CodeGenModel, CodeGenMutation, CodeGenQuery } from '../visitors/appsync-visitor'; import { TransformerV2DirectiveName } from './constants'; export function addFieldToModel(model: CodeGenModel, field: CodeGenField): void { @@ -40,4 +40,24 @@ export function getModelPrimaryKeyComponentFields(model: CodeGenModel): CodeGenF }; } return keyFields; +} + +/** + * Checks if a given mutation field contains the @conversation directive. + * + * @param {CodeGenMutation} queryField - The mutation field to check. + * @returns {boolean} True if the mutation field contains the @conversation directive, false otherwise. + */ +export function containsConversationDirective(queryField: CodeGenMutation): boolean { + return queryField.directives.some((directive) => directive.name === TransformerV2DirectiveName.CONVERSATION); +} + +/** + * Checks if a given query field contains the @generation directive. + * + * @param {CodeGenQuery} queryField - The query field to check. + * @returns {boolean} True if the query field contains the @generation directive, false otherwise. + */ +export function containsGenerationDirective(queryField: CodeGenQuery): boolean { + return queryField.directives.some((directive) => directive.name === TransformerV2DirectiveName.GENERATION); } \ No newline at end of file diff --git a/packages/appsync-modelgen-plugin/src/utils/process-conversation.ts b/packages/appsync-modelgen-plugin/src/utils/process-conversation.ts new file mode 100644 index 000000000..38190c414 --- /dev/null +++ b/packages/appsync-modelgen-plugin/src/utils/process-conversation.ts @@ -0,0 +1,198 @@ +import { CodeGenConnectionType, Field, FieldAttribute, FieldType, PrimaryKeyInfo, SchemaConversationRoute, SchemaModel, SchemaMutation, SchemaSubscription } from '../interfaces/introspection'; +import { pascalCase } from 'change-case'; +import { plural } from 'pluralize'; +import { CodeGenMutation } from '../visitors/appsync-visitor'; + +/** + * Generates metadata for a conversation model based on a mutation object. + * @param mutationObj - The mutation object to generate conversation metadata from. + * @param generateGraphQLOperationMetadata - A function to generate GraphQL operation metadata. + * @returns The generated conversation route schema. + */ +export function processConversationRoute( + mutationObj: CodeGenMutation, + generateGraphQLOperationMetadata: (operationObj: T) => V +): SchemaConversationRoute { + const routeName = pascalCase(mutationObj.name); + const conversationModelName = `Conversation${routeName}`; + const conversationMessageModelName = `ConversationMessage${routeName}`; + + return { + name: mutationObj.name, + models: { + [conversationModelName]: generateConversationModel(conversationModelName, conversationMessageModelName), + [conversationMessageModelName]: generateConversationMessageModel(conversationModelName, conversationMessageModelName), + }, + nonModels: {}, + enums: { + ConversationParticipantRole: { + name: 'ConversationParticipantRole', + values: ['user', 'assistant'], + } + }, + conversation: { + modelName: conversationModelName + }, + message: { + modelName: conversationMessageModelName, + send: { + ...generateGraphQLOperationMetadata(mutationObj), + type: { model: conversationMessageModelName } + }, + subscribe: generateSubscriptionMetadata(routeName, conversationMessageModelName), + } + }; +} + +/** + * Generates a conversation model schema. + * @param modelName - The name of the conversation model. + * @param messageModelName - The name of the associated message model. + * @returns The generated conversation model schema. + */ +function generateConversationModel(modelName: string, messageModelName: string): SchemaModel { + return { + name: modelName, + fields: { + id: generateField('id', 'ID', { isRequired: true }), + name: generateField('name', 'String'), + metadata: generateField('metadata', 'AWSJSON'), + messages: generateMessagesField(messageModelName), + createdAt: generateTimestampField('createdAt'), + updatedAt: generateTimestampField('updatedAt'), + }, + syncable: true, + pluralName: plural(modelName), + attributes: [ + { + type: 'model', + properties: { + subscriptions: { level: 'off' }, + mutations: { update: null } + } + }, + generateAuthAttribute() + ], + primaryKeyInfo: generatePrimaryKeyInfo(), + }; +} + +/** + * Generates a conversation message model schema. + * @param conversationModelName - The name of the associated conversation model. + * @param modelName - The name of the message model. + * @returns The generated conversation message model schema. + */ +function generateConversationMessageModel(conversationModelName: string, modelName: string): SchemaModel { + return { + name: modelName, + fields: { + id: generateField('id', 'ID', { isRequired: true }), + conversationId: generateField('conversationId', 'ID', { isRequired: true }), + conversation: generateConversationField(conversationModelName), + role: generateField('role', { enum: 'ConversationParticipantRole' }), + content: generateField('content', { nonModel: 'ContentBlock' }, { isArray: true }), + aiContext: generateField('aiContext', 'AWSJSON'), + toolConfiguration: generateField('toolConfiguration', { nonModel: 'ToolConfiguration' }, { isArray: true, isArrayNullable: true }), + createdAt: generateTimestampField('createdAt'), + updatedAt: generateTimestampField('updatedAt'), + }, + syncable: true, + pluralName: plural(modelName), + attributes: [ + { + type: 'model', + properties: { + subscriptions: {}, + mutations: { update: null } + } + }, + generateAuthAttribute() + ], + primaryKeyInfo: generatePrimaryKeyInfo(), + }; +} + +// Helper functions for generating common field structures + +function generateField(name: string, type: FieldType, options: { isRequired?: boolean; isArray?: boolean; isArrayNullable?: boolean, isReadOnly?: boolean } = {}): Field { + const { isRequired = false, isArray = false, isArrayNullable, isReadOnly } = options; + return { + name, + type, + attributes: [], + isArray, + isRequired, + isReadOnly, + ...(isArray && { isArrayNullable }), + }; +} + +function generateTimestampField(name: string): Field { + return { + ...generateField(name, 'AWSDateTime', { isReadOnly: true }), + isReadOnly: true, + }; +} + +function generateMessagesField(messageModelName: string): Field { + return { + ...generateField('messages', { model: messageModelName }, { isArray: true, isArrayNullable: true }), + association: { + connectionType: CodeGenConnectionType.HAS_MANY, + associatedWith: ['conversationId'] + } + }; +} + +function generateConversationField(conversationModelName: string): Field { + return { + ...generateField('conversation', { model: conversationModelName }), + association: { + connectionType: CodeGenConnectionType.BELONGS_TO, + targetNames: ['conversationId'] + } + }; +} + +function generateAuthAttribute(): FieldAttribute { + return { + type: 'auth', + properties: { + rules: [ + { + provider: 'userPools', + ownerField: 'owner', + allow: 'owner', + identityClaim: 'cognito:username', + operations: ['create', 'update', 'delete', 'read'] + } + ] + } + }; +} + +function generatePrimaryKeyInfo(): PrimaryKeyInfo { + return { + isCustomPrimaryKey: false, + primaryKeyFieldName: 'id', + sortKeyFieldNames: [] + }; +} + +function generateSubscriptionMetadata(routeName: string, modelName: string): SchemaSubscription { + return { + isArray: false, + isRequired: false, + name: `onCreateAssistantResponse${routeName}`, + type: { model: modelName }, + arguments: { + 'conversationId': { + name: 'conversationId', + isArray: false, + isRequired: true, + type: 'ID', + }, + } + }; +} diff --git a/packages/appsync-modelgen-plugin/src/validate-cjs.js b/packages/appsync-modelgen-plugin/src/validate-cjs.js index 4e47e2375..1e8875514 100644 --- a/packages/appsync-modelgen-plugin/src/validate-cjs.js +++ b/packages/appsync-modelgen-plugin/src/validate-cjs.js @@ -1 +1 @@ -"use strict";module.exports = validate10;module.exports.default = validate10;const schema11 = {"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"version":{"type":"number","const":1},"models":{"$ref":"#/definitions/SchemaModels"},"nonModels":{"$ref":"#/definitions/SchemaNonModels"},"enums":{"$ref":"#/definitions/SchemaEnums"},"queries":{"$ref":"#/definitions/SchemaQueries"},"mutations":{"$ref":"#/definitions/SchemaMutations"},"subscriptions":{"$ref":"#/definitions/SchemaSubscriptions"},"inputs":{"$ref":"#/definitions/SchemaInputs"}},"required":["version","models","nonModels","enums"],"additionalProperties":false,"description":"Root Schema Representation","definitions":{"SchemaModels":{"$ref":"#/definitions/Record%3Cstring%2CSchemaModel%3E","description":"Top-level Entities on a Schema"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/SchemaModel"}},"SchemaModel":{"type":"object","properties":{"name":{"type":"string"},"attributes":{"type":"array","items":{"$ref":"#/definitions/ModelAttribute"}},"fields":{"$ref":"#/definitions/Fields"},"pluralName":{"type":"string"},"syncable":{"type":"boolean"},"primaryKeyInfo":{"$ref":"#/definitions/PrimaryKeyInfo"}},"required":["name","fields","pluralName","primaryKeyInfo"],"additionalProperties":false},"ModelAttribute":{"type":"object","properties":{"type":{"type":"string"},"properties":{"type":"object"}},"required":["type"],"additionalProperties":false},"Fields":{"$ref":"#/definitions/Record%3Cstring%2CField%3E","description":"Field Definition"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/Field"}},"Field":{"type":"object","properties":{"name":{"type":"string"},"type":{"$ref":"#/definitions/FieldType"},"isArray":{"type":"boolean"},"isRequired":{"type":"boolean"},"isReadOnly":{"type":"boolean"},"isArrayNullable":{"type":"boolean"},"attributes":{"type":"array","items":{"$ref":"#/definitions/FieldAttribute"}},"association":{"$ref":"#/definitions/AssociationType"},"arguments":{"$ref":"#/definitions/Arguments"}},"required":["name","type","isArray","isRequired"],"additionalProperties":false},"FieldType":{"anyOf":[{"$ref":"#/definitions/ScalarType"},{"type":"object","properties":{"enum":{"type":"string"}},"required":["enum"],"additionalProperties":false},{"type":"object","properties":{"model":{"type":"string"}},"required":["model"],"additionalProperties":false},{"type":"object","properties":{"nonModel":{"type":"string"}},"required":["nonModel"],"additionalProperties":false}]},"ScalarType":{"type":"string","enum":["ID","String","Int","Float","AWSDate","AWSTime","AWSDateTime","AWSTimestamp","AWSEmail","AWSURL","AWSIPAddress","Boolean","AWSJSON","AWSPhone"]},"FieldAttribute":{"$ref":"#/definitions/ModelAttribute"},"AssociationType":{"anyOf":[{"$ref":"#/definitions/AssociationHasMany"},{"$ref":"#/definitions/AssociationHasOne"},{"$ref":"#/definitions/AssociationBelongsTo"}]},"AssociationHasMany":{"type":"object","additionalProperties":false,"properties":{"connectionType":{"$ref":"#/definitions/CodeGenConnectionType"},"associatedWith":{"type":"array","items":{"type":"string"}}},"required":["associatedWith","connectionType"]},"CodeGenConnectionType":{"type":"string","enum":["HAS_ONE","BELONGS_TO","HAS_MANY"],"description":"Field-level Relationship Definitions"},"AssociationHasOne":{"type":"object","additionalProperties":false,"properties":{"connectionType":{"$ref":"#/definitions/CodeGenConnectionType"},"associatedWith":{"type":"array","items":{"type":"string"}},"targetNames":{"type":"array","items":{"type":"string"}}},"required":["associatedWith","connectionType","targetNames"]},"AssociationBelongsTo":{"type":"object","additionalProperties":false,"properties":{"connectionType":{"$ref":"#/definitions/CodeGenConnectionType"},"targetNames":{"type":"array","items":{"type":"string"}}},"required":["connectionType","targetNames"]},"Arguments":{"$ref":"#/definitions/Record%3Cstring%2CArgument%3E"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/Argument"}},"Argument":{"type":"object","properties":{"name":{"type":"string"},"type":{"$ref":"#/definitions/InputFieldType"},"isArray":{"type":"boolean"},"isRequired":{"type":"boolean"},"isArrayNullable":{"type":"boolean"}},"required":["name","type","isArray","isRequired"],"additionalProperties":false},"InputFieldType":{"anyOf":[{"$ref":"#/definitions/ScalarType"},{"type":"object","properties":{"enum":{"type":"string"}},"required":["enum"],"additionalProperties":false},{"type":"object","properties":{"input":{"type":"string"}},"required":["input"],"additionalProperties":false}]},"PrimaryKeyInfo":{"type":"object","properties":{"isCustomPrimaryKey":{"type":"boolean"},"primaryKeyFieldName":{"type":"string"},"sortKeyFieldNames":{"type":"array","items":{"type":"string"}}},"required":["isCustomPrimaryKey","primaryKeyFieldName","sortKeyFieldNames"],"additionalProperties":false},"SchemaNonModels":{"$ref":"#/definitions/Record%3Cstring%2CSchemaNonModel%3E"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/SchemaNonModel"}},"SchemaNonModel":{"type":"object","properties":{"name":{"type":"string"},"fields":{"$ref":"#/definitions/Fields"}},"required":["name","fields"],"additionalProperties":false},"SchemaEnums":{"$ref":"#/definitions/Record%3Cstring%2CSchemaEnum%3E"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/SchemaEnum"}},"SchemaEnum":{"type":"object","properties":{"name":{"type":"string"},"values":{"type":"array","items":{"type":"string"}}},"required":["name","values"],"additionalProperties":false},"SchemaQueries":{"$ref":"#/definitions/Record%3Cstring%2CSchemaQuery%3E"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/SchemaQuery"}},"SchemaQuery":{"$ref":"#/definitions/Pick%3CField%2C(%22name%22%7C%22type%22%7C%22isArray%22%7C%22isRequired%22%7C%22isArrayNullable%22%7C%22arguments%22)%3E"},"Pick":{"type":"object","properties":{"name":{"type":"string"},"type":{"$ref":"#/definitions/FieldType"},"isArray":{"type":"boolean"},"isRequired":{"type":"boolean"},"isArrayNullable":{"type":"boolean"},"arguments":{"$ref":"#/definitions/Arguments"}},"required":["name","type","isArray","isRequired"],"additionalProperties":false},"SchemaMutations":{"$ref":"#/definitions/Record%3Cstring%2CSchemaMutation%3E"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/SchemaMutation"}},"SchemaMutation":{"$ref":"#/definitions/SchemaQuery"},"SchemaSubscriptions":{"$ref":"#/definitions/Record%3Cstring%2CSchemaSubscription%3E"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/SchemaSubscription"}},"SchemaSubscription":{"$ref":"#/definitions/SchemaQuery"},"SchemaInputs":{"$ref":"#/definitions/Record%3Cstring%2CInput%3E"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/Input"}},"Input":{"type":"object","properties":{"name":{"type":"string"},"attributes":{"$ref":"#/definitions/Arguments"}},"required":["name","attributes"],"additionalProperties":false,"description":"Input Definition"}}};const schema12 = {"type":"object","additionalProperties":{"$ref":"#/definitions/SchemaModel"}};const schema13 = {"type":"object","properties":{"name":{"type":"string"},"attributes":{"type":"array","items":{"$ref":"#/definitions/ModelAttribute"}},"fields":{"$ref":"#/definitions/Fields"},"pluralName":{"type":"string"},"syncable":{"type":"boolean"},"primaryKeyInfo":{"$ref":"#/definitions/PrimaryKeyInfo"}},"required":["name","fields","pluralName","primaryKeyInfo"],"additionalProperties":false};const schema14 = {"type":"object","properties":{"type":{"type":"string"},"properties":{"type":"object"}},"required":["type"],"additionalProperties":false};const schema31 = {"type":"object","properties":{"isCustomPrimaryKey":{"type":"boolean"},"primaryKeyFieldName":{"type":"string"},"sortKeyFieldNames":{"type":"array","items":{"type":"string"}}},"required":["isCustomPrimaryKey","primaryKeyFieldName","sortKeyFieldNames"],"additionalProperties":false};const schema15 = {"type":"object","additionalProperties":{"$ref":"#/definitions/Field"}};const schema16 = {"type":"object","properties":{"name":{"type":"string"},"type":{"$ref":"#/definitions/FieldType"},"isArray":{"type":"boolean"},"isRequired":{"type":"boolean"},"isReadOnly":{"type":"boolean"},"isArrayNullable":{"type":"boolean"},"attributes":{"type":"array","items":{"$ref":"#/definitions/FieldAttribute"}},"association":{"$ref":"#/definitions/AssociationType"},"arguments":{"$ref":"#/definitions/Arguments"}},"required":["name","type","isArray","isRequired"],"additionalProperties":false};const func2 = Object.prototype.hasOwnProperty;const schema17 = {"anyOf":[{"$ref":"#/definitions/ScalarType"},{"type":"object","properties":{"enum":{"type":"string"}},"required":["enum"],"additionalProperties":false},{"type":"object","properties":{"model":{"type":"string"}},"required":["model"],"additionalProperties":false},{"type":"object","properties":{"nonModel":{"type":"string"}},"required":["nonModel"],"additionalProperties":false}]};const schema18 = {"type":"string","enum":["ID","String","Int","Float","AWSDate","AWSTime","AWSDateTime","AWSTimestamp","AWSEmail","AWSURL","AWSIPAddress","Boolean","AWSJSON","AWSPhone"]};function validate15(data, {instancePath="", parentData, parentDataProperty, rootData=data}={}){let vErrors = null;let errors = 0;const _errs0 = errors;let valid0 = false;const _errs1 = errors;if(typeof data !== "string"){const err0 = {instancePath,schemaPath:"#/definitions/ScalarType/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(!((((((((((((((data === "ID") || (data === "String")) || (data === "Int")) || (data === "Float")) || (data === "AWSDate")) || (data === "AWSTime")) || (data === "AWSDateTime")) || (data === "AWSTimestamp")) || (data === "AWSEmail")) || (data === "AWSURL")) || (data === "AWSIPAddress")) || (data === "Boolean")) || (data === "AWSJSON")) || (data === "AWSPhone"))){const err1 = {instancePath,schemaPath:"#/definitions/ScalarType/enum",keyword:"enum",params:{allowedValues: schema18.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}var _valid0 = _errs1 === errors;valid0 = valid0 || _valid0;if(!valid0){const _errs4 = errors;if(errors === _errs4){if(data && typeof data == "object" && !Array.isArray(data)){let missing0;if((data.enum === undefined) && (missing0 = "enum")){const err2 = {instancePath,schemaPath:"#/anyOf/1/required",keyword:"required",params:{missingProperty: missing0},message:"must have required property '"+missing0+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}else {const _errs6 = errors;for(const key0 in data){if(!(key0 === "enum")){const err3 = {instancePath,schemaPath:"#/anyOf/1/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;break;}}if(_errs6 === errors){if(data.enum !== undefined){if(typeof data.enum !== "string"){const err4 = {instancePath:instancePath+"/enum",schemaPath:"#/anyOf/1/properties/enum/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}}}}}else {const err5 = {instancePath,schemaPath:"#/anyOf/1/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}var _valid0 = _errs4 === errors;valid0 = valid0 || _valid0;if(!valid0){const _errs9 = errors;if(errors === _errs9){if(data && typeof data == "object" && !Array.isArray(data)){let missing1;if((data.model === undefined) && (missing1 = "model")){const err6 = {instancePath,schemaPath:"#/anyOf/2/required",keyword:"required",params:{missingProperty: missing1},message:"must have required property '"+missing1+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}else {const _errs11 = errors;for(const key1 in data){if(!(key1 === "model")){const err7 = {instancePath,schemaPath:"#/anyOf/2/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key1},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;break;}}if(_errs11 === errors){if(data.model !== undefined){if(typeof data.model !== "string"){const err8 = {instancePath:instancePath+"/model",schemaPath:"#/anyOf/2/properties/model/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}}}}else {const err9 = {instancePath,schemaPath:"#/anyOf/2/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}var _valid0 = _errs9 === errors;valid0 = valid0 || _valid0;if(!valid0){const _errs14 = errors;if(errors === _errs14){if(data && typeof data == "object" && !Array.isArray(data)){let missing2;if((data.nonModel === undefined) && (missing2 = "nonModel")){const err10 = {instancePath,schemaPath:"#/anyOf/3/required",keyword:"required",params:{missingProperty: missing2},message:"must have required property '"+missing2+"'"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}else {const _errs16 = errors;for(const key2 in data){if(!(key2 === "nonModel")){const err11 = {instancePath,schemaPath:"#/anyOf/3/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key2},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;break;}}if(_errs16 === errors){if(data.nonModel !== undefined){if(typeof data.nonModel !== "string"){const err12 = {instancePath:instancePath+"/nonModel",schemaPath:"#/anyOf/3/properties/nonModel/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}}}}else {const err13 = {instancePath,schemaPath:"#/anyOf/3/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}var _valid0 = _errs14 === errors;valid0 = valid0 || _valid0;}}}if(!valid0){const err14 = {instancePath,schemaPath:"#/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;validate15.errors = vErrors;return false;}else {errors = _errs0;if(vErrors !== null){if(_errs0){vErrors.length = _errs0;}else {vErrors = null;}}}validate15.errors = vErrors;return errors === 0;}const schema20 = {"anyOf":[{"$ref":"#/definitions/AssociationHasMany"},{"$ref":"#/definitions/AssociationHasOne"},{"$ref":"#/definitions/AssociationBelongsTo"}]};const schema21 = {"type":"object","additionalProperties":false,"properties":{"connectionType":{"$ref":"#/definitions/CodeGenConnectionType"},"associatedWith":{"type":"array","items":{"type":"string"}}},"required":["associatedWith","connectionType"]};const schema22 = {"type":"string","enum":["HAS_ONE","BELONGS_TO","HAS_MANY"],"description":"Field-level Relationship Definitions"};function validate18(data, {instancePath="", parentData, parentDataProperty, rootData=data}={}){let vErrors = null;let errors = 0;if(errors === 0){if(data && typeof data == "object" && !Array.isArray(data)){let missing0;if(((data.associatedWith === undefined) && (missing0 = "associatedWith")) || ((data.connectionType === undefined) && (missing0 = "connectionType"))){validate18.errors = [{instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: missing0},message:"must have required property '"+missing0+"'"}];return false;}else {const _errs1 = errors;for(const key0 in data){if(!((key0 === "connectionType") || (key0 === "associatedWith"))){validate18.errors = [{instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"}];return false;break;}}if(_errs1 === errors){if(data.connectionType !== undefined){let data0 = data.connectionType;const _errs2 = errors;if(typeof data0 !== "string"){validate18.errors = [{instancePath:instancePath+"/connectionType",schemaPath:"#/definitions/CodeGenConnectionType/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}if(!(((data0 === "HAS_ONE") || (data0 === "BELONGS_TO")) || (data0 === "HAS_MANY"))){validate18.errors = [{instancePath:instancePath+"/connectionType",schemaPath:"#/definitions/CodeGenConnectionType/enum",keyword:"enum",params:{allowedValues: schema22.enum},message:"must be equal to one of the allowed values"}];return false;}var valid0 = _errs2 === errors;}else {var valid0 = true;}if(valid0){if(data.associatedWith !== undefined){let data1 = data.associatedWith;const _errs5 = errors;if(errors === _errs5){if(Array.isArray(data1)){var valid2 = true;const len0 = data1.length;for(let i0=0; i0":{"type":"object","additionalProperties":{"$ref":"#/definitions/SchemaModel"}},"SchemaModel":{"type":"object","properties":{"name":{"type":"string"},"attributes":{"type":"array","items":{"$ref":"#/definitions/ModelAttribute"}},"fields":{"$ref":"#/definitions/Fields"},"pluralName":{"type":"string"},"syncable":{"type":"boolean"},"primaryKeyInfo":{"$ref":"#/definitions/PrimaryKeyInfo"}},"required":["name","fields","pluralName","primaryKeyInfo"],"additionalProperties":false},"ModelAttribute":{"type":"object","properties":{"type":{"type":"string"},"properties":{"type":"object"}},"required":["type"],"additionalProperties":false},"Fields":{"$ref":"#/definitions/Record%3Cstring%2CField%3E","description":"Field Definition"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/Field"}},"Field":{"type":"object","properties":{"name":{"type":"string"},"type":{"$ref":"#/definitions/FieldType"},"isArray":{"type":"boolean"},"isRequired":{"type":"boolean"},"isReadOnly":{"type":"boolean"},"isArrayNullable":{"type":"boolean"},"attributes":{"type":"array","items":{"$ref":"#/definitions/FieldAttribute"}},"association":{"$ref":"#/definitions/AssociationType"},"arguments":{"$ref":"#/definitions/Arguments"}},"required":["name","type","isArray","isRequired"],"additionalProperties":false},"FieldType":{"anyOf":[{"$ref":"#/definitions/ScalarType"},{"type":"object","properties":{"enum":{"type":"string"}},"required":["enum"],"additionalProperties":false},{"type":"object","properties":{"model":{"type":"string"}},"required":["model"],"additionalProperties":false},{"type":"object","properties":{"nonModel":{"type":"string"}},"required":["nonModel"],"additionalProperties":false}]},"ScalarType":{"type":"string","enum":["ID","String","Int","Float","AWSDate","AWSTime","AWSDateTime","AWSTimestamp","AWSEmail","AWSURL","AWSIPAddress","Boolean","AWSJSON","AWSPhone"]},"FieldAttribute":{"$ref":"#/definitions/ModelAttribute"},"AssociationType":{"anyOf":[{"$ref":"#/definitions/AssociationHasMany"},{"$ref":"#/definitions/AssociationHasOne"},{"$ref":"#/definitions/AssociationBelongsTo"}]},"AssociationHasMany":{"type":"object","additionalProperties":false,"properties":{"connectionType":{"$ref":"#/definitions/CodeGenConnectionType"},"associatedWith":{"type":"array","items":{"type":"string"}}},"required":["associatedWith","connectionType"]},"CodeGenConnectionType":{"type":"string","enum":["HAS_ONE","BELONGS_TO","HAS_MANY"],"description":"Field-level Relationship Definitions"},"AssociationHasOne":{"type":"object","additionalProperties":false,"properties":{"connectionType":{"$ref":"#/definitions/CodeGenConnectionType"},"associatedWith":{"type":"array","items":{"type":"string"}},"targetNames":{"type":"array","items":{"type":"string"}}},"required":["associatedWith","connectionType","targetNames"]},"AssociationBelongsTo":{"type":"object","additionalProperties":false,"properties":{"connectionType":{"$ref":"#/definitions/CodeGenConnectionType"},"targetNames":{"type":"array","items":{"type":"string"}}},"required":["connectionType","targetNames"]},"Arguments":{"$ref":"#/definitions/Record%3Cstring%2CArgument%3E"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/Argument"}},"Argument":{"type":"object","properties":{"name":{"type":"string"},"type":{"$ref":"#/definitions/InputFieldType"},"isArray":{"type":"boolean"},"isRequired":{"type":"boolean"},"isArrayNullable":{"type":"boolean"}},"required":["name","type","isArray","isRequired"],"additionalProperties":false},"InputFieldType":{"anyOf":[{"$ref":"#/definitions/ScalarType"},{"type":"object","properties":{"enum":{"type":"string"}},"required":["enum"],"additionalProperties":false},{"type":"object","properties":{"input":{"type":"string"}},"required":["input"],"additionalProperties":false}]},"PrimaryKeyInfo":{"type":"object","properties":{"isCustomPrimaryKey":{"type":"boolean"},"primaryKeyFieldName":{"type":"string"},"sortKeyFieldNames":{"type":"array","items":{"type":"string"}}},"required":["isCustomPrimaryKey","primaryKeyFieldName","sortKeyFieldNames"],"additionalProperties":false},"SchemaNonModels":{"$ref":"#/definitions/Record%3Cstring%2CSchemaNonModel%3E"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/SchemaNonModel"}},"SchemaNonModel":{"type":"object","properties":{"name":{"type":"string"},"fields":{"$ref":"#/definitions/Fields"}},"required":["name","fields"],"additionalProperties":false},"SchemaEnums":{"$ref":"#/definitions/Record%3Cstring%2CSchemaEnum%3E"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/SchemaEnum"}},"SchemaEnum":{"type":"object","properties":{"name":{"type":"string"},"values":{"type":"array","items":{"type":"string"}}},"required":["name","values"],"additionalProperties":false},"SchemaQueries":{"$ref":"#/definitions/Record%3Cstring%2CSchemaQuery%3E"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/SchemaQuery"}},"SchemaQuery":{"$ref":"#/definitions/Pick%3CField%2C(%22name%22%7C%22type%22%7C%22isArray%22%7C%22isRequired%22%7C%22isArrayNullable%22%7C%22arguments%22)%3E"},"Pick":{"type":"object","properties":{"name":{"type":"string"},"type":{"$ref":"#/definitions/FieldType"},"isArray":{"type":"boolean"},"isRequired":{"type":"boolean"},"isArrayNullable":{"type":"boolean"},"arguments":{"$ref":"#/definitions/Arguments"}},"required":["name","type","isArray","isRequired"],"additionalProperties":false},"SchemaMutations":{"$ref":"#/definitions/Record%3Cstring%2CSchemaMutation%3E"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/SchemaMutation"}},"SchemaMutation":{"$ref":"#/definitions/SchemaQuery"},"SchemaSubscriptions":{"$ref":"#/definitions/Record%3Cstring%2CSchemaSubscription%3E"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/SchemaSubscription"}},"SchemaSubscription":{"$ref":"#/definitions/SchemaQuery"},"SchemaInputs":{"$ref":"#/definitions/Record%3Cstring%2CInput%3E"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/Input"}},"Input":{"type":"object","properties":{"name":{"type":"string"},"attributes":{"$ref":"#/definitions/Arguments"}},"required":["name","attributes"],"additionalProperties":false,"description":"Input Definition"},"SchemaGenerations":{"$ref":"#/definitions/SchemaQueries"},"SchemaConversationRoutes":{"$ref":"#/definitions/Record%3Cstring%2CSchemaConversationRoute%3E"},"Record":{"type":"object","additionalProperties":{"$ref":"#/definitions/SchemaConversationRoute"}},"SchemaConversationRoute":{"type":"object","properties":{"name":{"type":"string"},"models":{"$ref":"#/definitions/SchemaModels"},"nonModels":{"$ref":"#/definitions/SchemaNonModels"},"enums":{"$ref":"#/definitions/SchemaEnums"},"conversation":{"$ref":"#/definitions/SchemaConversation"},"message":{"$ref":"#/definitions/SchemaConversationMessage"}},"required":["name","models","nonModels","enums","conversation","message"],"additionalProperties":false},"SchemaConversation":{"type":"object","properties":{"modelName":{"type":"string"}},"required":["modelName"],"additionalProperties":false},"SchemaConversationMessage":{"type":"object","properties":{"modelName":{"type":"string"},"subscribe":{"$ref":"#/definitions/SchemaSubscription"},"send":{"$ref":"#/definitions/SchemaMutation"}},"required":["modelName","subscribe","send"],"additionalProperties":false}}};const func2 = Object.prototype.hasOwnProperty;const schema12 = {"type":"object","additionalProperties":{"$ref":"#/definitions/SchemaModel"}};const schema13 = {"type":"object","properties":{"name":{"type":"string"},"attributes":{"type":"array","items":{"$ref":"#/definitions/ModelAttribute"}},"fields":{"$ref":"#/definitions/Fields"},"pluralName":{"type":"string"},"syncable":{"type":"boolean"},"primaryKeyInfo":{"$ref":"#/definitions/PrimaryKeyInfo"}},"required":["name","fields","pluralName","primaryKeyInfo"],"additionalProperties":false};const schema14 = {"type":"object","properties":{"type":{"type":"string"},"properties":{"type":"object"}},"required":["type"],"additionalProperties":false};const schema31 = {"type":"object","properties":{"isCustomPrimaryKey":{"type":"boolean"},"primaryKeyFieldName":{"type":"string"},"sortKeyFieldNames":{"type":"array","items":{"type":"string"}}},"required":["isCustomPrimaryKey","primaryKeyFieldName","sortKeyFieldNames"],"additionalProperties":false};const schema15 = {"type":"object","additionalProperties":{"$ref":"#/definitions/Field"}};const schema16 = {"type":"object","properties":{"name":{"type":"string"},"type":{"$ref":"#/definitions/FieldType"},"isArray":{"type":"boolean"},"isRequired":{"type":"boolean"},"isReadOnly":{"type":"boolean"},"isArrayNullable":{"type":"boolean"},"attributes":{"type":"array","items":{"$ref":"#/definitions/FieldAttribute"}},"association":{"$ref":"#/definitions/AssociationType"},"arguments":{"$ref":"#/definitions/Arguments"}},"required":["name","type","isArray","isRequired"],"additionalProperties":false};const schema17 = {"anyOf":[{"$ref":"#/definitions/ScalarType"},{"type":"object","properties":{"enum":{"type":"string"}},"required":["enum"],"additionalProperties":false},{"type":"object","properties":{"model":{"type":"string"}},"required":["model"],"additionalProperties":false},{"type":"object","properties":{"nonModel":{"type":"string"}},"required":["nonModel"],"additionalProperties":false}]};const schema18 = {"type":"string","enum":["ID","String","Int","Float","AWSDate","AWSTime","AWSDateTime","AWSTimestamp","AWSEmail","AWSURL","AWSIPAddress","Boolean","AWSJSON","AWSPhone"]};function validate15(data, {instancePath="", parentData, parentDataProperty, rootData=data}={}){let vErrors = null;let errors = 0;const _errs0 = errors;let valid0 = false;const _errs1 = errors;if(typeof data !== "string"){const err0 = {instancePath,schemaPath:"#/definitions/ScalarType/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err0];}else {vErrors.push(err0);}errors++;}if(!((((((((((((((data === "ID") || (data === "String")) || (data === "Int")) || (data === "Float")) || (data === "AWSDate")) || (data === "AWSTime")) || (data === "AWSDateTime")) || (data === "AWSTimestamp")) || (data === "AWSEmail")) || (data === "AWSURL")) || (data === "AWSIPAddress")) || (data === "Boolean")) || (data === "AWSJSON")) || (data === "AWSPhone"))){const err1 = {instancePath,schemaPath:"#/definitions/ScalarType/enum",keyword:"enum",params:{allowedValues: schema18.enum},message:"must be equal to one of the allowed values"};if(vErrors === null){vErrors = [err1];}else {vErrors.push(err1);}errors++;}var _valid0 = _errs1 === errors;valid0 = valid0 || _valid0;if(!valid0){const _errs4 = errors;if(errors === _errs4){if(data && typeof data == "object" && !Array.isArray(data)){let missing0;if((data.enum === undefined) && (missing0 = "enum")){const err2 = {instancePath,schemaPath:"#/anyOf/1/required",keyword:"required",params:{missingProperty: missing0},message:"must have required property '"+missing0+"'"};if(vErrors === null){vErrors = [err2];}else {vErrors.push(err2);}errors++;}else {const _errs6 = errors;for(const key0 in data){if(!(key0 === "enum")){const err3 = {instancePath,schemaPath:"#/anyOf/1/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err3];}else {vErrors.push(err3);}errors++;break;}}if(_errs6 === errors){if(data.enum !== undefined){if(typeof data.enum !== "string"){const err4 = {instancePath:instancePath+"/enum",schemaPath:"#/anyOf/1/properties/enum/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err4];}else {vErrors.push(err4);}errors++;}}}}}else {const err5 = {instancePath,schemaPath:"#/anyOf/1/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err5];}else {vErrors.push(err5);}errors++;}}var _valid0 = _errs4 === errors;valid0 = valid0 || _valid0;if(!valid0){const _errs9 = errors;if(errors === _errs9){if(data && typeof data == "object" && !Array.isArray(data)){let missing1;if((data.model === undefined) && (missing1 = "model")){const err6 = {instancePath,schemaPath:"#/anyOf/2/required",keyword:"required",params:{missingProperty: missing1},message:"must have required property '"+missing1+"'"};if(vErrors === null){vErrors = [err6];}else {vErrors.push(err6);}errors++;}else {const _errs11 = errors;for(const key1 in data){if(!(key1 === "model")){const err7 = {instancePath,schemaPath:"#/anyOf/2/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key1},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err7];}else {vErrors.push(err7);}errors++;break;}}if(_errs11 === errors){if(data.model !== undefined){if(typeof data.model !== "string"){const err8 = {instancePath:instancePath+"/model",schemaPath:"#/anyOf/2/properties/model/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err8];}else {vErrors.push(err8);}errors++;}}}}}else {const err9 = {instancePath,schemaPath:"#/anyOf/2/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err9];}else {vErrors.push(err9);}errors++;}}var _valid0 = _errs9 === errors;valid0 = valid0 || _valid0;if(!valid0){const _errs14 = errors;if(errors === _errs14){if(data && typeof data == "object" && !Array.isArray(data)){let missing2;if((data.nonModel === undefined) && (missing2 = "nonModel")){const err10 = {instancePath,schemaPath:"#/anyOf/3/required",keyword:"required",params:{missingProperty: missing2},message:"must have required property '"+missing2+"'"};if(vErrors === null){vErrors = [err10];}else {vErrors.push(err10);}errors++;}else {const _errs16 = errors;for(const key2 in data){if(!(key2 === "nonModel")){const err11 = {instancePath,schemaPath:"#/anyOf/3/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key2},message:"must NOT have additional properties"};if(vErrors === null){vErrors = [err11];}else {vErrors.push(err11);}errors++;break;}}if(_errs16 === errors){if(data.nonModel !== undefined){if(typeof data.nonModel !== "string"){const err12 = {instancePath:instancePath+"/nonModel",schemaPath:"#/anyOf/3/properties/nonModel/type",keyword:"type",params:{type: "string"},message:"must be string"};if(vErrors === null){vErrors = [err12];}else {vErrors.push(err12);}errors++;}}}}}else {const err13 = {instancePath,schemaPath:"#/anyOf/3/type",keyword:"type",params:{type: "object"},message:"must be object"};if(vErrors === null){vErrors = [err13];}else {vErrors.push(err13);}errors++;}}var _valid0 = _errs14 === errors;valid0 = valid0 || _valid0;}}}if(!valid0){const err14 = {instancePath,schemaPath:"#/anyOf",keyword:"anyOf",params:{},message:"must match a schema in anyOf"};if(vErrors === null){vErrors = [err14];}else {vErrors.push(err14);}errors++;validate15.errors = vErrors;return false;}else {errors = _errs0;if(vErrors !== null){if(_errs0){vErrors.length = _errs0;}else {vErrors = null;}}}validate15.errors = vErrors;return errors === 0;}const schema20 = {"anyOf":[{"$ref":"#/definitions/AssociationHasMany"},{"$ref":"#/definitions/AssociationHasOne"},{"$ref":"#/definitions/AssociationBelongsTo"}]};const schema21 = {"type":"object","additionalProperties":false,"properties":{"connectionType":{"$ref":"#/definitions/CodeGenConnectionType"},"associatedWith":{"type":"array","items":{"type":"string"}}},"required":["associatedWith","connectionType"]};const schema22 = {"type":"string","enum":["HAS_ONE","BELONGS_TO","HAS_MANY"],"description":"Field-level Relationship Definitions"};function validate18(data, {instancePath="", parentData, parentDataProperty, rootData=data}={}){let vErrors = null;let errors = 0;if(errors === 0){if(data && typeof data == "object" && !Array.isArray(data)){let missing0;if(((data.associatedWith === undefined) && (missing0 = "associatedWith")) || ((data.connectionType === undefined) && (missing0 = "connectionType"))){validate18.errors = [{instancePath,schemaPath:"#/required",keyword:"required",params:{missingProperty: missing0},message:"must have required property '"+missing0+"'"}];return false;}else {const _errs1 = errors;for(const key0 in data){if(!((key0 === "connectionType") || (key0 === "associatedWith"))){validate18.errors = [{instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"}];return false;break;}}if(_errs1 === errors){if(data.connectionType !== undefined){let data0 = data.connectionType;const _errs2 = errors;if(typeof data0 !== "string"){validate18.errors = [{instancePath:instancePath+"/connectionType",schemaPath:"#/definitions/CodeGenConnectionType/type",keyword:"type",params:{type: "string"},message:"must be string"}];return false;}if(!(((data0 === "HAS_ONE") || (data0 === "BELONGS_TO")) || (data0 === "HAS_MANY"))){validate18.errors = [{instancePath:instancePath+"/connectionType",schemaPath:"#/definitions/CodeGenConnectionType/enum",keyword:"enum",params:{allowedValues: schema22.enum},message:"must be equal to one of the allowed values"}];return false;}var valid0 = _errs2 === errors;}else {var valid0 = true;}if(valid0){if(data.associatedWith !== undefined){let data1 = data.associatedWith;const _errs5 = errors;if(errors === _errs5){if(Array.isArray(data1)){var valid2 = true;const len0 = data1.length;for(let i0=0; i0 { // Skip the field if the field type is union/interface // TODO: Remove this skip once these types are supported for stakeholder usages + // Also skip if this query has a generation directive. These are handled separately and keyed under `generations`. const fieldType = this.getType(queryObj.type) as any; - if (this.isUnionFieldType(fieldType) || this.isInterfaceFieldType(fieldType)) { + if (this.isUnionFieldType(fieldType) || this.isInterfaceFieldType(fieldType) || containsGenerationDirective(queryObj)) { return acc; } return { ...acc, [queryObj.name]: this.generateGraphQLOperationMetadata(queryObj) }; }, {}) + const generations = Object.values(this.queryMap).reduce((acc, queryObj: CodeGenQuery) => { + // Skip the field if the field type is union/interface + // TODO: Remove this skip once these types are supported for stakeholder usages + const fieldType = this.getType(queryObj.type) as any; + if (this.isUnionFieldType(fieldType) || this.isInterfaceFieldType(fieldType) || !containsGenerationDirective(queryObj)) { + return acc; + } + return { ...acc, [queryObj.name]: this.generateGenerationMetadata(queryObj) }; + }, {}); const mutations = Object.values(this.mutationMap).reduce((acc, mutationObj: CodeGenMutation) => { // Skip the field if the field type is union/interface // TODO: Remove this skip once these types are supported for stakeholder usages + // Additionally, skip the mutation if it contains the @conversation directive -- those are keyed under `conversations` const fieldType = this.getType(mutationObj.type) as any; - if (this.isUnionFieldType(fieldType) || this.isInterfaceFieldType(fieldType)) { + if (this.isUnionFieldType(fieldType) || this.isInterfaceFieldType(fieldType) || containsConversationDirective(mutationObj)) { return acc; } return { ...acc, [mutationObj.name]: this.generateGraphQLOperationMetadata(mutationObj) }; }, {}); + const conversations = Object.values(this.mutationMap).reduce((acc, mutationObj: CodeGenMutation) => { + if (!containsConversationDirective(mutationObj)) { + return acc; + } + return { ...acc, [mutationObj.name]: this.generateConversationMetadata(mutationObj) } + }, {}); const subscriptions = Object.values(this.subscriptionMap).reduce((acc, subscriptionObj: CodeGenSubscription) => { // Skip the field if the field type is union/interface // TODO: Remove this skip once these types are supported for stakeholder usages @@ -95,9 +115,15 @@ export class AppSyncModelIntrospectionVisitor< if (Object.keys(queries).length > 0) { result = { ...result, queries }; } + if (Object.keys(generations).length > 0) { + result = { ...result, generations }; + } if (Object.keys(mutations).length > 0) { result = { ...result, mutations }; } + if (Object.keys(conversations).length > 0) { + result = { ...result, conversations } + } if (Object.keys(subscriptions).length > 0) { result = { ...result, subscriptions }; } @@ -114,8 +140,8 @@ export class AppSyncModelIntrospectionVisitor< if (connectionInfo.kind === CodeGenConnectionType.HAS_MANY) { connectionAttribute.associatedWith = connectionInfo.associatedWithFields.map(f => this.getFieldName(f)); } else if (connectionInfo.kind === CodeGenConnectionType.HAS_ONE) { - connectionAttribute.associatedWith = connectionInfo.associatedWithFields.map(f => this.getFieldName(f)); - connectionAttribute.targetNames = connectionInfo.targetNames ?? []; + connectionAttribute.associatedWith = connectionInfo.associatedWithFields.map(f => this.getFieldName(f)); + connectionAttribute.targetNames = connectionInfo.targetNames ?? []; } else { connectionAttribute.targetNames = connectionInfo.targetNames; } @@ -189,7 +215,7 @@ export class AppSyncModelIntrospectionVisitor< private generateGraphQLInputMetadata(inputObj: CodeGenInputObject): Input { return { name: inputObj.name, - attributes: inputObj.inputValues.reduce((acc, param ) => { + attributes: inputObj.inputValues.reduce((acc, param) => { const arg: Argument = { name: param.name, isArray: param.isList, @@ -220,7 +246,7 @@ export class AppSyncModelIntrospectionVisitor< (operationMeta as V).isArrayNullable = operationObj.isListNullable; } if (operationObj.parameters && operationObj.parameters.length > 0) { - (operationMeta as V).arguments = operationObj.parameters.reduce((acc, param ) => { + (operationMeta as V).arguments = operationObj.parameters.reduce((acc, param) => { const arg: Argument = { name: param.name, isArray: param.isList, @@ -236,6 +262,14 @@ export class AppSyncModelIntrospectionVisitor< return operationMeta as V; } + private generateGenerationMetadata(generationObj: CodeGenQuery): SchemaQuery { + return this.generateGraphQLOperationMetadata(generationObj); + } + + private generateConversationMetadata(mutationObj: CodeGenMutation): SchemaConversationRoute { + return processConversationRoute(mutationObj, this.generateGraphQLOperationMetadata); + } + protected getType(gqlType: string): FieldType | InputFieldType | UnionFieldType | InterfaceFieldType { // Todo: Handle unlisted scalars if (gqlType in METADATA_SCALAR_MAP) {