Skip to content

Commit

Permalink
feat(cli): add support for multiple model generation
Browse files Browse the repository at this point in the history
multiple selection of models to generate multiple repositories

implements loopbackio#1588
  • Loading branch information
marioestradarosa committed Sep 14, 2018
1 parent b010de2 commit 1bbcf1f
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 46 deletions.
120 changes: 75 additions & 45 deletions packages/cli/generators/repository/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,62 +11,70 @@ const inspect = require('util').inspect;
const path = require('path');
const chalk = require('chalk');
const utils = require('../../lib/utils');
const connectors = require('../datasource/connectors.json');

const SERVICE_VALUE_CONNECTOR = 'soap,rest';
const KEY_VALUE_CONNECTOR = 'kv-';
const VALID_CONNECTORS_FOR_REPOSITORY = 'KeyValueModel, PersistedModel';

const KEY_VALUE_CONNECTOR = 'KeyValueModel';
const KEY_VALUE_REPOSITORY = 'KeyValueRepository';
const DEFAULT_CRUD_REPOSITORY = 'DefaultCrudRepository';

const REPOSITORY_KV_TEMPLATE = 'repository-kv-template.ts.ejs';
const REPOSITORY_CRUD_TEMPLATE = 'repository-crud-default-template.ts.ejs';

const PROMPT_MESSAGE_MODEL =
'Select the model you want to generate a repository';
'Select the model(s) you want to generate a repository';
const PROMPT_MESSAGE_DATA_SOURCE = 'Please select the datasource';
const PROMPT_MESSAGE_ID_TYPE = 'What is the type of your ID?';

const ERROR_READING_FILE = 'Error reading file';
const ERROR_NO_DATA_SOURCES_FOUND = 'No datasources found in';
const ERROR_NO_MODELS_FOUND = 'No models found in';
const ERROR_NO_MODEL_SELECTED = 'You did not select any model';

module.exports = class RepositoryGenerator extends ArtifactGenerator {
// Note: arguments and options should be defined in the constructor.
constructor(args, opts) {
super(args, opts);
}

/** instance helper method isolated from the execution loop
* @connectorType: can be a single or a comma separated string list
*/
this.isConnectorType = async function(connectorType, dataSourceClassName) {
debug(`callling isConnectorType ${connectorType}`);
let jsonFileContent = '';
let result = false;
/** helper method isolated from the execution loop
* @connectorType: can be a single or a comma separated string list
*/
async _isConnectorOfType(connectorType, dataSourceClassName) {
debug(`callling isConnectorType ${connectorType}`);
let jsonFileContent = '';
let result = false;

let datasourceJSONFile = path.join(
this.artifactInfo.datasourcesDir,
dataSourceClassName
.replace('Datasource', '.datasource.json')
.toLowerCase(),
);

let datasourceJSONFile = path.join(
this.artifactInfo.datasourcesDir,
dataSourceClassName
.replace('Datasource', '.datasource.json')
.toLowerCase(),
);
try {
jsonFileContent = this.fs.readJSON(datasourceJSONFile, {});
} catch (err) {
debug(`${ERROR_READING_FILE} ${datasourceJSONFile}: ${err.message}`);
return this.exit(err);
}

try {
jsonFileContent = this.fs.readJSON(datasourceJSONFile, {});
} catch (err) {
debug(`${ERROR_READING_FILE} ${datasourceJSONFile}: ${err.message}`);
return this.exit(err);
}
let keyWordsToSearch = connectorType.split(',');

let keyWordsToSearch = connectorType.split(',');
Object.values(connectors).forEach(connector => {
for (let keyWord of keyWordsToSearch) {
debug(`asking for keyword ${keyWord}`);
if (jsonFileContent.connector.includes(keyWord)) {
if (
jsonFileContent.connector === connector.name &&
connector.baseModel === keyWord
) {
result = true;
break;
}
}
return result;
};
});

return result;
}

_setupGenerator() {
Expand Down Expand Up @@ -99,7 +107,7 @@ module.exports = class RepositoryGenerator extends ArtifactGenerator {
return super.checkLoopBackProject();
}

async promptDataSource() {
async promptDataSourceName() {
debug('Prompting for a datasource ');
let datasourcesList;

Expand All @@ -116,9 +124,14 @@ module.exports = class RepositoryGenerator extends ArtifactGenerator {
// iterate over it to exclude service oriented data sources
let tempDataSourceList = Object.assign(datasourcesList, {});
for (let item of tempDataSourceList) {
let result = await this.isConnectorType(SERVICE_VALUE_CONNECTOR, item);
debug(`${item} has keyword ${SERVICE_VALUE_CONNECTOR} is ${result}`);
if (result) {
let result = await this._isConnectorOfType(
VALID_CONNECTORS_FOR_REPOSITORY,
item,
);
debug(
`${item} has keyword ${VALID_CONNECTORS_FOR_REPOSITORY} is ${result}`,
);
if (!result) {
// remove from original list
_.remove(datasourcesList, e => e == item);
}
Expand Down Expand Up @@ -156,7 +169,7 @@ module.exports = class RepositoryGenerator extends ArtifactGenerator {
}

async inferRepositoryType() {
let result = await this.isConnectorType(
let result = await this._isConnectorOfType(
KEY_VALUE_CONNECTOR,
this.artifactInfo.dataSourceClassName,
);
Expand Down Expand Up @@ -201,21 +214,12 @@ module.exports = class RepositoryGenerator extends ArtifactGenerator {

return this.prompt([
{
type: 'list',
name: 'modelName',
type: 'checkbox',
name: 'modelNameList',
message: PROMPT_MESSAGE_MODEL,
choices: modelList,
when: this.artifactInfo.modelName === undefined,
when: this.artifactInfo.modelNameList === undefined,
default: modelList[0],
validate: utils.validateClassName,
},
{
type: 'list',
name: 'idType',
message: PROMPT_MESSAGE_ID_TYPE,
choices: ['number', 'string', 'object'],
when: this.artifactInfo.idType === undefined,
default: 'number',
},
])
.then(props => {
Expand All @@ -229,7 +233,33 @@ module.exports = class RepositoryGenerator extends ArtifactGenerator {
});
}

scaffold() {
async promptModelId() {
if (_.isEmpty(this.artifactInfo.modelNameList)) {
return this.exit(new Error(`${ERROR_NO_MODEL_SELECTED}`));
} else {
// iterate thru each selected model, infer or ask for the ID type
for (let item of this.artifactInfo.modelNameList) {
debug(`iterating thru ${item}`);
this.artifactInfo.modelName = item;
const prompts = [
{
type: 'list',
name: 'idType',
message: `What is the ID type of ${item} `,
choices: ['number', 'string', 'object'],
default: 'number',
},
];
const answer = await this.prompt(prompts);
this.artifactInfo.idType = answer.idType;

// Generate this repository
await this._scaffold();
}
}
}

async _scaffold() {
// We don't want to call the base scaffold function since it copies
// all of the templates!
if (this.shouldExit()) return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import {<%= modelName %>} from '../models';
import {inject} from '@loopback/core';

export class <%= className %>Repository extends <%= repositoryTypeClass %><
<%= modelName %>, <%= idType %>> {
<%= modelName %>,
<%= idType %>,
> {
constructor(
@inject('datasources.<%= dataSourceName %>') protected datasource: juggler.DataSource,
) {
Expand Down

0 comments on commit 1bbcf1f

Please sign in to comment.