amplify-codegen icon indicating copy to clipboard operation
amplify-codegen copied to clipboard

Amplify codegen models is creating id when primarykey is specified

Open kddejong opened this issue 2 years ago • 8 comments

Before opening, please confirm:

  • [X] I have installed the latest version of the Amplify CLI (see above), and confirmed that the issue still persists.
  • [X] I have searched for duplicate or closed issues.
  • [X] I have read the guide for submitting bug reports.
  • [X] I have done my best to include a minimal, self-contained set of instructions for consistently reproducing the issue.
  • [X] I have removed any sensitive information from my code snippets and submission.

How did you install the Amplify CLI?

npm

If applicable, what version of Node.js are you using?

v16.14.0

Amplify CLI Version

8.0.3

What operating system are you using?

Mac

Did you make any manual changes to the cloud resources managed by Amplify? Please describe the changes made.

No

Amplify Categories

api

Amplify Commands

codegen

Describe the bug

When specifying the following model.

type Todo @model {
  key: ID! @primaryKey
  name: String!
  description: String
}

I'm still getting an id field in the files created by amplify codegen models

Expected behavior

I expect the DataStore to adhere to the primaryKey directive.

Reproduction steps

Create a schema as described and then do amplify codegen models

GraphQL schema(s)

type Todo @model {
  key: ID! @primaryKey
  name: String!
  description: String
}


Log output

# Put your logs below this line


Additional information

schema.js

