aws-amplify / amplify-category-api

The AWS Amplify CLI is a toolchain for simplifying serverless web and mobile development. This plugin provides functionality for the API category, allowing for the creation and management of GraphQL and REST based backends for your amplify project.
https://docs.amplify.aws/
Apache License 2.0
81 stars 71 forks source link

[Gen2] 'Custom Identifier + ownerDefinedIn' Cause error: "Implicit field xx conflicts with the explicit field definition." #2576

Open hangoocn opened 1 month ago

hangoocn commented 1 month ago

Environment information

System:
  OS: Windows 10 10.0.19045
  CPU: (12) x64 11th Gen Intel(R) Core(TM) i5-11400 @ 2.60GHz 
  Memory: 15.98 GB / 31.84 GB
Binaries:
  Node: 18.17.1 - C:\Program Files\nodejs\node.EXE
  Yarn: 1.22.19 - ~\AppData\Roaming\npm\yarn.CMD
  npm: 9.8.1 - C:\Program Files\nodejs\npm.CMD
  pnpm: undefined - undefined
NPM Packages:
  @aws-amplify/backend: 1.0.0
  @aws-amplify/backend-cli: 1.0.1
  aws-amplify: 6.2.0
  aws-cdk: 2.140.0
  aws-cdk-lib: 2.140.0
  typescript: 5.4.5
AWS environment variables:
  AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1
  AWS_SDK_LOAD_CONFIG = 1
  AWS_STS_REGIONAL_ENDPOINTS = regional
No CDK environment variables

Description

The issue happens when we specify a custom identifier e.g. userId, and specify the allow.ownerDefinedIn('userId') for this custom idetifier like below:

a.schema({
  User: a
  .model({
    userId: a.id().required(),
    email: a.email().required(),
  })
  .identifier(['userId'])
  .authorization((allow) => [
    allow.ownerDefinedIn('userId'),
  ]),
})

It will have error below in the sandbox mode (have not tested the production mode):


Implicit field userId conflicts with the explicit field definition.
Caused By: Implicit field userId conflicts with the explicit field definition.

Resolution: Check your data schema definition for syntax and type errors.

I also noticed the issue does not happen whne change the userId type from id() to string().

chrisbonifacio commented 1 month ago

Thanks for raising this issue, @hangoocn We're aware of this issue and are looking to improve the error messaging to make the cause more clear.

For now, please type the owner field as string.

kita3222 commented 1 month ago

I faced a similar error. Currently, id is not available, so it is all replaced with string.

 User: a
    .model({
      id: a.id().required(),
      familyName: a.string().required(),
      givenName: a.string().required(),
      email: a.string().required(),    
      tenantId: a.id().required(), 
    })
    .authorization((allow) => [
      allow.groupsDefinedIn('tenantId'),
      allow.owner(),
    ]),
InvalidSchemaError: Implicit field tenantId conflicts with the explicit field definition.
Resolution: Check your data schema definition for syntax and type errors.
Caused By: Implicit field tenantId conflicts with the explicit field definition.
ykethan commented 1 month ago

Hey👋 thanks for raising this! I'm going to transfer this over to our API repository for better assistance 🙂

amuresia commented 2 weeks ago

I am getting the same error message even when I don't use the .identifier

Feedback: a
      .model({
        content: a.string().required(),
        readers: a.string().array(),
        authorId: a.id().required(),
        author: a.belongsTo("User", "authorId"),
      })
      .authorization((allow) => [
        allow.ownerDefinedIn("authorId").to(["create", "read", "update"]),
        allow.ownersDefinedIn("readers").to(["read"]),
      ]),

The error message is

Implicit field authorId conflicts with the explicit field definition.
Caused By: Implicit field authorId conflicts with the explicit field definition.

Resolution: Check your data schema definition for syntax and type errors.

If I change

allow.ownerDefinedIn("authorId").to(["create", "read", "update"]),

to

allow.owner().to(["create", "read", "update"]),

or

authorId: a.id().required(),

to

authorId: a.string().required(),

then the error goes away. However now I am duplicating some data as both the owner and authorId attributes will have the same value.

I couldn't find the best practice in the docs, should we use a.string().required() or should we duplicate the data in both owner and authorId. If I use authorId: a.string().required() I see that the value is UUID not UUID::UUID, is this recommended?

chrisbonifacio commented 2 weeks ago

@amuresia Thanks for testing some scenarios, the issue has more to do with the types around auth rules using.ownerDefinedIn() than the identifier().

Preferably, the owner value should be in the format <sub>:<username> but just the sub should still work. I believe the logic is backwards compatible with the way Gen 1 used to store the owner value. Although, I'm only speaking from memory - I haven't tried in a while so that would require some testing to be sure.

We are discussing this internally regarding the error messaging being made clearer, support for id as a type for owner fields, and the general use case around using a cognito sub as the primary key for records.

jewells07 commented 2 weeks ago

Same Issue:

Implicit field participants conflicts with the explicit field definition.
Caused By: Implicit field participants conflicts with the explicit field definition.

Resolution: Check your data schema definition for syntax and type errors.
Chat: a
    .model({
      participants: a.id().array().required(),
      messages: a.hasMany('Message', 'chatId'),
    })
    .authorization((allow) => [
      allow.authenticated().to(['create']),
      allow.ownersDefinedIn('participants').to(['read'])
    ]),