-
Notifications
You must be signed in to change notification settings - Fork 1.1k
fix(cli): discard changes if lb4 relation fails. Allow customized relation name for belongsTo
#4373
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
Conversation
479d9e0 to
d492728
Compare
relation failsrelation fails. Allow customized relation name for belongsTo
agnes512
left a comment
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.
Add a new prompt to ask the source key of belongsTo (e.g customerId). By comparing the source key name and the relation name, the generator would respect customized relation name, see issue #4209
| }, | ||
| ]; | ||
| // except the customized relation name, also need to check if the property name is the same as relation name | ||
| // or it will get 'navigational property' error when creating instances |
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.
related stackoverflow question.
| this.artifactInfo.defaultRelationName = this._getDefaultRelationName(); | ||
| // for hasMany && hasOne, the source key is the same as the relation name | ||
| const msg = | ||
| this.artifactInfo.relationType === 'belongsTo' |
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.
This property name will be the source key and the relation name for hasMany and hasOne. So I separate it from belongsTo here.
bajtos
left a comment
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 am afraid I don't have bandwidth to review the changes in details, I'll leave the review to somebody else who is more familiar with this part of our code base.
packages/cli/snapshots/integration/generators/relation.belongs-to.integration.snapshots.js
Show resolved
Hide resolved
| > { | ||
| public readonly customerClass: BelongsToAccessor<CustomerClass, typeof OrderClass.prototype.orderNumber>; | ||
| public readonly customerClassCustNumber: BelongsToAccessor<CustomerClass, typeof OrderClass.prototype.orderNumber>; |
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.
hmm what does Cust mean 🤔 ?
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.
CustNumber is the name of the id property of customerClass 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 change the generator to generate customerClassId instead to match with our code in /repository/relation
jannyHou
left a comment
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.
Great effort on improving the complicated prompts in relation cli 👍 👍
I left some minor comments and questions.
packages/cli/test/integration/generators/relation.has-many.integration.js
Outdated
Show resolved
Hide resolved
| ); | ||
| // checks if the relation name already exists in repo | ||
| if ( | ||
| relationUtils.doesPropertyExist( |
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.
Just curious: I think the relation definition is in the model file, why do we have to use more steps to get the corresponding repository file and check the existence?
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.
Good question! From my understanding the model has the property (e.g orders or customerId). And the repository has the relation name (e.g orders or customer). They might have the same name but most of time they don't. For example, for belongsTo relation, the relation name is customer.
The generator throws errors if the repository already has the relation name defined. And the generator didn't have such checks before .
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.
And the repository has the relation name
Oh that's smart! 👍 makes perfect sense then
| `this.${relationName} = ` + | ||
| `this.createBelongsToAccessorFor('` + | ||
| `${this.artifactInfo.relationName.replace(/Id$/, '')}',` + | ||
| `${relationName}',` + |
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.
Here Id is no longer going to be replaced with empty string. Will this cause a regression?
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 have relationName defined above so we don't need it here anymore.
|
|
||
| _getRepositoryRelationPropertyName() { | ||
| return utils.pluralize(utils.camelCase(this.artifactInfo.dstModelClass)); | ||
| return this.artifactInfo.relationName; |
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're removing calls to the util.camelCase in several places. Will this cause a problem?
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 have relationName defined above so we can just use the value here.
packages/cli/test/integration/generators/relation.has-many.integration.js
Outdated
Show resolved
Hide resolved
packages/cli/test/integration/generators/relation.has-many.integration.js
Outdated
Show resolved
Hide resolved
|
The relation-based test cases pass (i ran them on my laptop), so that's great. Doesn't appear to be any regressions. |
If you check the tests It is caused by the generator every time it does |
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.
Just curious; do we also need to update the docs that reference the CLI prompt procedure?
See:
Yes, I will add them once the proposal looks good to most of you 😄 |
| } else { | ||
| relationUtils.addProperty(classDeclaration, property); | ||
| } | ||
| // already chcked the existence of property before |
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.
(sorry I am not very familiar with the relation cli which results in many questions ❔ ❔ ) Hmm, where is it checked? did a quick search of code before it but didn't find any clue 🙈
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.
Here the property is the relation name, which has been checked in index file. Sorry the PR is big, I should've separated them.
| throw new Error( | ||
| 'Parameter ' + parameterName + ' already exist in the constructor.', | ||
| ); | ||
| // no need to check if the getter already exists |
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.
Doesn't parameter exist imply the relation already exists? And therefore an error should be thrown like the deleted code?
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.
The param is getter, such as @repository.getter('OrderRepository') protected orderRepositoryGetter: Getter<OrderRepository>. And I don't think this implies a certain relation name already exists. That's why I removed it.
And I was also thinking about some edge cases. For example, since we allow customized keyFrom, user could have two same relations with 2 models with two different names. In this case, they will share the same getter.
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.
since we allow customized keyFrom, user could have two same relations with 2 models with two different names. In this case, they will share the same getter.
👍 Thank you I agree with you.
The property should be checked instead of parameter.
| relationDecorator = [ | ||
| { | ||
| name: 'belongsTo', | ||
| arguments: [`() => ${className}, {name: '${relationName}'}`], |
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 am a bit confused about defaultRelationName and sourceKeyName, wouldn't they be the same?
I find it hard to think of a user scenario that defaultRelationName, sourceKeyName and relationName are all different...
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.
Good point. I will list out the differences:
Before, the generator asked foreign key, relationName for hasMany.
and it only asked relationName for belongTo if defaultRelationName doesn't exist. It used relationName as the foreign key name (e.g customerId). Then it use the target model as the real relation name (e.g `customer).
This is why how it dealt with belongsTo is problematic:
- if the default foreign key already exists, it didn't prompt
- if the default foreign key doesn't exists, it takes whatever it is, but the real relation name is still the target model, and this is different from how the
belongsToAccessorgenerates the default name. For example: if you type incustomer_idas your foreign key, the generator would havecustomeras the default relation name. But for thebelongsToAccessor, the default relation name iscustomer_idinstead ofcustomer. That's why users are gettingCannot find type of undefinederrors.
In order to allow custom fk and relation name for belongsTo, I added a new field sourceKeyName to represent the foreign key of belongsTo. And relationName to represent the real relation name.
The line defaultRelationName !== relationName here is to check if the relationName is customized. If so, it should be specified in the name field, just like how we documented in our docs Relation Metadata.
Therefore, it can have correct property:
@belongsTo(() => Customer, {name: 'my_customer'}) // customized relation name
customer_id: number; // customized fk
6d8e6a5 to
3a8932a
Compare
5d33f0d to
6c552fb
Compare
5fe3a6f to
fb85d50
Compare
nabdelgadir
left a comment
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.
Great job 👍
fb85d50 to
ad21b69
Compare
ad21b69 to
c6df843
Compare
c6df843 to
6f0f474
Compare
Fixes #3287 and #4209
1st commit: code changes
2nd commit: tests
Before
Example:
hasMany
? SourceModel:
Customer? DestModel:
Order,? Foreign key to define on the target model:
customerId? Source property name for the relation getter: `orders
? Allow Order queries to include data from related Customer instances? Yes
==> issues:
/repository/relationbelongTo
? SourceModel:
Order? DestModel:
Customer,? Source property name for the relation getter:
customerId? Allow ..inclusion? Yes
==> issues:
/repository/relationtargetModelName(e.gcustomer)New changes
Examples of new prompt:
? Please select the relation type
hasMany? Please select source model
Customer? Please select target model
Order? Foreign key name to define on the target model
c_id// not default value? Source property name for the relation getter (will be the relation name)
orders? Allow Customer queries to include data from related Review instances? Yes
=>
? Please select the relation type
belongsTo? Please select source model
Order? Please select target model
Customer? Foreign key name to define on the source model
c_id? Relation name my_customer // not default name
? Allow Customer queries to include data from related Review instances? Yes
=>
For belongsTo, if the fk name is the same as the relation name, the prompt would ask the user to re-enter a new name to avoid a known issue: Relations with custom names not working
Checklist
👉 Read and sign the CLA (Contributor License Agreement) 👈
npm testpasses on your machinepackages/cliwere updatedexamples/*were updated👉 Check out how to submit a PR 👈