@loopback/cli 4.0.0-alpha.8 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.yo-rc.json +1719 -0
- package/{generators/project/templates/LICENSE → LICENSE} +2 -1
- package/README.md +44 -43
- package/bin/cli-main.js +61 -0
- package/generators/app/index.js +109 -15
- package/generators/app/templates/.dockerignore +5 -0
- package/generators/app/templates/Dockerfile +28 -0
- package/generators/app/templates/README.md.ejs +130 -0
- package/generators/app/templates/public/index.html.ejs +88 -0
- package/generators/app/templates/{test → src/__tests__}/README.md +0 -1
- package/generators/app/templates/src/__tests__/acceptance/home-page.acceptance.ts.ejs +31 -0
- package/generators/app/templates/src/__tests__/acceptance/ping.controller.acceptance.ts.ejs +21 -0
- package/generators/app/templates/src/__tests__/acceptance/test-helper.ts.ejs +32 -0
- package/generators/app/templates/src/application.ts.ejs +70 -0
- package/generators/app/templates/src/controllers/README.md +6 -0
- package/generators/app/templates/src/controllers/index.ts.ejs +1 -0
- package/generators/app/templates/src/controllers/ping.controller.ts.ejs +55 -0
- package/generators/app/templates/src/datasources/README.md +3 -0
- package/generators/app/templates/src/index.ts.ejs +39 -0
- package/generators/app/templates/src/migrate.ts.ejs +20 -0
- package/generators/app/templates/src/models/README.md +3 -0
- package/generators/app/templates/src/openapi-spec.ts.ejs +23 -0
- package/generators/app/templates/src/sequence.ts.ejs +3 -0
- package/generators/controller/index.js +240 -23
- package/generators/controller/templates/src/controllers/controller-rest-template.ts.ejs +150 -0
- package/generators/controller/templates/src/controllers/{controller-template.ts → controller-template.ts.ejs} +2 -2
- package/generators/copyright/fs.js +46 -0
- package/generators/copyright/git.js +78 -0
- package/generators/copyright/header.js +306 -0
- package/generators/copyright/index.js +230 -0
- package/generators/copyright/license.js +105 -0
- package/generators/datasource/index.js +341 -0
- package/generators/datasource/templates/datasource.ts.ejs +22 -0
- package/generators/discover/import-discovered-model.js +70 -0
- package/generators/discover/index.js +411 -0
- package/generators/example/downloader.js +16 -0
- package/generators/example/index.js +176 -0
- package/generators/extension/index.js +34 -5
- package/generators/extension/templates/README.md.ejs +32 -0
- package/generators/extension/templates/{test → src/__tests__}/acceptance/README.md +0 -0
- package/generators/extension/templates/{test → src/__tests__}/integration/README.md +0 -0
- package/generators/extension/templates/{test → src/__tests__}/unit/README.md +0 -0
- package/generators/extension/templates/src/component.ts.ejs +22 -0
- package/generators/extension/templates/src/controllers/README.md +3 -2
- package/generators/extension/templates/src/decorators/README.md +10 -4
- package/generators/extension/templates/src/index.ts.ejs +3 -0
- package/generators/extension/templates/src/keys.ts.ejs +11 -0
- package/generators/extension/templates/src/mixins/README.md +77 -21
- package/generators/extension/templates/src/providers/README.md +51 -25
- package/generators/extension/templates/src/repositories/README.md +1 -1
- package/generators/extension/templates/src/types.ts.ejs +15 -0
- package/generators/import-lb3-models/index.js +197 -0
- package/generators/import-lb3-models/lb3app-loader.js +31 -0
- package/generators/import-lb3-models/migrate-model.js +249 -0
- package/generators/import-lb3-models/model-names.js +32 -0
- package/generators/interceptor/index.js +178 -0
- package/generators/interceptor/templates/interceptor-template.ts.ejs +62 -0
- package/generators/model/index.js +536 -0
- package/generators/model/property-definition.js +85 -0
- package/generators/model/templates/model.ts.ejs +49 -0
- package/generators/observer/index.js +132 -0
- package/generators/observer/templates/observer-template.ts.ejs +40 -0
- package/generators/openapi/README.md +211 -0
- package/generators/openapi/index.js +535 -0
- package/generators/openapi/schema-helper.js +447 -0
- package/generators/openapi/spec-helper.js +484 -0
- package/generators/openapi/spec-loader.js +75 -0
- package/generators/openapi/templates/src/controllers/controller-template.ts.ejs +43 -0
- package/generators/openapi/templates/src/datasources/datasource.ts.ejs +42 -0
- package/generators/openapi/templates/src/models/model-template.ts.ejs +71 -0
- package/generators/openapi/templates/src/models/type-template.ts.ejs +13 -0
- package/generators/openapi/templates/src/services/service-proxy-template.ts.ejs +55 -0
- package/generators/openapi/utils.js +322 -0
- package/generators/project/templates/.eslintignore +4 -0
- package/generators/project/templates/.eslintrc.js.ejs +3 -0
- package/generators/project/templates/.mocharc.json +5 -0
- package/generators/project/templates/.prettierignore +0 -2
- package/generators/project/templates/.prettierrc +2 -1
- package/generators/project/templates/.vscode/launch.json +38 -0
- package/generators/project/templates/.vscode/settings.json +32 -0
- package/generators/project/templates/.vscode/tasks.json +29 -0
- package/generators/project/templates/DEVELOPING.md +36 -0
- package/generators/project/templates/_.gitignore +3 -5
- package/generators/project/templates/package.json.ejs +175 -0
- package/generators/project/templates/package.plain.json.ejs +176 -0
- package/generators/project/templates/tsconfig.json.ejs +39 -0
- package/generators/relation/base-relation.generator.js +220 -0
- package/generators/relation/belongs-to-relation.generator.js +196 -0
- package/generators/relation/has-many-relation.generator.js +200 -0
- package/generators/relation/has-many-through-relation.generator.js +331 -0
- package/generators/relation/has-one-relation.generator.js +200 -0
- package/generators/relation/index.js +795 -0
- package/generators/relation/references-many-relation.generator.js +142 -0
- package/generators/relation/templates/controller-relation-template-belongs-to.ts.ejs +38 -0
- package/generators/relation/templates/controller-relation-template-has-many-through.ts.ejs +110 -0
- package/generators/relation/templates/controller-relation-template-has-many.ts.ejs +110 -0
- package/generators/relation/templates/controller-relation-template-has-one.ts.ejs +110 -0
- package/generators/relation/utils.generator.js +260 -0
- package/generators/repository/index.js +576 -0
- package/generators/repository/templates/src/repositories/repository-crud-default-template.ts.ejs +21 -0
- package/generators/repository/templates/src/repositories/repository-kv-template.ts.ejs +19 -0
- package/generators/rest-crud/crud-rest-component.js +63 -0
- package/generators/rest-crud/index.js +423 -0
- package/generators/rest-crud/templates/src/model-endpoints/model.rest-config-template.ts.ejs +11 -0
- package/generators/service/index.js +351 -0
- package/generators/service/templates/local-service-class-template.ts.ejs +10 -0
- package/generators/service/templates/local-service-provider-template.ts.ejs +19 -0
- package/generators/service/templates/remote-service-proxy-template.ts.ejs +21 -0
- package/generators/update/index.js +55 -0
- package/intl/cs/messages.json +204 -0
- package/intl/de/messages.json +204 -0
- package/intl/en/messages.json +204 -0
- package/intl/es/messages.json +204 -0
- package/intl/fr/messages.json +204 -0
- package/intl/it/messages.json +204 -0
- package/intl/ja/messages.json +204 -0
- package/intl/ko/messages.json +204 -0
- package/intl/nl/messages.json +204 -0
- package/intl/pl/messages.json +204 -0
- package/intl/pt/messages.json +204 -0
- package/intl/ru/messages.json +204 -0
- package/intl/tr/messages.json +204 -0
- package/intl/zh-Hans/messages.json +204 -0
- package/intl/zh-Hant/messages.json +204 -0
- package/lib/artifact-generator.js +137 -39
- package/lib/ast-helper.js +214 -0
- package/lib/base-generator.js +509 -0
- package/lib/cli.js +233 -0
- package/lib/connectors.json +894 -0
- package/lib/debug.js +16 -0
- package/lib/globalize.js +12 -0
- package/lib/model-discoverer.js +118 -0
- package/lib/project-generator.js +154 -57
- package/lib/tab-completion.js +127 -0
- package/lib/update-index.js +44 -0
- package/lib/utils.js +689 -20
- package/lib/version-helper.js +299 -0
- package/package.json +183 -39
- package/CHANGELOG.md +0 -75
- package/bin/cli.js +0 -66
- package/generators/.DS_Store +0 -0
- package/generators/app/templates/index.js +0 -14
- package/generators/app/templates/src/application.ts +0 -27
- package/generators/app/templates/src/controllers/ping-controller.ts +0 -25
- package/generators/app/templates/src/index.ts +0 -25
- package/generators/app/templates/test/ping-controller.test.ts +0 -46
- package/generators/extension/templates/index.js +0 -8
- package/generators/extension/templates/src/component.ts +0 -14
- package/generators/extension/templates/src/index.ts +0 -6
- package/generators/project/templates/.npmrc +0 -1
- package/generators/project/templates/.yo-rc.json +0 -1
- package/generators/project/templates/README.md +0 -4
- package/generators/project/templates/index.d.ts +0 -6
- package/generators/project/templates/index.ts +0 -11
- package/generators/project/templates/package.json +0 -79
- package/generators/project/templates/package.plain.json +0 -82
- package/generators/project/templates/test/mocha.opts +0 -1
- package/generators/project/templates/tsconfig.json +0 -29
- package/generators/project/templates/tslint.build.json +0 -17
- package/generators/project/templates/tslint.json +0 -33
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/cli
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const BaseGenerator = require('../../lib/base-generator');
|
|
11
|
+
const modelUtils = require('../../lib/model-discoverer');
|
|
12
|
+
const debug = require('../../lib/debug')('import-lb3-models');
|
|
13
|
+
const utils = require('../../lib/utils');
|
|
14
|
+
const {loadLb3App} = require('./lb3app-loader');
|
|
15
|
+
const {importLb3ModelDefinition} = require('./migrate-model');
|
|
16
|
+
const {canImportModelName} = require('./model-names');
|
|
17
|
+
const g = require('../../lib/globalize');
|
|
18
|
+
|
|
19
|
+
module.exports = class Lb3ModelImporter extends BaseGenerator {
|
|
20
|
+
constructor(args, opts) {
|
|
21
|
+
super(args, opts);
|
|
22
|
+
|
|
23
|
+
this.argument('lb3app', {
|
|
24
|
+
type: String,
|
|
25
|
+
required: true,
|
|
26
|
+
description: g.f(
|
|
27
|
+
'Path to your LoopBack 3.x application. ' +
|
|
28
|
+
'This can be a project directory (e.g. "my-lb3-app") or ' +
|
|
29
|
+
'the server file (e.g. "my-lb3-app/server/server.js").',
|
|
30
|
+
),
|
|
31
|
+
// description:
|
|
32
|
+
// 'Path to your LoopBack 3.x application. ' +
|
|
33
|
+
// 'This can be a project directory (e.g. "my-lb3-app") or ' +
|
|
34
|
+
// 'the server file (e.g. "my-lb3-app/server/server.js").',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
this.option('outDir', {
|
|
38
|
+
type: String,
|
|
39
|
+
description: g.f('Directory where to write the generated source file'),
|
|
40
|
+
default: 'src/models',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async processOptions() {
|
|
45
|
+
this.sourceAppDir = this.args[0];
|
|
46
|
+
this.artifactInfo.outDir = this.options.outDir;
|
|
47
|
+
this.artifactInfo.relPath = path.relative(
|
|
48
|
+
this.destinationPath(),
|
|
49
|
+
this.artifactInfo.outDir,
|
|
50
|
+
);
|
|
51
|
+
this.artifactInfo.modelDir = path.resolve(
|
|
52
|
+
this.artifactInfo.rootDir,
|
|
53
|
+
utils.modelsDir,
|
|
54
|
+
);
|
|
55
|
+
return super.setOptions();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async logExperimentalStatus() {
|
|
59
|
+
this.log(
|
|
60
|
+
chalk.red(`
|
|
61
|
+
WARNING: This command is experimental and not feature-complete yet.
|
|
62
|
+
Learn more at https://loopback.io/doc/en/lb4/Importing-LB3-models.html
|
|
63
|
+
`),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Ensure CLI is being run in a LoopBack 4 project.
|
|
69
|
+
*/
|
|
70
|
+
checkLoopBackProject() {
|
|
71
|
+
if (this.shouldExit()) return;
|
|
72
|
+
return super.checkLoopBackProject();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async loadTheApp() {
|
|
76
|
+
this.lb3app = await loadLb3App(this.sourceAppDir);
|
|
77
|
+
this.modelRegistry = this.lb3app.registry.modelBuilder.models;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async promptModels() {
|
|
81
|
+
const modelNames = Object.keys(this.modelRegistry).filter(
|
|
82
|
+
canImportModelName,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
debug('Available LB3 models', modelNames);
|
|
86
|
+
|
|
87
|
+
const prompts = [
|
|
88
|
+
{
|
|
89
|
+
name: 'modelNames',
|
|
90
|
+
message: g.f('Select models to import:'),
|
|
91
|
+
type: 'checkbox',
|
|
92
|
+
choices: modelNames,
|
|
93
|
+
// Require at least one model to be selected
|
|
94
|
+
// This prevents users from accidentally pressing ENTER instead of SPACE
|
|
95
|
+
// to select a model from the list
|
|
96
|
+
validate: result => !!result.length,
|
|
97
|
+
// TODO: add a CLI flag to supply these names programmatically
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
const answers = await this.prompt(prompts);
|
|
102
|
+
debug('Models chosen:', answers.modelNames);
|
|
103
|
+
this.modelNames = answers.modelNames;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async loadExistingLb4Models() {
|
|
107
|
+
debug(`model list dir ${this.artifactInfo.modelDir}`);
|
|
108
|
+
this.existingLb4ModelNames = await utils.getArtifactList(
|
|
109
|
+
this.artifactInfo.modelDir,
|
|
110
|
+
'model',
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async migrateSelectedModels() {
|
|
115
|
+
if (this.shouldExit()) return;
|
|
116
|
+
this.modelFiles = [];
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
for (const name of this.modelNames) {
|
|
120
|
+
await this._migrateSingleModel(name);
|
|
121
|
+
}
|
|
122
|
+
} catch (err) {
|
|
123
|
+
if (err.exit) {
|
|
124
|
+
this.exit(err.message);
|
|
125
|
+
} else {
|
|
126
|
+
throw err;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async _migrateSingleModel(name) {
|
|
132
|
+
utils.logClassCreation('model', 'models', name, this.log.bind(this));
|
|
133
|
+
const modelCtor = this.modelRegistry[name];
|
|
134
|
+
if (typeof modelCtor !== 'function') {
|
|
135
|
+
const availableModels = Object.keys(this.modelRegistry)
|
|
136
|
+
.filter(canImportModelName)
|
|
137
|
+
.join(', ');
|
|
138
|
+
|
|
139
|
+
this.exit(
|
|
140
|
+
`Unknown model name ${name}. Available models: ${availableModels}.`,
|
|
141
|
+
);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const templateData = importLb3ModelDefinition(
|
|
146
|
+
modelCtor,
|
|
147
|
+
this.log.bind(this),
|
|
148
|
+
);
|
|
149
|
+
debug('LB4 model data', templateData);
|
|
150
|
+
|
|
151
|
+
if (!templateData.isModelBaseBuiltin) {
|
|
152
|
+
const baseName = templateData.modelBaseClass;
|
|
153
|
+
if (
|
|
154
|
+
!this.existingLb4ModelNames.includes(baseName) &&
|
|
155
|
+
!this.modelNames.includes(baseName)
|
|
156
|
+
) {
|
|
157
|
+
this.log(
|
|
158
|
+
'Adding %s (base of %s) to the list of imported models.',
|
|
159
|
+
chalk.yellow(baseName),
|
|
160
|
+
chalk.yellow(name),
|
|
161
|
+
);
|
|
162
|
+
this.modelNames.push(baseName);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const fileName = utils.getModelFileName(name);
|
|
166
|
+
const fullTargetPath = path.resolve(this.artifactInfo.relPath, fileName);
|
|
167
|
+
debug('Model %s output file', name, fullTargetPath);
|
|
168
|
+
|
|
169
|
+
this.copyTemplatedFiles(
|
|
170
|
+
modelUtils.MODEL_TEMPLATE_PATH,
|
|
171
|
+
fullTargetPath,
|
|
172
|
+
templateData,
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
this.modelFiles.push(fileName);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Iterate through all the models we have discovered and scaffold
|
|
180
|
+
*/
|
|
181
|
+
async scaffold() {
|
|
182
|
+
if (this.shouldExit()) return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async end() {
|
|
186
|
+
if (this.shouldExit() || !this._isGenerationSuccessful()) {
|
|
187
|
+
await super.end();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
for (const f of this.modelFiles) {
|
|
192
|
+
await this._updateIndexFile(this.artifactInfo.outDir, f);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
await super.end();
|
|
196
|
+
}
|
|
197
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/cli
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const debug = require('../../lib/debug')('import-lb3-models');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const {once} = require('events');
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
loadLb3App,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// TODO: do we want to share this code with `Lb3AppBooter.loadAndBootTheApp`?
|
|
17
|
+
async function loadLb3App(dir) {
|
|
18
|
+
debug('Loading LB3 app from', dir);
|
|
19
|
+
const lb3App = require(path.resolve(dir));
|
|
20
|
+
|
|
21
|
+
debug(
|
|
22
|
+
'If your LB3 app does not boot correctly then make sure it is using loopback-boot version 3.2.1 or higher.',
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
if (lb3App.booting) {
|
|
26
|
+
debug(' waiting for boot process to finish');
|
|
27
|
+
// Wait until the legacy LB3 app boots
|
|
28
|
+
await once(lb3App, 'booted');
|
|
29
|
+
}
|
|
30
|
+
return lb3App;
|
|
31
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/cli
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
validateClassName,
|
|
10
|
+
logNamingIssues,
|
|
11
|
+
pascalCase,
|
|
12
|
+
stringifyModelSettings,
|
|
13
|
+
} = require('../../lib/utils');
|
|
14
|
+
const {sanitizeProperty} = require('../../lib/model-discoverer');
|
|
15
|
+
const {
|
|
16
|
+
createPropertyTemplateData,
|
|
17
|
+
findBuiltinType,
|
|
18
|
+
} = require('../model/property-definition');
|
|
19
|
+
const chalk = require('chalk');
|
|
20
|
+
const {isDeepStrictEqual} = require('util');
|
|
21
|
+
|
|
22
|
+
module.exports = {
|
|
23
|
+
importLb3ModelDefinition,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Convert definition of a LB3 model into template data used by
|
|
28
|
+
* LB4 model generator.
|
|
29
|
+
*
|
|
30
|
+
* @param {Function} modelCtor LB3 model class
|
|
31
|
+
* @param {(msg: string, ...args: any) => void} log Logging function
|
|
32
|
+
* @returns {object} Template data for model source file template
|
|
33
|
+
*/
|
|
34
|
+
function importLb3ModelDefinition(modelCtor, log) {
|
|
35
|
+
const modelName = modelCtor.modelName;
|
|
36
|
+
const result = validateClassName(modelName);
|
|
37
|
+
if (!result) {
|
|
38
|
+
const err = new Error(
|
|
39
|
+
`Cannot import model: the name ${modelName} is not valid. ${result}`,
|
|
40
|
+
);
|
|
41
|
+
err.exit = true;
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
logNamingIssues(modelName, log);
|
|
46
|
+
|
|
47
|
+
const baseDefinition = modelCtor.base.definition;
|
|
48
|
+
const baseProps = {...baseDefinition.properties};
|
|
49
|
+
|
|
50
|
+
// Core LB3 models like PersistedModel come with an id property that's
|
|
51
|
+
// injected by juggler. We don't want to inherit that property, because
|
|
52
|
+
// in LB4, we want models to define the id property explicitly.
|
|
53
|
+
if (isCoreModel(modelCtor.base)) {
|
|
54
|
+
delete baseProps.id;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const templateData = {
|
|
58
|
+
name: modelName,
|
|
59
|
+
className: pascalCase(modelName),
|
|
60
|
+
...migrateBaseClass(modelCtor.settings.base),
|
|
61
|
+
properties: migrateModelProperties(
|
|
62
|
+
modelCtor.definition.properties,
|
|
63
|
+
baseProps,
|
|
64
|
+
),
|
|
65
|
+
settings: migrateModelSettings(
|
|
66
|
+
modelCtor.definition.settings,
|
|
67
|
+
baseDefinition.settings,
|
|
68
|
+
log,
|
|
69
|
+
),
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const settings = templateData.settings;
|
|
73
|
+
delete settings.base; // already handled by migrateBaseClass
|
|
74
|
+
templateData.allowAdditionalProperties = !settings.strict;
|
|
75
|
+
templateData.modelSettings = stringifyModelSettings(settings);
|
|
76
|
+
|
|
77
|
+
return templateData;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function migrateModelProperties(properties = {}, inherited = {}) {
|
|
81
|
+
const templateData = {};
|
|
82
|
+
|
|
83
|
+
// In LB 3.x, primary keys are typically contributed by connectors later in
|
|
84
|
+
// the startup process, therefore they end up at the end of the property list.
|
|
85
|
+
// Here we create placeholder entries for PKs to get them generated first.
|
|
86
|
+
Object.keys(properties)
|
|
87
|
+
.filter(p => !!properties[p].id)
|
|
88
|
+
.forEach(pk => {
|
|
89
|
+
templateData[pk] = null;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
for (const prop in properties) {
|
|
93
|
+
const propDef = properties[prop];
|
|
94
|
+
|
|
95
|
+
// Skip the property if it was inherited from the base model (the parent)
|
|
96
|
+
const baseProp = inherited[prop];
|
|
97
|
+
if (baseProp && isDeepStrictEqual(propDef, baseProp)) {
|
|
98
|
+
delete templateData[prop];
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const def = migratePropertyDefinition(propDef);
|
|
103
|
+
templateData[prop] = createPropertyTemplateData(def);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return templateData;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function migratePropertyDefinition(lb3PropDef) {
|
|
110
|
+
const def = {...lb3PropDef};
|
|
111
|
+
|
|
112
|
+
if (!Array.isArray(def.type)) {
|
|
113
|
+
// scalar type
|
|
114
|
+
def.type = migratePropertyType(def.type);
|
|
115
|
+
} else {
|
|
116
|
+
// array type - conversion is slightly more complex
|
|
117
|
+
def.itemType = migratePropertyType(def.type[0]);
|
|
118
|
+
def.type = 'array';
|
|
119
|
+
|
|
120
|
+
// workaround for a weird behavior of LB3
|
|
121
|
+
if (0 in def && def[0] === def.itemType) {
|
|
122
|
+
delete def[0];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (def.updateOnly === false) {
|
|
127
|
+
// updateOnly is disabled by default, no need to specify "false" value
|
|
128
|
+
delete def.updateOnly;
|
|
129
|
+
}
|
|
130
|
+
sanitizeProperty(def);
|
|
131
|
+
return def;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function migratePropertyType(typeDef) {
|
|
135
|
+
if (typeof typeDef === 'function') {
|
|
136
|
+
typeDef = typeDef.name.toString();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const builtin = findBuiltinType(typeDef);
|
|
140
|
+
if (builtin) typeDef = builtin;
|
|
141
|
+
|
|
142
|
+
// TODO: handle anonymous object types (nested properties)
|
|
143
|
+
|
|
144
|
+
return typeDef;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const BUILTIN_BASE_MODELS = {
|
|
148
|
+
Model: 'Model',
|
|
149
|
+
PersistedModel: 'Entity',
|
|
150
|
+
KeyValueModel: 'KeyValueModel',
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
function migrateBaseClass(base) {
|
|
154
|
+
const baseModelName = base.modelName || base.name || base;
|
|
155
|
+
if (baseModelName in BUILTIN_BASE_MODELS) {
|
|
156
|
+
return {
|
|
157
|
+
modelBaseClass: BUILTIN_BASE_MODELS[baseModelName],
|
|
158
|
+
isModelBaseBuiltin: true,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
modelBaseClass: baseModelName,
|
|
164
|
+
isModelBaseBuiltin: false,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function migrateModelSettings(settings = {}, inherited = {}, log) {
|
|
169
|
+
// Shallow-clone the object to prevent modification of external data
|
|
170
|
+
settings = {...settings};
|
|
171
|
+
|
|
172
|
+
// "strict" mode is enabled only when explicitly requested
|
|
173
|
+
// LB3 models allow additional properties by default
|
|
174
|
+
settings.strict = settings.strict === true;
|
|
175
|
+
|
|
176
|
+
// Remove settings inherited from the base model
|
|
177
|
+
for (const key in inherited) {
|
|
178
|
+
// Always emit the value of 'strict' setting, make it explicit
|
|
179
|
+
if (key === 'strict') continue;
|
|
180
|
+
|
|
181
|
+
if (isDeepStrictEqual(settings[key], inherited[key])) {
|
|
182
|
+
delete settings[key];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (settings.forceId === 'auto') {
|
|
187
|
+
// The value 'auto' is used when a parent model wants to let the child
|
|
188
|
+
// model make the decision automatically, depending on whether the child
|
|
189
|
+
// model has a database-generated PK.
|
|
190
|
+
// See https://github.com/loopbackio/loopback-datasource-juggler/blob/15251880a1d07ccc2ca6d2dccdd065d00a7375eb/lib/model-builder.js#L347-L355
|
|
191
|
+
//
|
|
192
|
+
// Let's delete the flag from the generated model settings and
|
|
193
|
+
// leave it up to the runtime to decide.
|
|
194
|
+
delete settings.forceId;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Notable settings that are not supported yet:
|
|
198
|
+
// relations, acls, methods, mixins, validations
|
|
199
|
+
|
|
200
|
+
const relationNames = Object.keys(settings.relations || {});
|
|
201
|
+
if (relationNames.length) {
|
|
202
|
+
log(
|
|
203
|
+
chalk.yellow(
|
|
204
|
+
'Import of model relations is not supported yet. ' +
|
|
205
|
+
'Skipping the following relations: ' +
|
|
206
|
+
relationNames.join(', '),
|
|
207
|
+
),
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
delete settings.relations;
|
|
211
|
+
|
|
212
|
+
const unsupportedSettings = [];
|
|
213
|
+
if ((settings.acls || []).length) {
|
|
214
|
+
unsupportedSettings.push('acls');
|
|
215
|
+
}
|
|
216
|
+
delete settings.acls;
|
|
217
|
+
|
|
218
|
+
for (const k of ['methods', 'mixins', 'validations']) {
|
|
219
|
+
if (k in settings && Object.keys(settings[k]).length) {
|
|
220
|
+
unsupportedSettings.push(k);
|
|
221
|
+
}
|
|
222
|
+
delete settings[k];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (unsupportedSettings.length) {
|
|
226
|
+
log(
|
|
227
|
+
chalk.yellow(
|
|
228
|
+
'Ignoring the following unsupported settings: ' +
|
|
229
|
+
unsupportedSettings.join(', '),
|
|
230
|
+
),
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (
|
|
235
|
+
typeof settings.hiddenProperties === 'object' &&
|
|
236
|
+
Object.keys(settings.hiddenProperties).length === 0
|
|
237
|
+
) {
|
|
238
|
+
delete settings.hiddenProperties;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return settings;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function isCoreModel(modelCtor) {
|
|
245
|
+
const name = modelCtor.modelName;
|
|
246
|
+
return (
|
|
247
|
+
name === 'Model' || name === 'PersistedModel' || name === 'KeyValueModel'
|
|
248
|
+
);
|
|
249
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2019. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/cli
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
canImportModelName,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// List of built-in LB models to exclude from the prompt
|
|
13
|
+
const EXCLUDED_MODEL_NAMES = [
|
|
14
|
+
// base models
|
|
15
|
+
'Model',
|
|
16
|
+
'PersistedModel',
|
|
17
|
+
'KeyValueModel',
|
|
18
|
+
// change tracking & replication
|
|
19
|
+
'Change',
|
|
20
|
+
'Checkpoint',
|
|
21
|
+
// Email - a dummy model used to attach email-connector methods only
|
|
22
|
+
'Email',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
function canImportModelName(name) {
|
|
26
|
+
if (EXCLUDED_MODEL_NAMES.includes(name)) return false;
|
|
27
|
+
// TODO: find out where are anonymous models coming from
|
|
28
|
+
// (perhaps from object types defined inside property definitions?)
|
|
29
|
+
// and add test cases to verify that we are handling those scenarios correctly
|
|
30
|
+
if (name.startsWith('AnonymousModel_')) return false;
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
|
|
2
|
+
// Node module: @loopback/cli
|
|
3
|
+
// This file is licensed under the MIT License.
|
|
4
|
+
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
+
|
|
6
|
+
// no translation: Interceptor
|
|
7
|
+
'use strict';
|
|
8
|
+
const ArtifactGenerator = require('../../lib/artifact-generator');
|
|
9
|
+
const debug = require('../../lib/debug')('interceptor-generator');
|
|
10
|
+
const inspect = require('util').inspect;
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const utils = require('../../lib/utils');
|
|
13
|
+
|
|
14
|
+
const SCRIPT_TEMPLATE = 'interceptor-template.ts.ejs';
|
|
15
|
+
const g = require('../../lib/globalize');
|
|
16
|
+
|
|
17
|
+
module.exports = class InterceptorGenerator extends ArtifactGenerator {
|
|
18
|
+
// Note: arguments and options should be defined in the constructor.
|
|
19
|
+
constructor(args, opts) {
|
|
20
|
+
super(args, opts);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
_setupGenerator() {
|
|
24
|
+
this.option('global', {
|
|
25
|
+
description: g.f('Flag to indicate a global interceptor'),
|
|
26
|
+
required: false,
|
|
27
|
+
type: Boolean,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
this.option('group', {
|
|
31
|
+
description: g.f('Group name for ordering the global interceptor'),
|
|
32
|
+
required: false,
|
|
33
|
+
type: String,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
this.artifactInfo = {
|
|
37
|
+
type: 'interceptor',
|
|
38
|
+
rootDir: utils.sourceRootDir,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
this.artifactInfo.outDir = path.resolve(
|
|
42
|
+
this.artifactInfo.rootDir,
|
|
43
|
+
utils.interceptorsDir,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
this.artifactInfo.defaultTemplate = SCRIPT_TEMPLATE;
|
|
47
|
+
|
|
48
|
+
return super._setupGenerator();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
setOptions() {
|
|
52
|
+
return super.setOptions();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
checkLoopBackProject() {
|
|
56
|
+
if (this.shouldExit()) return;
|
|
57
|
+
return super.checkLoopBackProject();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Ask for Service Name
|
|
62
|
+
*/
|
|
63
|
+
async promptArtifactName() {
|
|
64
|
+
debug('Prompting for interceptor name');
|
|
65
|
+
if (this.shouldExit()) return;
|
|
66
|
+
|
|
67
|
+
if (this.options.name) {
|
|
68
|
+
Object.assign(this.artifactInfo, {name: this.options.name});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const prompts = [
|
|
72
|
+
{
|
|
73
|
+
type: 'input',
|
|
74
|
+
name: 'name',
|
|
75
|
+
// capitalization
|
|
76
|
+
message: g.f('%s name:', utils.toClassName(this.artifactInfo.type)),
|
|
77
|
+
when: !this.artifactInfo.name,
|
|
78
|
+
validate: utils.validateClassName,
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
return this.prompt(prompts).then(props => {
|
|
82
|
+
Object.assign(this.artifactInfo, props);
|
|
83
|
+
return props;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async promptGlobal() {
|
|
88
|
+
debug('Prompting for global interceptor flag');
|
|
89
|
+
if (this.shouldExit()) return;
|
|
90
|
+
|
|
91
|
+
if (this.options.global != null) {
|
|
92
|
+
Object.assign(this.artifactInfo, {isGlobal: !!this.options.global});
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// --group hints global
|
|
97
|
+
if (this.options.group != null) {
|
|
98
|
+
Object.assign(this.artifactInfo, {isGlobal: true});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const prompts = [
|
|
103
|
+
{
|
|
104
|
+
type: 'confirm',
|
|
105
|
+
name: 'isGlobal',
|
|
106
|
+
message: g.f('Is it a global interceptor?'),
|
|
107
|
+
default: true,
|
|
108
|
+
},
|
|
109
|
+
];
|
|
110
|
+
return this.prompt(prompts).then(props => {
|
|
111
|
+
Object.assign(this.artifactInfo, props);
|
|
112
|
+
return props;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async promptInterceptorGroup() {
|
|
117
|
+
if (!this.artifactInfo.isGlobal) return;
|
|
118
|
+
debug('Prompting for global interceptor group');
|
|
119
|
+
if (this.shouldExit()) return;
|
|
120
|
+
|
|
121
|
+
if (this.options.group) {
|
|
122
|
+
Object.assign(this.artifactInfo, {group: this.options.group});
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const note = g.f(
|
|
127
|
+
'Global interceptors are sorted by the order of an array of' +
|
|
128
|
+
' group names bound to ContextBindings.GLOBAL_INTERCEPTOR_ORDERED_GROUPS.' +
|
|
129
|
+
' See https://loopback.io/doc/en/lb4/Interceptors.html#order-of-invocation-for-interceptors.',
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
this.log();
|
|
133
|
+
this.log(note);
|
|
134
|
+
this.log();
|
|
135
|
+
|
|
136
|
+
const prompts = [
|
|
137
|
+
{
|
|
138
|
+
type: 'input',
|
|
139
|
+
name: 'group',
|
|
140
|
+
// capitalization
|
|
141
|
+
message: g.f("Group name for the global interceptor: ('')"),
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
return this.prompt(prompts).then(props => {
|
|
145
|
+
Object.assign(this.artifactInfo, props);
|
|
146
|
+
return props;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
scaffold() {
|
|
151
|
+
if (this.shouldExit()) return false;
|
|
152
|
+
|
|
153
|
+
// Setting up data for templates
|
|
154
|
+
this.artifactInfo.className =
|
|
155
|
+
utils.toClassName(this.artifactInfo.name) + 'Interceptor';
|
|
156
|
+
this.artifactInfo.fileName = utils.toFileName(this.artifactInfo.name);
|
|
157
|
+
|
|
158
|
+
Object.assign(this.artifactInfo, {
|
|
159
|
+
outFile: utils.getInterceptorFileName(this.artifactInfo.name),
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const source = this.templatePath(this.artifactInfo.defaultTemplate);
|
|
163
|
+
|
|
164
|
+
const dest = this.destinationPath(
|
|
165
|
+
path.join(this.artifactInfo.outDir, this.artifactInfo.outFile),
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
debug(`artifactInfo: ${inspect(this.artifactInfo)}`);
|
|
169
|
+
debug(`Copying artifact to: ${dest}`);
|
|
170
|
+
|
|
171
|
+
this.copyTemplatedFiles(source, dest, this.artifactInfo);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async end() {
|
|
176
|
+
await super.end();
|
|
177
|
+
}
|
|
178
|
+
};
|