Skip to content

Commit 6ae1423

Browse files
awaissaeed530dhmlau
authored andcommitted
feat(rest-crud): add option to create readonly rest-crud apis
Signed-off-by: Awais Saeed <[email protected]>
1 parent 4f9214c commit 6ae1423

File tree

11 files changed

+145
-25
lines changed

11 files changed

+145
-25
lines changed

docs/site/Rest-Crud-generator.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ src/datasources
2727

2828
`--basePath` : _(Optional)_ base path of the model endpoint
2929

30+
`--readonly` : _(Optional)_ create readonly APIs e.g find and count
31+
3032
### Configuration file
3133

3234
This generator supports a config file with the following format, see the
@@ -37,7 +39,8 @@ file.
3739
{
3840
"datasource": "validDataSourceName",
3941
"model": "validModelName",
40-
"basePath": "/<base-path>"
42+
"basePath": "/<base-path>",
43+
"readonly": "<boolean>"
4144
}
4245
```
4346

@@ -74,6 +77,9 @@ The tool will prompt you for:
7477
supplied from the command line with `--basePath` option or more than one
7578
models are selected, the prompt is skipped.
7679

80+
- **Create readonly APIs.** _(readonly)_ If readonly had been supplied from the
81+
command line with `--readonly` option, the prompt is skipped.
82+
7783
### Output
7884

7985
Once all the prompts have been answered, the CLI will do the following for each

packages/cli/.yo-rc.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,6 +1577,14 @@
15771577
"name": "basePath",
15781578
"hide": false
15791579
},
1580+
"readonly": {
1581+
"type": "Boolean",
1582+
"required": false,
1583+
"description": "Create readonly APIs",
1584+
"default": false,
1585+
"name": "readonly",
1586+
"hide": false
1587+
},
15801588
"config": {
15811589
"type": "String",
15821590
"alias": "c",

packages/cli/generators/rest-crud/index.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const PROMPT_MESSAGE_MODEL = g.f(
2626
);
2727
const PROMPT_MESSAGE_DATA_SOURCE = g.f('Please select the datasource');
2828
const PROMPT_MESSAGE_BASE_PATH = g.f('Please specify the base path');
29+
const PROMPT_MESSAGE_READONLY = g.f('Do you want to create readonly APIs?');
2930

3031
const ERROR_NO_DATA_SOURCES_FOUND = g.f('No datasources found at');
3132
const ERROR_NO_MODELS_FOUND = g.f('No models found at');
@@ -93,6 +94,13 @@ module.exports = class RestCrudGenerator extends ArtifactGenerator {
9394
description: g.f('A valid base path'),
9495
});
9596

97+
this.option('readonly', {
98+
type: Boolean,
99+
required: false,
100+
description: g.f('Create readonly APIs'),
101+
default: false,
102+
});
103+
96104
return super._setupGenerator();
97105
}
98106

@@ -308,6 +316,20 @@ module.exports = class RestCrudGenerator extends ArtifactGenerator {
308316
}
309317
}
310318

319+
async promptReadonly() {
320+
const props = await this.prompt([
321+
{
322+
type: 'confirm',
323+
name: 'readonly',
324+
message: PROMPT_MESSAGE_READONLY,
325+
default: false,
326+
},
327+
]);
328+
if (props) {
329+
this.artifactInfo.readonly = props.readonly;
330+
}
331+
}
332+
311333
async scaffold() {
312334
if (this.shouldExit()) return false;
313335

packages/cli/generators/rest-crud/templates/src/model-endpoints/model.rest-config-template.ts.ejs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ const config: ModelCrudRestApiConfig = {
66
pattern: 'CrudRest',
77
dataSource: '<%= dataSourceName %>',
88
basePath: '<%= basePath %>',
9+
readonly: <%= readonly %>,
910
};
1011
module.exports = config;

packages/cli/snapshots/integration/cli/cli.integration.snapshots.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,6 +1635,14 @@ exports[`cli saves command metadata to .yo-rc.json 1`] = `
16351635
"name": "basePath",
16361636
"hide": false
16371637
},
1638+
"readonly": {
1639+
"type": "Boolean",
1640+
"required": false,
1641+
"description": "Create readonly APIs",
1642+
"default": false,
1643+
"name": "readonly",
1644+
"hide": false
1645+
},
16381646
"config": {
16391647
"type": "String",
16401648
"alias": "c",

packages/cli/snapshots/integration/generators/rest-crud.integration.snapshots.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const config: ModelCrudRestApiConfig = {
1616
pattern: 'CrudRest',
1717
dataSource: 'dbmem',
1818
basePath: '/default-models',
19+
readonly: false,
1920
};
2021
module.exports = config;
2122

packages/cli/test/integration/generators/rest-crud.integration.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,38 @@ describe('lb4 rest-crud', /** @this {Mocha.Suite} */ function () {
193193
assert.fileContent(expectedFile, /basePath: '\/decorator-defineds'/);
194194
assertApplicationTsFileUpdated();
195195
});
196+
197+
it('generates a single readonly rest-crud model endpoint with base path', async () => {
198+
const singleModelPrompt = {
199+
basePath: '/multiWords',
200+
dataSourceName: 'dbmem',
201+
modelNameList: ['MultiWord'],
202+
readonly: true,
203+
};
204+
205+
await testUtils
206+
.executeGenerator(generator)
207+
.inDir(sandbox.path, () =>
208+
testUtils.givenLBProject(sandbox.path, {
209+
additionalFiles: SANDBOX_FILES,
210+
}),
211+
)
212+
.withPrompts(singleModelPrompt);
213+
214+
const expectedFile = path.join(
215+
sandbox.path,
216+
MODEL_ENDPOINT_PATH,
217+
'multi-word.rest-config.ts',
218+
);
219+
220+
assert.file(expectedFile);
221+
assert.fileContent(
222+
expectedFile,
223+
/import \{MultiWord\} from '\.\.\/models'/,
224+
);
225+
assert.fileContent(expectedFile, /readonly: true/);
226+
assertApplicationTsFileUpdated();
227+
});
196228
});
197229

