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 b12f44e55..224ec091c 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 @@ -2320,3 +2320,327 @@ exports[`Primary key info within a belongsTo model tests should generate correct \\"nonModels\\": {} }" `; + +exports[`schemas with pk on a belongsTo fk works for v1 1`] = ` +"{ + \\"version\\": 1, + \\"models\\": { + \\"Blog\\": { + \\"name\\": \\"Blog\\", + \\"fields\\": { + \\"id\\": { + \\"name\\": \\"id\\", + \\"isArray\\": false, + \\"type\\": \\"ID\\", + \\"isRequired\\": true, + \\"attributes\\": [] + }, + \\"title\\": { + \\"name\\": \\"title\\", + \\"isArray\\": false, + \\"type\\": \\"String\\", + \\"isRequired\\": false, + \\"attributes\\": [] + }, + \\"posts\\": { + \\"name\\": \\"posts\\", + \\"isArray\\": true, + \\"type\\": { + \\"model\\": \\"Post\\" + }, + \\"isRequired\\": false, + \\"attributes\\": [], + \\"isArrayNullable\\": true, + \\"association\\": { + \\"connectionType\\": \\"HAS_MANY\\", + \\"associatedWith\\": [] + } + }, + \\"createdAt\\": { + \\"name\\": \\"createdAt\\", + \\"isArray\\": false, + \\"type\\": \\"AWSDateTime\\", + \\"isRequired\\": false, + \\"attributes\\": [], + \\"isReadOnly\\": true + }, + \\"updatedAt\\": { + \\"name\\": \\"updatedAt\\", + \\"isArray\\": false, + \\"type\\": \\"AWSDateTime\\", + \\"isRequired\\": false, + \\"attributes\\": [], + \\"isReadOnly\\": true + } + }, + \\"syncable\\": true, + \\"pluralName\\": \\"Blogs\\", + \\"attributes\\": [ + { + \\"type\\": \\"model\\", + \\"properties\\": {} + } + ], + \\"primaryKeyInfo\\": { + \\"isCustomPrimaryKey\\": false, + \\"primaryKeyFieldName\\": \\"id\\", + \\"sortKeyFieldNames\\": [] + } + }, + \\"Post\\": { + \\"name\\": \\"Post\\", + \\"fields\\": { + \\"id\\": { + \\"name\\": \\"id\\", + \\"isArray\\": false, + \\"type\\": \\"ID\\", + \\"isRequired\\": true, + \\"attributes\\": [] + }, + \\"blogId\\": { + \\"name\\": \\"blogId\\", + \\"isArray\\": false, + \\"type\\": \\"ID\\", + \\"isRequired\\": true, + \\"attributes\\": [] + }, + \\"title\\": { + \\"name\\": \\"title\\", + \\"isArray\\": false, + \\"type\\": \\"String\\", + \\"isRequired\\": true, + \\"attributes\\": [] + }, + \\"description\\": { + \\"name\\": \\"description\\", + \\"isArray\\": false, + \\"type\\": \\"String\\", + \\"isRequired\\": true, + \\"attributes\\": [] + }, + \\"blog\\": { + \\"name\\": \\"blog\\", + \\"isArray\\": false, + \\"type\\": { + \\"model\\": \\"Blog\\" + }, + \\"isRequired\\": false, + \\"attributes\\": [], + \\"association\\": { + \\"connectionType\\": \\"BELONGS_TO\\", + \\"targetNames\\": [] + } + }, + \\"createdAt\\": { + \\"name\\": \\"createdAt\\", + \\"isArray\\": false, + \\"type\\": \\"AWSDateTime\\", + \\"isRequired\\": false, + \\"attributes\\": [], + \\"isReadOnly\\": true + }, + \\"updatedAt\\": { + \\"name\\": \\"updatedAt\\", + \\"isArray\\": false, + \\"type\\": \\"AWSDateTime\\", + \\"isRequired\\": false, + \\"attributes\\": [], + \\"isReadOnly\\": true + } + }, + \\"syncable\\": true, + \\"pluralName\\": \\"Posts\\", + \\"attributes\\": [ + { + \\"type\\": \\"model\\", + \\"properties\\": {} + }, + { + \\"type\\": \\"key\\", + \\"properties\\": { + \\"fields\\": [ + \\"blogId\\", + \\"title\\", + \\"description\\" + ] + } + } + ], + \\"primaryKeyInfo\\": { + \\"isCustomPrimaryKey\\": true, + \\"primaryKeyFieldName\\": \\"blogId\\", + \\"sortKeyFieldNames\\": [ + \\"title\\", + \\"description\\" + ] + } + } + }, + \\"enums\\": {}, + \\"nonModels\\": {} +}" +`; + +exports[`schemas with pk on a belongsTo fk works for v2 1`] = ` +"{ + \\"version\\": 1, + \\"models\\": { + \\"Blog\\": { + \\"name\\": \\"Blog\\", + \\"fields\\": { + \\"id\\": { + \\"name\\": \\"id\\", + \\"isArray\\": false, + \\"type\\": \\"ID\\", + \\"isRequired\\": true, + \\"attributes\\": [] + }, + \\"title\\": { + \\"name\\": \\"title\\", + \\"isArray\\": false, + \\"type\\": \\"String\\", + \\"isRequired\\": false, + \\"attributes\\": [] + }, + \\"posts\\": { + \\"name\\": \\"posts\\", + \\"isArray\\": true, + \\"type\\": { + \\"model\\": \\"Post\\" + }, + \\"isRequired\\": false, + \\"attributes\\": [], + \\"isArrayNullable\\": true, + \\"association\\": { + \\"connectionType\\": \\"HAS_MANY\\", + \\"associatedWith\\": [ + \\"blog\\" + ] + } + }, + \\"createdAt\\": { + \\"name\\": \\"createdAt\\", + \\"isArray\\": false, + \\"type\\": \\"AWSDateTime\\", + \\"isRequired\\": false, + \\"attributes\\": [], + \\"isReadOnly\\": true + }, + \\"updatedAt\\": { + \\"name\\": \\"updatedAt\\", + \\"isArray\\": false, + \\"type\\": \\"AWSDateTime\\", + \\"isRequired\\": false, + \\"attributes\\": [], + \\"isReadOnly\\": true + } + }, + \\"syncable\\": true, + \\"pluralName\\": \\"Blogs\\", + \\"attributes\\": [ + { + \\"type\\": \\"model\\", + \\"properties\\": {} + } + ], + \\"primaryKeyInfo\\": { + \\"isCustomPrimaryKey\\": false, + \\"primaryKeyFieldName\\": \\"id\\", + \\"sortKeyFieldNames\\": [] + } + }, + \\"Post\\": { + \\"name\\": \\"Post\\", + \\"fields\\": { + \\"id\\": { + \\"name\\": \\"id\\", + \\"isArray\\": false, + \\"type\\": \\"ID\\", + \\"isRequired\\": true, + \\"attributes\\": [] + }, + \\"blogId\\": { + \\"name\\": \\"blogId\\", + \\"isArray\\": false, + \\"type\\": \\"ID\\", + \\"isRequired\\": true, + \\"attributes\\": [] + }, + \\"title\\": { + \\"name\\": \\"title\\", + \\"isArray\\": false, + \\"type\\": \\"String\\", + \\"isRequired\\": true, + \\"attributes\\": [] + }, + \\"description\\": { + \\"name\\": \\"description\\", + \\"isArray\\": false, + \\"type\\": \\"String\\", + \\"isRequired\\": true, + \\"attributes\\": [] + }, + \\"blog\\": { + \\"name\\": \\"blog\\", + \\"isArray\\": false, + \\"type\\": { + \\"model\\": \\"Blog\\" + }, + \\"isRequired\\": false, + \\"attributes\\": [], + \\"association\\": { + \\"connectionType\\": \\"BELONGS_TO\\", + \\"targetNames\\": [ + \\"blogId\\" + ] + } + }, + \\"createdAt\\": { + \\"name\\": \\"createdAt\\", + \\"isArray\\": false, + \\"type\\": \\"AWSDateTime\\", + \\"isRequired\\": false, + \\"attributes\\": [], + \\"isReadOnly\\": true + }, + \\"updatedAt\\": { + \\"name\\": \\"updatedAt\\", + \\"isArray\\": false, + \\"type\\": \\"AWSDateTime\\", + \\"isRequired\\": false, + \\"attributes\\": [], + \\"isReadOnly\\": true + } + }, + \\"syncable\\": true, + \\"pluralName\\": \\"Posts\\", + \\"attributes\\": [ + { + \\"type\\": \\"model\\", + \\"properties\\": {} + }, + { + \\"type\\": \\"key\\", + \\"properties\\": { + \\"fields\\": [ + \\"blogId\\", + \\"title\\", + \\"description\\" + ] + } + } + ], + \\"primaryKeyInfo\\": { + \\"isCustomPrimaryKey\\": true, + \\"primaryKeyFieldName\\": \\"blogId\\", + \\"sortKeyFieldNames\\": [ + \\"title\\", + \\"description\\" + ] + } + } + }, + \\"enums\\": {}, + \\"nonModels\\": {} +}" +`; 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 01b8b2188..e50caf04e 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 @@ -228,4 +228,48 @@ describe('Primary key info within a belongsTo model tests', () => { const visitor: AppSyncModelIntrospectionVisitor = getVisitor(schema, { respectPrimaryKeyAttributesOnConnectionField: false }); expect(visitor.generate()).toMatchSnapshot(); }); -}); \ No newline at end of file +}); + +describe('schemas with pk on a belongsTo fk', () => { + it('works for v1', () => { + expect(getVisitor(/* GraphQL */ ` + type Blog @model { + id: ID! + title: String + posts: [Post] @connection(fields: ["id"]) + } + + type Post @model @key(fields: ["blogId", "title", "description"]) { + id: ID! + blogId: ID! + title: String! + description: String! + blog: Blog @connection(fields: ["blogId"]) + } + `, { + transformerVersion: 1, + usePipelinedTransformer: false, + }).generate()).toMatchSnapshot(); + }); + + it('works for v2', () => { + expect(getVisitor(/* GraphQL */ ` + type Blog @model { + id: ID! + title: String + posts: [Post] @hasMany(fields: ["id"]) + } + + type Post @model { + id: ID! + blogId: ID! @primaryKey(sortKeyFields: ["title", "description"]) + title: String! + description: String! + blog: Blog @belongsTo(fields: ["blogId"]) + } + `, { + transformerVersion: 2, + usePipelinedTransformer: true, + }).generate()).toMatchSnapshot(); + }); +}); diff --git a/packages/appsync-modelgen-plugin/src/visitors/appsync-model-introspection-visitor.ts b/packages/appsync-modelgen-plugin/src/visitors/appsync-model-introspection-visitor.ts index f2cf393a7..d67208921 100644 --- a/packages/appsync-modelgen-plugin/src/visitors/appsync-model-introspection-visitor.ts +++ b/packages/appsync-modelgen-plugin/src/visitors/appsync-model-introspection-visitor.ts @@ -27,6 +27,7 @@ export class AppSyncModelIntrospectionVisitor< const modelIntrospectionSchema = JSON.parse(modelIntrospectionSchemaText); this.schemaValidator = new Ajv().compile(modelIntrospectionSchema); } + generate(): string { const shouldUseModelNameFieldInHasManyAndBelongsTo = false; // This flag is going to be used to tight-trigger on JS implementations only. @@ -152,11 +153,14 @@ export class AppSyncModelIntrospectionVisitor< private generateModelPrimaryKeyInfo(model: CodeGenModel): PrimaryKeyInfo { const primaryKeyField = this.getModelPrimaryKeyField(model); - const { primaryKeyType, sortKeyFields } = primaryKeyField.primaryKeyInfo!; - return { - isCustomPrimaryKey: primaryKeyType === CodeGenPrimaryKeyType.CustomId, - primaryKeyFieldName: this.getFieldName(primaryKeyField), - sortKeyFieldNames: sortKeyFields.map(field => this.getFieldName(field)) - }; + if (primaryKeyField && primaryKeyField.primaryKeyInfo) { + const { primaryKeyType, sortKeyFields } = primaryKeyField.primaryKeyInfo; + return { + isCustomPrimaryKey: primaryKeyType === CodeGenPrimaryKeyType.CustomId, + primaryKeyFieldName: this.getFieldName(primaryKeyField), + sortKeyFieldNames: sortKeyFields.map(field => this.getFieldName(field)) + }; + } + throw new Error(`No primary key found for model ${model.name}`); } } \ No newline at end of file diff --git a/packages/appsync-modelgen-plugin/src/visitors/appsync-visitor.ts b/packages/appsync-modelgen-plugin/src/visitors/appsync-visitor.ts index 71ede95db..75c543373 100644 --- a/packages/appsync-modelgen-plugin/src/visitors/appsync-visitor.ts +++ b/packages/appsync-modelgen-plugin/src/visitors/appsync-visitor.ts @@ -673,7 +673,7 @@ export class AppSyncModelVisitor< if (connectionInfo.kind === CodeGenConnectionType.HAS_MANY || connectionInfo.kind === CodeGenConnectionType.HAS_ONE) { // Need to update the other side of the connection even if there is no connection directive addFieldToModel(connectionInfo.connectedModel, connectionInfo.associatedWith); - } else if (connectionInfo.targetName !== 'id') { + } else if (connectionInfo.targetName !== this.getModelPrimaryKeyField(model)?.name ?? 'id') { // Need to remove the field that is targetName removeFieldFromModel(model, connectionInfo.targetName); }