Skip to content

Commit

Permalink
feat: version 0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
bajtos committed Sep 9, 2019
1 parent cc5cd20 commit 477d523
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 46 deletions.
26 changes: 26 additions & 0 deletions docs/site/Importing-LB3-models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
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.

## Overview

A single model can be imported by running `lb4 import-model` command.

### Arguments

`modelFile`: Path to the JSON file with model definition, e.g.
`lb3app/common/models/my-model.json`.

### Options

`outDir`: Directory where to write the generated source file. Default:
`src/models`
4 changes: 4 additions & 0 deletions docs/site/sidebars/lb4_sidebar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,10 @@ children:
url: DataSource-generator.html
output: 'web, pdf'

- title: 'Import models from LoopBack 3'
url: Importing-LB3-models.html
output: 'web, pdf'

- title: 'Model generator'
url: Model-generator.html
output: 'web, pdf'
Expand Down
7 changes: 5 additions & 2 deletions docs/site/tables/lb4-artifact-commands.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
<td><a href="DataSource-generator.html">DataSource generator</a></td>
</tr>

<tr>
<td><code>lb4 import-model</code></td>
<td>Import a LoopBack 3 model to a LoopBack 4 application</td>
<td><a href="Importing-LB3-models.html">Model generator</a></td>
</tr>
<tr>
<td><code>lb4 model</code></td>
<td>Add a new model to a LoopBack 4 application</td>
Expand Down Expand Up @@ -70,7 +75,5 @@
<td>Generate interceptors</td>
<td><a href="Interceptor-generator.html">Global interceptor generator</a></td>
</tr>


</tbody>
</table>
169 changes: 169 additions & 0 deletions packages/cli/generators/import-model/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// 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-model-generator');
const utils = require('../../lib/utils');

