-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat(cli): import LB3 models with a custom base class #4737
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
|
@emonddr @deepakrkris @nabdelgadir @agnes512 please review |
8ff723b to
abc4c07
Compare
| expect(toJSON(modelData.settings)).to.deepEqual({ | ||
| // By default, LB3 models are not strict | ||
| strict: false, | ||
| replaceOnPUT: true, |
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.
FYI: replaceOnPUT is a flag set by juggler, it's used for backwards compatibility with LoopBack 2.x.
Quoting from https://loopback.io/doc/en/lb4/Model.html:
replaceOnPUT- This entry is no longer supported as LB4 controllers scaffolded by LB4 controller, PUT is always calling replaceById. Users are free to change the generated code to call patchById if needed.
| 'Adding %s (base of %s) to the list of imported models.', | ||
| chalk.yellow(baseName), | ||
| chalk.yellow(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.
Here is an example console output.
$ lb4 import-lb3-models ~/src/loopback/next/packages/cli/test/fixtures/import-lb3-models/app-using-model-inheritance.js
WARNING: This command is experimental and not feature-complete yet.
Learn more at https://loopback.io/doc/en/lb4/Importing-LB3-models.html
? Select models to import: Customer
Model Customer will be created in src/models/customer.model.ts
Adding UserBase (base of Customer) to the list of imported models.
Model UserBase will be created in src/models/user-base.model.ts
Adding User (base of UserBase) to the list of imported models.
Model User will be created in src/models/user.model.ts
Import of model relations is not supported yet. Skipping the following relations: accessTokens
Ignoring the following unsupported settings: acls
create src/models/customer.model.ts
create src/models/user-base.model.ts
create src/models/user.model.ts
update src/models/index.ts
update src/models/index.ts
update src/models/index.ts
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.
So just making sure I understand correctly, we're importing User because UserBase inherits from it? And do we always eventually want one model to extend from a built in model (e.g. in this case User inheriting from Entity)?
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 importing
UserbecauseUserBaseinherits from it?
Yes, exactly 👍
And do we always eventually want one model to extend from a built in model (e.g. in this case
Userinheriting fromEntity)?
Yes. Every LoopBack 3 model has the LB3 built-in Model at the top of the inheritance chain. Some models are inheriting from PersistedModel (which inherits from Model and is mapped to Entity in LB4), some models are inheriting from KeyValueModel (which inherits from Model and is probably mapped to KeyValueModel in LB4, I am not sure).
| validateUpsert: true, | ||
| idInjection: true | ||
| } | ||
| settings: {strict: false, forceId: false, validateUpsert: true, idInjection: true} |
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 following settings are no longer imported because they are inherited:
replaceOnPUT
Please note that forceId, idInjection and validateUpsert are explicitly configured in coffee-shop's model JSON file, therefore they are preserved in the generated output. You can compare this line with User settings below https://github.com/strongloop/loopback-next/pull/4737/files#diff-e9be610fe11bc2c954313d9283d0eda3R128-R137, where only strict + custom settings are generated.
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.
LGTM in general 👏 Just have a few questions.
| if (!templateData.isModelBaseBuiltin) { | ||
| const baseName = templateData.modelBaseClass; | ||
| if ( | ||
| !this.existingLb4ModelNames.includes(baseName) && |
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 want to make sure that I understand it correctly, the reason we check the existence of the base model is because we need to import the custom base class and also it's parent class if that doesn't exist?
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.
Yes.
Let's say the application has:
AuditedPersistedModelwhich is adding auditing functionality for all CRUD operations provided byPersistedModel,- several models like
Product,CategoryandStore, where all models are based onAuditedPersistedModel.
When we run import for the first time and choose only Store to import, we also need to import AuditedPersistedModel to allow the new LB4 model to inherit from the correct base class (model).
When we run import for the second time and choose to import Product and Category, we should not import AuditedPersistedModel again, because that could overwrite bits imported manually in the previous step. Instead, LB4 CLI will skip importing this base model.
| // Core LB3 models like PersistedModel come with an id property that's | ||
| // injected by juggler. We don't want to inherit that property, because | ||
| // in LB4, we want models to define the id property explicitly. | ||
| if (isCoreModel(modelCtor.base)) { |
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 a bit confused. Does this mean if a custom model inherits a base model, it won't have the id property after importing to LB4 app?
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.
When scaffolding code for an imported model, we want to skip all properties inherited from the base model. For example, if User model has email property and Customer model inherits from User, then the code for Customer SHOULD NOT re-define email property again. The property should be inherited instead, both at TypeScript and juggler-metadata level.
Now juggler's PersistedModel always comes with id property (in fact, the property is added to every model created by juggler, including the base Model class), see ModelBuilder.prototype.define().
As a result, when deciding which properties belong to the model being imported (e.g. Product) and which are inherited (e.g. from PersistedModel), the primary key property id was always skipped before I introduced this check, simply because it was considered as coming from the base model (PersistedModel).
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.
LGTM 👍
packages/cli/test/fixtures/import-lb3-models/app-using-model-inheritance.js
Outdated
Show resolved
Hide resolved
| const def = migratePropertyDefinition(properties[prop]); | ||
| const propDef = properties[prop]; | ||
|
|
||
| // Skip the property if it was inherited from the base model (the parent) |
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.
reasonable to me 👍
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.
Just have a clarification question, but LGTM
| 'Adding %s (base of %s) to the list of imported models.', | ||
| chalk.yellow(baseName), | ||
| chalk.yellow(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.
So just making sure I understand correctly, we're importing User because UserBase inherits from it? And do we always eventually want one model to extend from a built in model (e.g. in this case User inheriting from Entity)?
We are using LB3 only for tests. CLI commands dealing with LB3 apps are using `loopback` module installed inside the LB3 app. Signed-off-by: Miroslav Bajtoš <mbajtoss@gmail.com>
Signed-off-by: Miroslav Bajtoš <mbajtoss@gmail.com>
Signed-off-by: Miroslav Bajtoš <mbajtoss@gmail.com>
Signed-off-by: Miroslav Bajtoš <mbajtoss@gmail.com>
abc4c07 to
8c5f3cf
Compare
The initial implementation of
lb4 import-lb3-modelswas able to import only models inheriting from models that have a built-in counter-part in LB4:Model,PersistedModel,KeyValueModel.In this pull request, I am implementing import of models inheriting from non-LB4-builtins. There are two major use cases:
User.The changes are split into multiple smaller commits to make the patch easier to review.
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 👈