Skip to content

Commit a68d78b

Browse files
committed
feat(cli): add strict prompt to model generator
1 parent 9cc6e27 commit a68d78b

File tree

7 files changed

+78
-6
lines changed

7 files changed

+78
-6
lines changed

docs/site/Model-generator.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,16 @@ The tool will prompt you for:
4141
been supplied with a valid model class name, the prompt is skipped. It will
4242
present you with a list of available models from `src/models` including the
4343
**Entity** and **Model** at the top of the list.
44+
4445
- An **Entity** is a persisted model with an identity (ID).
4546
- A **Model** is a business domain object.
4647
- For more information, see
4748
[here](https://loopback.io/doc/en/lb4/Model.html#overview).
4849

50+
- **Allow additonal properties.** _(allowAdditionalProperties)_ Defaults to
51+
**false**. To allow arbitrary properties in addition to well-defined
52+
properties, disable strict mode.
53+
4954
The tool will next recursively prompt you for the model's properties until a
5055
blank one is entered. Properties will be prompted for as follows:
5156

docs/site/Model.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ persistence layer respects this constraint and configures underlying
127127
PersistedModel classes to enforce `strict` mode.
128128

129129
To create a model that allows both well-defined but also arbitrary extra
130-
properties, you need to disable `strict` mode in model settings and tell
131-
TypeScript to allow arbitrary additional properties to be set on model
130+
properties, you need to disable `strict` mode in model settings through the CLI
131+
and tell TypeScript to allow arbitrary additional properties to be set on model
132132
instances.
133133

134134
```ts

docs/site/todo-list-tutorial-model.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ for us as follows:
3232
$ lb4 model
3333
? Model class name: TodoList
3434
? Please select the model base class Entity
35+
? Allow additional (free-form) properties? No
36+
3537
Let's add a property to TodoList
3638
Enter an empty property name when done
3739

docs/site/todo-tutorial-model.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ these steps:
4949
lb4 model
5050
? Model class name: todo
5151
? Please select the model base class: Entity
52+
? Allow additional (free-form) properties? No
5253

5354
Let's add a property to Todo
5455
Enter an empty property name when done

packages/cli/generators/model/index.js

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,20 +154,42 @@ module.exports = class ModelGenerator extends ArtifactGenerator {
154154
},
155155
])
156156
.then(props => {
157-
if (typeof props.modelBaseClass === 'object')
157+
if (typeof props.modelBaseClass === 'object') {
158158
props.modelBaseClass = props.modelBaseClass.value;
159+
}
159160

160161
Object.assign(this.artifactInfo, props);
161162
debug(`props after model base class prompt: ${inspect(props)}`);
163+
return props;
164+
})
165+
.catch(err => {
166+
debug(`Error during model base class prompt: ${err}`);
167+
return this.exit(err);
168+
});
169+
}
170+
171+
async promptStrictMode() {
172+
if (this.shouldExit()) return false;
173+
return this.prompt([
174+
{
175+
name: 'allowAdditionalProperties',
176+
message: 'Allow additional (free-form) properties?',
177+
type: 'confirm',
178+
default: false,
179+
when: !this.artifactInfo.allowAdditionalProperties,
180+
},
181+
])
182+
.then(setting => {
183+
Object.assign(this.artifactInfo, setting);
184+
162185
this.log(
163186
`Let's add a property to ${chalk.yellow(
164187
this.artifactInfo.className,
165188
)}`,
166189
);
167-
return props;
168190
})
169191
.catch(err => {
170-
debug(`Error during model base class prompt: ${err}`);
192+
debug(`Error during model strict mode prompt: ${err}`);
171193
return this.exit(err);
172194
});
173195
}

packages/cli/generators/model/templates/model.ts.ejs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import {model, property} from '@loopback/repository';
55
import {<%= modelBaseClass %>} from '.';
66
<% } -%>
77

8+
<% if(allowAdditionalProperties) { -%>
9+
@model({settings: {strict: false}})
10+
<% } else { -%>
811
@model()
12+
<% } -%>
913
export class <%= className %> extends <%= modelBaseClass %> {
1014
<% Object.entries(properties).forEach(([key, val]) => { -%>
1115
@property({
@@ -16,8 +20,16 @@ export class <%= className %> extends <%= modelBaseClass %> {
1620
<%_ }) -%>
1721
})
1822
<%= key %><%if (!val.required) {%>?<% } %>: <%= val.tsType %>;
19-
2023
<% }) -%>
24+
25+
<% if(allowAdditionalProperties) { -%>
26+
// Define well-known properties here
27+
28+
29+
// Indexer property to allow additional data
30+
[prop: string]: any;
31+
<% } -%>
32+
2133
constructor(data?: Partial<<%= className %>>) {
2234
super(data);
2335
}

packages/cli/test/integration/generators/model.integration.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ describe('lb4 model integration', () => {
9292
name: 'test',
9393
propName: null,
9494
modelBaseClass: 'Model',
95+
allowAdditionalProperties: false,
9596
});
9697

9798
assert.file(expectedModelFile);
@@ -136,6 +137,35 @@ describe('lb4 model integration', () => {
136137
);
137138
});
138139

140+
it('scaffolds model with strict setting disabled', async () => {
141+
await testUtils
142+
.executeGenerator(generator)
143+
.inDir(SANDBOX_PATH, () => testUtils.givenLBProject(SANDBOX_PATH))
144+
.withPrompts({
145+
name: 'test',
146+
propName: null,
147+
modelBaseClass: 'Entity',
148+
allowAdditionalProperties: true,
149+
});
150+
151+
assert.file(expectedModelFile);
152+
assert.file(expectedIndexFile);
153+
154+
assert.fileContent(
155+
expectedModelFile,
156+
/import {Entity, model, property} from '@loopback\/repository';/,
157+
);
158+
assert.fileContent(
159+
expectedModelFile,
160+
/@model\({settings: {strict: false}}\)/,
161+
);
162+
assert.fileContent(
163+
expectedModelFile,
164+
/export class Test extends Entity {/,
165+
);
166+
assert.fileContent(expectedModelFile, /\[prop: string\]: any;/);
167+
});
168+
139169
it('scaffolds correct files with args', async () => {
140170
await testUtils
141171
.executeGenerator(generator)

0 commit comments

Comments
 (0)