module.exports = class ModelImporter extends BaseGenerator {
constructor(args, opts) {
super(args, opts);

this.argument('modelFile', {
type: String,
required: true,
description:
'Path to the model JSON file to import, ' +
'e.g. "lb3app/common/models/{model-name}.json"',
});

this.option('outDir', {
type: String,
description: 'Directory where to write the generated source file',
default: 'src/models',
});
}

async setOptions() {
this.modelFile = this.args[0];
this.artifactInfo.outDir = this.options.outDir;
this.artifactInfo.relPath = path.relative(
this.destinationPath(),
this.artifactInfo.outDir,
);
return super.setOptions();
}

async experimentalStatus() {
this.log();
this.log(
chalk.red(
'**WARNING: This command is experimental and not feature-complete yet.**',
),
);
this.log();
}

/**
* Ensure CLI is being run in a LoopBack 4 project.
*/
checkLoopBackProject() {
if (this.shouldExit()) return;
return super.checkLoopBackProject();
}

async loadModelFile() {
if (this.shouldExit()) return;
this.log(`Loading LoopBack 3.x model from ${chalk.yellow(this.modelFile)}`);
const fullModelPath = path.resolve(this.destinationPath(), this.modelFile);
this.modelDefinition = this.fs.readJSON(fullModelPath);
}

async migrateModel() {
if (this.shouldExit()) return;
this.artifactInfo.name = this.modelDefinition.name;
const result = utils.validateClassName(this.artifactInfo.name);
if (!result) {
return this.exit(
`Cannot import model: the name ${this.artifactInfo.name} is not valid. ${result}`,
);
}
utils.remindAboutNamingRules(this.artifactInfo.name, this.log.bind(this));

this.templateData = this.modelDefinition;

Object.entries(this.templateData.properties).forEach(([k, v]) =>
modelUtils.sanitizeProperty(v),
);

/* FIXME: The following base model classes should be recognized and mapped to LB4:
- Model -> Model
- PersistedModel -> Entity
- KeyValueModel -> (to be determined)
When the base model class is not recognized, the CLI should abort with a descriptive error.
*/
this.templateData.modelBaseClass = 'Entity';
this.templateData.isModelBaseBuiltin = true;

this.templateData.className = utils.pascalCase(this.templateData.name);

/* FIXME
The following model settings should trigger a warning when a non-empty value is provided in LB3 model file:
- relations
- methods
- mixins
- acls
All other model settings should be copied as-is to LB4 model definition.
IMPORTANT: settings can be specified either at top-level or inside options
property, we need to support both flavors.
Notable settings to test & support:
- forceId
- strict
- connector-specific config like SQL table name and MongoDB collection name
*/

// These last two are so that the template doesn't error out if they aren't there
// FIXME: parse LB3 "strict" setting
this.templateData.allowAdditionalProperties = true;
this.templateData.modelSettings = utils.stringifyModelSettings(
this.templateData.settings || {},
);
debug('LB4 model data', this.templateData);
}

/**
* Iterate through all the models we have discovered and scaffold
*/
async scaffold() {
if (this.shouldExit()) return;
utils.printClassFileName(
'model',
'models',
this.artifactInfo.name,
this.log.bind(this),
);

const fullTargetPath = path.resolve(
this.artifactInfo.relPath,
utils.getModelFileName(this.artifactInfo.name),
);
debug('Output file', fullTargetPath);

this.copyTemplatedFiles(
modelUtils.MODEL_TEMPLATE_PATH,
fullTargetPath,
this.templateData,
);
}

async end() {
if (this.shouldExit() || !this._wasGenerationSuccessful()) {
await super.end();
return;
}

await this._updateIndexFile(
this.artifactInfo.outDir,
utils.getModelFileName(this.artifactInfo.name),
);

this.log();
this.log(
'Model',
chalk.yellow(this.artifactInfo.name),
'was created in',
chalk.yellow(`${this.artifactInfo.relPath}/`),
);

await super.end();
}
};
48 changes: 4 additions & 44 deletions packages/cli/lib/artifact-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,7 @@ module.exports = class ArtifactGenerator extends BaseGenerator {
* remind user the input might get changed if it contains _ or accented char
**/
promptWarningMsgForName() {
if (this.artifactInfo.name.includes('_')) {
this.log(
chalk.red('>>> ') +
`Underscores _ in the class name will get removed: ${this.artifactInfo.name}`,
);
}
if (this.artifactInfo.name.match(/[\u00C0-\u024F\u1E00-\u1EFF]/)) {
this.log(
chalk.red('>>> ') +
`Accented chars in the class name will get replaced: ${this.artifactInfo.name}`,
);
}
utils.remindAboutNamingRules(this.artifactInfo.name, this.log.bind(this));
}

/**
Expand All @@ -90,14 +79,7 @@ module.exports = class ArtifactGenerator extends BaseGenerator {
* >> Model MyModel will be created in src/models/my-model.model.ts
**/
promptClassFileName(type, typePlural, name) {
this.log(
`${utils.toClassName(type)} ${chalk.yellow(
name,
)} will be created in src/${typePlural}/${chalk.yellow(
utils.toFileName(name) + '.' + `${type}.ts`,
)}`,
);
this.log();
utils.printClassFileName(type, typePlural, name, this.log.bind(log));
}

scaffold() {
Expand All @@ -121,16 +103,7 @@ module.exports = class ArtifactGenerator extends BaseGenerator {
return;
}

// Check all files being generated to ensure they succeeded
const generationStatus = !!Object.entries(
this.conflicter.generationStatus,
).find(([key, val]) => {
// If a file was modified, update the indexes and say stuff about it
return val !== 'skip' && val !== 'identical';
});
debug(`Generation status: ${generationStatus}`);

if (generationStatus) {
if (this._wasGenerationSuccessful()) {
await this._updateIndexFiles();

const classes = this.artifactInfo.name
Expand Down Expand Up @@ -189,20 +162,7 @@ module.exports = class ArtifactGenerator extends BaseGenerator {
}

for (const idx of this.artifactInfo.indexesToBeUpdated) {
await updateIndex(idx.dir, idx.file);
// Output for users
const updateDirRelPath = path.relative(
this.artifactInfo.relPath,
idx.dir,
);

const outPath = path.join(
this.artifactInfo.relPath,
updateDirRelPath,
'index.ts',
);

this.log(chalk.green(' update'), `${outPath}`);
await this._updateIndexFile(idx.dir, idx.file);
}
}
};
33 changes: 33 additions & 0 deletions packages/cli/lib/base-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const path = require('path');
const fs = require('fs');
const debug = require('./debug')('base-generator');
const semver = require('semver');
const updateIndex = require('./update-index');

/**
* Base Generator for LoopBack 4
Expand Down Expand Up @@ -444,4 +445,36 @@ module.exports = class BaseGenerator extends Generator {
}
await this._runLintFix();
}

// Check all files being generated to ensure they succeeded
_wasGenerationSuccessful() {
debug('conflicter status', this.conflicter.generationStatus);
debug('entries', Object.entries(this.conflicter.generationStatus));
const generationStatus = !!Object.entries(
this.conflicter.generationStatus,
).find(([key, val]) => {
// If a file was modified, update the indexes and say stuff about it
return val !== 'skip' && val !== 'identical';
});
debug(`Generation status: ${generationStatus}`);
return generationStatus;
}

async _updateIndexFile(dir, file) {
await updateIndex(dir, file);

// Output for users
const updateDirRelPath = path.relative(
this.artifactInfo.relPath,
this.artifactInfo.outDir,
);

const outPath = path.join(
this.artifactInfo.relPath,
updateDirRelPath,
'index.ts',
);

this.log(chalk.green(' update'), `${outPath}`);
}
};
4 changes: 4 additions & 0 deletions packages/cli/lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ function setupGenerators() {
path.join(__dirname, '../generators/datasource'),
PREFIX + 'datasource',
);
env.register(
path.join(__dirname, '../generators/import-model'),
PREFIX + 'import-model',
);
env.register(path.join(__dirname, '../generators/model'), PREFIX + 'model');
env.register(
path.join(__dirname, '../generators/repository'),
Expand Down
Loading

0 comments on commit 477d523

Please sign in to comment.