-
Notifications
You must be signed in to change notification settings - Fork 202
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: add targetName to hasOne relationships #926
Conversation
Codecov Report
@@ Coverage Diff @@
## main #926 +/- ##
==========================================
- Coverage 67.29% 67.05% -0.24%
==========================================
Files 895 917 +22
Lines 35389 35532 +143
==========================================
+ Hits 23814 23826 +12
- Misses 11575 11706 +131
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
.../AWSAPICategoryPluginFunctionalTests/GraphQLModelBased/GraphQLConnectionScenario1Tests.swift
Show resolved
Hide resolved
AmplifyPlugins/Core/AWSPluginsCore/Model/Support/Model+GraphQL.swift
Outdated
Show resolved
Hide resolved
// When the associated object is found, take this value over the explicit field's value by replacing the correct | ||
// entry for the field name of the associated model. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not necessarily disagreeing, but why is it right to take the object rather than the specified field? How do we let the customer know which one will be used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If they do any of the below, the correct team identifier will be used
let project = Project2(teamID: team.id, team: team)
let project = Project2(teamID: team.id)
// note `teamID` is always required
So the decision to take the object over the specified field only matters when the developer sets their values differently, ie.
let project = Project2(teamID: "nonExistentTeamID", team: team)
If the developer started off with
let project = Project2(teamID: "nonExistentTeamID")
This will be persisted but the association points to an non existent team. They'll eventually end up with one of the three scenarios once they get working code, by persisting/retrieving the team object before using it in the project object
let project = Project2(teamID: team.id) // 1. they updated their code to use team.id, and it works without passing in an optional `team`
let project = Project2(teamID: team.id, team: team) // 2. they updated it in both places, because it's confusing where to add it to
let project = Project2(teamID: "nonExistentTeamID", team: team) // 3. they only passed it into `team` parameter, but it starts working
Another thought I had for this was thinking that we will omit the specific field in the future with a breaking codegen change, so that our documentation always promotes the use of passing the object (since belongsTo associations already omit the specific field). So where it's not omitted, we would continue to document it as let project = Project2(teamID: team.id, team: team)
to communicate that developers should work with the model, while satisfying requirement that teamID
is a required field
|
||
return nil | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this refactor!
AmplifyPlugins/Core/AWSPluginsCore/Model/Support/Model+GraphQL.swift
Outdated
Show resolved
Hide resolved
a988f65
to
f8b1830
Compare
wait(for: [queriedCommentCompleted], timeout: networkTimeout) | ||
} | ||
|
||
// TODO: Issue https://github.com/aws-amplify/amplify-ios/issues/939 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Capturing the issue separately: #939
Amplify/Categories/DataStore/Model/Internal/Schema/ModelField+Association.swift
Outdated
Show resolved
Hide resolved
/// This information is also stored in the schema as `targetName` which is codegenerated to be the same as the | ||
/// default or an explicit field specified by the developer. | ||
private func getFieldNameForAssociatedModels(modelField: ModelField) -> String { | ||
let defaultFieldName = modelName.camelCased() + modelField.name + "Id" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Id" was not added before this change.. is this a bug fix ? Earlier it was modelName.camelCased() + field.graphQLName
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
graphQLName
internally resolves to name.pascalCased() + "Id"
for associations. oh shoot. this is incorrect then it should be:
modelName.camelCased() + modelField.name.pascalCased() + "Id"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated to correct association field, we always pascal case the modeField.name
for the associated field
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool! Wondering whether we have coverage on this logic. It would nice if you can add this or add a backlog to add this logic to unit test.
guard let fieldValue = fieldOptionalValue else { | ||
input.updateValue(nil, forKey: name) | ||
guard let fieldValue = fieldValueOptional else { | ||
input.updateValue(nil, forKey: modelField.name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We used field.graphQLName
before? Just confirming both works?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
graphQLName returns name by default, and only in the case it's a belongsTo it will return name.pascalCase()+"Id"
. this value name.pascalCase() + "Id"
is not valid until we've prepended the current model name, and is done in the case model
scenario. which is why i've refactored this to just modelField.name
and consolidated the logic down below
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if it is belongsTo and returns targetName because targetName exists, then it may set targetName to nil here. I don't think this is ever the case because you cannot have nil belongsTo assocations, but i will revert this change and only make it if we're sure that there isn't a scenario that i've missed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reverted this change
return primaryKeyValue | ||
} | ||
|
||
preconditionFailure("Could not retrieve the identifier from associated model: \(value)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hope this wont break? Previous code returned nil here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i wrote this on the assumption that it should always get an id from the value, but i'm more inclined to return nil here to introduce less changes to the code
@@ -64,13 +64,13 @@ extension GraphQLRequest: ModelSyncGraphQLRequestFactory { | |||
decodePath: document.name) | |||
} | |||
|
|||
public static func createMutation(of model: Model, version: Int?) -> GraphQLRequest<MutationSyncResult> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need these changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed in the integration tests over in AWSAPIPlugin that tests these paths, for some reason were not compiling. until i added the default parameters here. Not sure how those were written without the nil default parameter on these methods
Issue #, if available:
#920
Description of changes:
The fix in this PR is enabled by the CLI codegen changes which adds the
targetName
to hasOne relationships so that this becomes an additive, non-breaking change for developers.The fix targets making the following schema usable by API, and thus DataStore when sync is enabled:
See #920 for more details
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.