-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cli): add new command
import-lb3-model
(EXPERIMENTAL)
- Loading branch information
Showing
16 changed files
with
2,355 additions
and
124 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
--- | ||
lang: en | ||
title: 'Importing models from LoopBack 3 projects' | ||
keywords: LoopBack 4.0, Migration | ||
sidebar: lb4_sidebar | ||
permalink: /doc/en/lb4/Importing-LB3-models.html | ||
--- | ||
|
||
## Synopsis | ||
|
||
To simplify migration from LoopBack 3, LoopBack 4 provides a CLI tool to import | ||
LoopBack 3 models into your LoopBack 4 project. | ||
|
||
{% include warning.html content=" | ||
This command is experimental and not feature-complete yet. | ||
See the list of known limitations below. | ||
" %} | ||
|
||
## Overview | ||
|
||
Import one or more models from your LB 3.x application by running | ||
`lb4 import-lb3-models` command. | ||
|
||
### Arguments | ||
|
||
`lb3app`: Path to the directory containing your LoopBack 3.x application. | ||
|
||
{% include important.html content=" | ||
The generator loads the application via `require()`, it does not | ||
support applications that are unable to boot (throw errors at startup). | ||
" %} | ||
|
||
### Options | ||
|
||
`outDir`: Directory where to write the generated source file. Default: | ||
`src/models` | ||
|
||
## Known limitations | ||
|
||
{% include note.html content=" | ||
Please up-vote the tracking GitHub issue for scenarios that are important to | ||
your project. It will help us to better prioritize which limitations to remove | ||
first. | ||
" %} | ||
|
||
### Connector-specific metadata in property definitions is not imported | ||
|
||
_The tracking GitHub issue: | ||
[loopback-next#3810](https://github.com/strongloop/loopback-next/issues/3810)_ | ||
|
||
Workaround: Add this metadata manually to the generated file. | ||
|
||
### Nested properties are not upgraded | ||
|
||
_The tracking GitHub issue: | ||
[loopback-next#3811](https://github.com/strongloop/loopback-next/issues/3811)_ | ||
|
||
When a property is defined with a complex object type, the nested property | ||
definitions are not converted from LB3 to LB4 format. | ||
|
||
Workaround: Fix the generated definition manually. | ||
|
||
### Model relations are not imported | ||
|
||
_The tracking GitHub issue: | ||
[loopback-next#3812](https://github.com/strongloop/loopback-next/issues/3812)_ | ||
|
||
Workaround: define relational metadata & navigational properties manually. | ||
|
||
### Models inheriting from custom base class | ||
|
||
_The tracking GitHub issue: | ||
[loopback-next#3813](https://github.com/strongloop/loopback-next/issues/3813)_ | ||
|
||
Models inheriting from application-specific models (including LB3 built-in | ||
models like `User`) cannot be imported yet. | ||
|
||
Workaround: | ||
|
||
1. Modify your LB3 model to inherit from `Model`, `PersistedModel` or | ||
`KeyValueModel`. | ||
|
||
2. Import the model to LB4 | ||
|
||
3. Update the imported model to inherit for the desired application-specific | ||
model. | ||
|
||
### MongoDB's `ObjectID` type | ||
|
||
The tracking GitHub issue: | ||
[loopback-next#3814](https://github.com/strongloop/loopback-next/issues/3814). | ||
|
||
For models attached to MongoDB datasource, the imported LB4 model contains | ||
incorrect definition of the primary key property of `ObjectID` type. | ||
|
||
As a workaround, you can change the property definition from: | ||
|
||
```ts | ||
@property({ | ||
type: ObjectID; | ||
}) | ||
id: ObjectID; | ||
``` | ||
|
||
to: | ||
|
||
```ts | ||
@property({ | ||
type: 'string', | ||
mongodb: {dataType: 'ObjectID'} | ||
}) | ||
id: string; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
// Copyright IBM Corp. 2019. All Rights Reserved. | ||
// Node module: @loopback/cli | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
'use strict'; | ||
|
||
const chalk = require('chalk'); | ||
const path = require('path'); | ||
const BaseGenerator = require('../../lib/base-generator'); | ||
const modelUtils = require('../../lib/model-discoverer'); | ||
const debug = require('../../lib/debug')('import-lb3-models'); | ||
const utils = require('../../lib/utils'); | ||
const {loadLb3App} = require('./lb3app-loader'); | ||
const {importLb3ModelDefinition} = require('./migrate-model'); | ||
const {canImportModelName} = require('./model-names'); | ||
|
||
module.exports = class Lb3ModelImporter extends BaseGenerator { | ||
constructor(args, opts) { | ||
super(args, opts); | ||
|
||
this.argument('lb3app', { | ||
type: String, | ||
required: true, | ||
description: | ||
'Path to your LoopBack 3.x application. ' + | ||
'This can be a project directory (e.g. "my-lb3-app") or ' + | ||
'the server file (e.g. "my-lb3-app/server/server.js").', | ||
}); | ||
|
||
this.option('outDir', { | ||
type: String, | ||
description: 'Directory where to write the generated source file', | ||
default: 'src/models', | ||
}); | ||
} | ||
|
||
async processOptions() { | ||
this.sourceAppDir = this.args[0]; | ||
this.artifactInfo.outDir = this.options.outDir; | ||
this.artifactInfo.relPath = path.relative( | ||
this.destinationPath(), | ||
this.artifactInfo.outDir, | ||
); | ||
return super.setOptions(); | ||
} | ||
|
||
async logExperimentalStatus() { | ||
this.log( | ||
chalk.red(` | ||
WARNING: This command is experimental and not feature-complete yet. | ||
Learn more at https://loopback.io/doc/en/lb4/Importing-LB3-models.html | ||
`), | ||
); | ||
} | ||
|
||
/** | ||
* Ensure CLI is being run in a LoopBack 4 project. | ||
*/ | ||
checkLoopBackProject() { | ||
if (this.shouldExit()) return; | ||
return super.checkLoopBackProject(); | ||
} | ||
|
||
async loadTheApp() { | ||
this.lb3app = await loadLb3App(this.sourceAppDir); | ||
this.modelRegistry = this.lb3app.registry.modelBuilder.models; | ||
} | ||
|
||
async promptModels() { | ||
const modelNames = Object.keys(this.modelRegistry).filter( | ||
canImportModelName, | ||
); | ||
|
||
debug('Available LB3 models', modelNames); | ||
|
||
const prompts = [ | ||
{ | ||
name: 'modelNames', | ||
message: 'Select models to import:', | ||
type: 'checkbox', | ||
choices: modelNames, | ||
validate: result => !!result.length, | ||
// TODO: add a CLI flag to supply these names programmatically | ||
}, | ||
]; | ||
|
||
const answers = await this.prompt(prompts); | ||
debug('Models chosen:', answers.modelNames); | ||
this.modelNames = answers.modelNames; | ||
} | ||
|
||
async migrateSelectedModels() { | ||
if (this.shouldExit()) return; | ||
this.modelFiles = []; | ||
|
||
try { | ||
for (const name of this.modelNames) { | ||
await this._migrateSingleModel(name); | ||
} | ||
} catch (err) { | ||
if (err.exit) { | ||
this.exit(err.message); | ||
} else { | ||
throw err; | ||
} | ||
} | ||
} | ||
|
||
async _migrateSingleModel(name) { | ||
utils.logClassCreation('model', 'models', name, this.log.bind(this)); | ||
const modelCtor = this.modelRegistry[name]; | ||
if (typeof modelCtor !== 'function') { | ||
const availableModels = Object.keys(this.modelRegistry) | ||
.filter(canImportModelName) | ||
.join(', '); | ||
|
||
this.exit( | ||
`Unknown model name ${name}. Available models: ${availableModels}.`, | ||
); | ||
return; | ||
} | ||
|
||
const templateData = importLb3ModelDefinition( | ||
modelCtor, | ||
this.log.bind(this), | ||
); | ||
debug('LB4 model data', templateData); | ||
|
||
const fileName = utils.getModelFileName(name); | ||
const fullTargetPath = path.resolve(this.artifactInfo.relPath, fileName); | ||
debug('Model %s output file', name, fullTargetPath); | ||
|
||
this.copyTemplatedFiles( | ||
modelUtils.MODEL_TEMPLATE_PATH, | ||
fullTargetPath, | ||
templateData, | ||
); | ||
|
||
this.modelFiles.push(fileName); | ||
} | ||
|
||
/** | ||
* Iterate through all the models we have discovered and scaffold | ||
*/ | ||
async scaffold() { | ||
if (this.shouldExit()) return; | ||
} | ||
|
||
async end() { | ||
if (this.shouldExit() || !this._isGenerationSuccessful()) { | ||
await super.end(); | ||
return; | ||
} | ||
|
||
for (const f of this.modelFiles) { | ||
await this._updateIndexFile(this.artifactInfo.outDir, f); | ||
} | ||
|
||
await super.end(); | ||
} | ||
}; |
31 changes: 31 additions & 0 deletions
31
packages/cli/generators/import-lb3-models/lb3app-loader.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// Copyright IBM Corp. 2019. All Rights Reserved. | ||
// Node module: @loopback/cli | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
'use strict'; | ||
|
||
const debug = require('../../lib/debug')('import-lb3-models'); | ||
const path = require('path'); | ||
const pEvent = require('p-event'); | ||
|
||
module.exports = { | ||
loadLb3App, | ||
}; | ||
|
||
// TODO: do we want to share this code with `Lb3AppBooter.loadAndBootTheApp`? | ||
async function loadLb3App(dir) { | ||
debug('Loading LB3 app from', dir); | ||
const lb3App = require(path.resolve(dir)); | ||
|
||
debug( | ||
'If your LB3 app does not boot correctly then make sure it is using loopback-boot version 3.2.1 or higher.', | ||
); | ||
|
||
if (lb3App.booting) { | ||
debug(' waiting for boot process to finish'); | ||
// Wait until the legacy LB3 app boots | ||
await pEvent(lb3App, 'booted'); | ||
} | ||
return lb3App; | ||
} |
Oops, something went wrong.