amplify-codegen
amplify-codegen copied to clipboard
Cannot have 2 different hasOne relations to the same type with belongsTo
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.
How did you install the Amplify CLI?
npm
If applicable, what version of Node.js are you using?
v16.13.1
Amplify CLI Version
7.6.13
What operating system are you using?
Mac
Amplify Codegen Command
codegen models
Describe the bug
Two different fields with a hasOne
relation to the same type causes the following error if it has a reverse-link (belongsTo
) field:
A 'belongsTo' field should match to a corresponding 'hasMany' or 'hasOne' field
For example, consider a user that can have home & shipping addresses (may or may not be the same):
User.homeAddr: Addr @hasOne
User.shipAddr: Addr @hasOne
Addr.user: User @belongsTo
The same error occurs if a separate field is used for each reverse link:
Addr.homeUser: User @belongsTo
Addr.shipUser: User @belongsTo
The error does not occur if the belongsTo
annotation is removed.
It's only a problem for amplify codegen models
, not amplify api gql-compile
or the generated Dynamo tables.
Expected behavior
It should be possible to model something like a user with two addresses without resorting to a one-to-many relationship. We are not modeling a list of addresses, only two specific addresses. With a list of addresses, we would have to give each a type. Then we would have to verify only one of each type occurs in the list. Also, to access an address of a specific type we would have to search the list.
Reproduction steps
In a fresh directory do the following, accepting the defaults:
amplify init
amplify add api
Replace schema.graphql
with the one in the next section, then continue as follows:
amplify api gql-compile
amplify codegen models
Notice gcl-compile
succeeds, but codegen
returns the error mentioned in the description.
GraphQL schema(s)
type User @model {
id: ID! @primaryKey
homeAddr: Addr @hasOne
shipAddr: Addr @hasOne
}
type Addr @model {
id: ID! @primaryKey
user: User @belongsTo
# workaround (doesn't work)
# homeUser: User @belongsTo
# shipUser: User @belongsTo
}
Log output
# Put your logs below this line
Additional information
Stack trace from codegen models
A 'belongsTo' field should match to a corresponding 'hasMany' or 'hasOne' field
Error: A 'belongsTo' field should match to a corresponding 'hasMany' or 'hasOne' field
at Object.processBelongsToConnection (/Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/node_modules/@aws-amplify/appsync-modelgen-plugin/src/utils/process-belongs-to.ts:40:11)
at Object.processConnectionsV2 (/Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/node_modules/@aws-amplify/appsync-modelgen-plugin/src/utils/process-connections-v2.ts:136:16)
at /Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/node_modules/@aws-amplify/appsync-modelgen-plugin/src/visitors/appsync-visitor.ts:691:32
at Array.forEach (<anonymous>)
at /Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/node_modules/@aws-amplify/appsync-modelgen-plugin/src/visitors/appsync-visitor.ts:690:20
at Array.forEach (<anonymous>)
at AppSyncModelJavascriptVisitor.processConnectionDirectivesV2 (/Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/node_modules/@aws-amplify/appsync-modelgen-plugin/src/visitors/appsync-visitor.ts:689:34)
at AppSyncModelJavascriptVisitor.processDirectives (/Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/node_modules/@aws-amplify/appsync-modelgen-plugin/src/visitors/appsync-visitor.ts:303:12)
at AppSyncModelJavascriptVisitor.generate (/Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/node_modules/@aws-amplify/appsync-modelgen-plugin/src/visitors/appsync-javascript-visitor.ts:50:10)
at Object.exports.plugin (/Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/node_modules/@aws-amplify/appsync-modelgen-plugin/src/plugin.ts:54:20)
at Object.executePlugin (/Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/node_modules/@graphql-codegen/core/src/execute-plugin.ts:54:12)
at /Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/node_modules/@graphql-codegen/core/src/codegen.ts:74:28
at Array.map (<anonymous>)
at Object.codegen (/Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/node_modules/@graphql-codegen/core/src/codegen.ts:61:21)
at /Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/node_modules/amplify-codegen/src/commands/models.js:131:23
at Array.map (<anonymous>)
at Object.generateModels (/Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/node_modules/amplify-codegen/src/commands/models.js:130:46)
at Object.run (/Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/node_modules/amplify-codegen/commands/codegen/models.js:9:7)
at Object.executeAmplifyCommand (/Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/node_modules/amplify-codegen/src/amplify-plugin-index.js:9:3)
at executePluginModuleCommand (/Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/src/execution-manager.ts:178:3)
at executeCommand (/Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/src/execution-manager.ts:30:5)
at Object.run (/Users/clint/.nvm/versions/node/v16.13.1/lib/node_modules/@aws-amplify/cli/src/index.ts:205:5)
Hi @clintfoster. It looks like such access pattern might not be supported by Datastore. One alternative is to model Home and Ship Addresses separately like below:
type User @model {
id: ID! @primaryKey
homeAddr: HomeAddr @hasOne
shipAddr: ShipAddr @hasOne
}
type HomeAddr @model {
id: ID! @primaryKey
user: User @belongsTo
}
type ShipAddr @model {
id: ID! @primaryKey
user: User @belongsTo
}
Thanks for the response, @phani-srikar. The workaround you described seems reasonable, but it presents a few problems. We actually have three types of addresses:
- home
- billing
- shipping
When all three are the same (usually the case), they should point to the same Addr
. Otherwise the user must make changes in three places.
Also, we have indexes allowing administrators to search by address and retrieve the User
via belongsTo
. If there are three tables they will need to search all three (or know which addresses were specified).
I guess it goes without saying that a model that doesn't match the problem introduces complications. The real-world situation is a User that may have one, two or three addresses (which may be different or the same). Just as a list is a poor model for this problem, so are three separate address types with duplicate fields.
Notice the generated GraphQL input types are straightforward. It is easy to understand how the API works.
export type CreateAddrInput = {
addrUserId?: string | null
};
export type CreateUserInput = {
userHomeAddrId?: string | null,
userShipAddrId?: string | null,
userMailAddrId?: string | null
};
The representation in Dynamo is also straightforward.
Is it possible to request this as a feature?
We are currently investigating the feasibility of the change.