198230
describe('all invalid parameters and usage', () => {

packages/rest-crud/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export class TryApplication extends BootMixin(
4343

4444
Create a new file for the configuration, e.g.
4545
`src/model-endpoints/product.rest-config.ts` that defines the `model`,
46-
`pattern`, `dataSource`, and `basePath` properties:
46+
`pattern`, `dataSource`, `basePath`, and `readonly` properties:
4747

4848
```ts
4949
import {ModelCrudRestApiConfig} from '@loopback/rest-crud';
@@ -54,6 +54,7 @@ module.exports = <ModelCrudRestApiConfig>{
5454
pattern: 'CrudRest', // make sure to use this pattern
5555
dataSource: 'db',
5656
basePath: '/products',
57+
readonly: false,
5758
};
5859
```
5960

packages/rest-crud/src/__tests__/unit/crud-rest-controller.unit.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,26 @@ describe('defineCrudRestController', () => {
2323

2424
expect(CrudRestController.name).to.equal('ProductController');
2525
});
26+
27+
it('should generate controller based on Model name with readonly APIs', async () => {
28+
@model()
29+
class Product extends Entity {
30+
@property({id: true})
31+
id: number;
32+
}
33+
34+
const CrudRestController = defineCrudRestController<
35+
Product,
36+
typeof Product.prototype.id,
37+
'id'
38+
>(Product, {basePath: '/products', readonly: true});
39+
40+
expect(CrudRestController.name).to.equal('ProductController');
41+
42+
// It should not generate create method
43+
expect(CrudRestController.prototype.create).to.equal(undefined);
44+
45+
// It must generate read methods
46+
expect(CrudRestController.prototype.find).not.equal(undefined);
47+
});
2648
});

packages/rest-crud/src/crud-rest.api-builder.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ const debug = debugFactory('loopback:boot:crud-rest');
3131
export interface ModelCrudRestApiConfig extends ModelApiConfig {
3232
// E.g. '/products'
3333
basePath: string;
34+
/**
35+
* If true, the generated controller only has find and count APIs
36+
*/
37+
readonly?: boolean;
3438
}
3539

3640
@injectable(asModelApiBuilder)

0 commit comments

Comments
 (0)