export const schema = {
    "models": {
        "Todo": {
            "name": "Todo",
            "fields": {
                "id": {
                    "name": "id",
                    "isArray": false,
                    "type": "ID",
                    "isRequired": true,
                    "attributes": []
                },
                "key": {
                    "name": "key",
                    "isArray": false,
                    "type": "ID",
                    "isRequired": true,
                    "attributes": []
                },
                "name": {
                    "name": "name",
                    "isArray": false,
                    "type": "String",
                    "isRequired": true,
                    "attributes": []
                },
                "description": {
                    "name": "description",
                    "isArray": false,
                    "type": "String",
                    "isRequired": false,
                    "attributes": []
                },
                "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": "Todos",
            "attributes": [
                {
                    "type": "model",
                    "properties": {}
                },
                {
                    "type": "key",
                    "properties": {
                        "fields": [
                            "key"
                        ]
                    }
                }
            ]
        }
    },
    "enums": {},
    "nonModels": {},
    "version": "fe2b4ee8f0b0a06f4de46e98893aaf50"
};

Additionally it looks like when using the Datastore the subscriptions are looking for the id column which doesn't exist on the server.

kddejong avatar Apr 29 '22 20:04 kddejong

Hey @kddejong :wave: thanks for raising this! I was able to successfully reproduce this behavior using the provided steps:

type Todo @model {
  key: ID! @primaryKey
  name: String!
  description: String
}

outputs (in our API's build/ directory):

type Todo {
  key: ID!
  name: String!
  description: String
  createdAt: AWSDateTime!
  updatedAt: AWSDateTime!
}

However the generated models include a required id field as you mentioned:

export declare class Todo {
  readonly id: string;
  readonly key: string;
  readonly name: string;
  readonly description?: string | null;
  readonly createdAt?: string | null;
  readonly updatedAt?: string | null;
  constructor(init: ModelInit<Todo, TodoMetaData>);
  static copyOf(source: Todo, mutator: (draft: MutableModel<Todo, TodoMetaData>) => MutableModel<Todo, TodoMetaData> | void): Todo;
}

Marking as a codegen bug 🙂

josefaidt avatar Apr 29 '22 20:04 josefaidt

Hi @kddejong, this is a known gap in DataStore interacting with a customer Primary Key today. If you refer to the Javascript DataStore docs https://docs.amplify.aws/lib/datastore/relational/q/platform/js/ we do call out that you'll need to explicitly set the id column as well. We are actively working on resolving this feature parity issue, and will be releasing support in the future.

alharris-at avatar May 10 '22 18:05 alharris-at

Hey @kddejong I ran into the same issue and saw your comment. But I ran into another issue is that I cannot override the id column with @primaryKey when creating new item. It seems like amplify is ignoring the @primaryKey notation.

shrentseng avatar Oct 01 '22 04:10 shrentseng

Hi @kddejong, @shrentseng, the team has just merged in support for custom primary keys into the JS library, the next release of the aws-amplify library should add support for custom primary keys, resolving the issues you're seeing here. We are not enabling this functionality for all projects right now, but leaving it behind a feature flag you can enable in the /amplify/cli.json file, called respectPrimaryKeyAttributesOnConnectionField. In order to take advantage of this, once you've upgraded to aws-amplify >4.3.37 (not yet released), you can enable this functionality by flipping that flag to true.

Note: If you use Amplify Studio for CMS or Data Modeling functionality, I'd recommend against enabling that flag for now, as it will cause render issues in Studio. We are working through those issues, and will enable this functionality by default once resolved.

alharris-at avatar Oct 05 '22 15:10 alharris-at

@alharris-at Could you please clarify if the bug can be somehow resolved with regard to Amplify for iOS? Looks like it is became a major issue for iOS:

Amplify/ModelSchema.swift:104: Fatal error: Primary Key not defined for `Boat`
2022-12-09 17:11:23.082976+0200 AddBoat[825:70376] Amplify/ModelSchema.swift:104: Fatal error: Primary Key not defined for `Boat`

while my model is described as in schema.graphql file:

type Boat @model @auth(rules: [
  { allow: owner } ]) {
        id: ID! @primaryKey
        ...

philipto avatar Dec 09 '22 15:12 philipto

I'm getting the same issue - I am using a custom primary key name and an id field is still being added.
userID : ID! @primaryKey my "respectprimarykeyattributesonconnectionfield": true, was already set by default. I'm using version "@aws-amplify/core" "5.0.16"

sharived avatar May 19 '23 19:05 sharived

Just hit this issue in our Swift project. Manually removed the offending id fields in the generated code and switched to specifying a primaryKey(fields:) in model.attributes.

ChadyG avatar Jun 22 '23 20:06 ChadyG

I'm also running into this for my typescript/react project. If I leave the field name as id in the model, then it does work as expected (even when "id" is annotated as @primaryKey). But as soon as I change the name of the @primaryKey field, it stops working.

The behavior that I see is that the Mutationcreate<ENTITY>init0Function pipeline resolver is still generates $util.qr($ctx.stash.defaultValues.put("id", $util.autoId())) instead of using the custom field name there. This value gets merged into the defaults and when the entity eventually gets created in Dynamo, it has this id field and default value merged into the final object. More importantly though, the actual @primaryKey field is not getting an auto-generated value. Furthermore, the GraphQL schema has the @primaryKey field marked as required on create, instead of allowing the field to be optional and populated with a default value in the resolver. I can confirm that when the @primaryKey is id, the createMutation input has it as optional and it does get auto-populated in the resolver. So it seems like the code generation just isn't taking the primaryKey field into account when generating the resolvers and GraphQL schema (though the actual dynamoDB table does have the custom field as the primary key).

For some context, here is the change I have in my schema.graphql:

type Zone @model {
-  id: String! @primaryKey
+  zoneId: String! @primaryKey
   title: String!

and you can see from my schema.json that the id field is now required for the create input:

{
         "kind" : "INPUT_OBJECT",
         "name" : "CreateZoneInput",
         "description" : null,
         "fields" : null,
         "inputFields" : [ {
-          "name" : "id",
+          "name" : "zoneId",
           "description" : null,
           "type" : {
+            "kind" : "NON_NULL",
+            "name" : null,
+            "ofType" : {
               "kind" : "SCALAR",
               "name" : "String",
               "ofType" : null
+            }
           },
           "defaultValue" : null
         }, 

It would be great if this can be fixed before I need to push to production (since the primaryKey fieldname change requires drop/replace on the whole table), but for now I'm going to limp along with the default "id" field name.

pmarkert avatar Sep 06 '23 00:09 pmarkert