@loopback/cli
Version:
Yeoman generator for LoopBack 4
201 lines (171 loc) • 6.76 kB
JavaScript
// Copyright IBM Corp. and LoopBack contributors 2019,2020. 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 path = require('path');
const BaseRelationGenerator = require('./base-relation.generator');
const relationUtils = require('./utils.generator');
const utils = require('../../lib/utils');
const CONTROLLER_TEMPLATE_PATH_HAS_MANY =
'controller-relation-template-has-many.ts.ejs';
module.exports = class HasManyRelationGenerator extends BaseRelationGenerator {
constructor(args, opts) {
super(args, opts);
}
async generateControllers(options) {
this.artifactInfo.sourceModelClassName = options.sourceModel;
this.artifactInfo.targetModelClassName = options.destinationModel;
this.artifactInfo.sourceRepositoryClassName =
this.artifactInfo.sourceModelClassName + 'Repository';
this.artifactInfo.controllerClassName =
this.artifactInfo.sourceModelClassName +
this.artifactInfo.targetModelClassName +
'Controller';
this.artifactInfo.paramSourceRepository = utils.camelCase(
this.artifactInfo.sourceModelClassName + 'Repository',
);
this.artifactInfo.sourceModelName = utils.toFileName(options.sourceModel);
this.artifactInfo.sourceModelPath = utils.pluralize(
this.artifactInfo.sourceModelName,
);
this.artifactInfo.targetModelName = utils.toFileName(
options.destinationModel,
);
this.artifactInfo.targetModelPath = utils.pluralize(
this.artifactInfo.targetModelName,
);
this.artifactInfo.targetModelRequestBody = utils.camelCase(
this.artifactInfo.targetModelName,
);
this.artifactInfo.relationPropertyName = options.relationName;
this.artifactInfo.sourceModelPrimaryKey = options.sourceModelPrimaryKey;
this.artifactInfo.sourceModelPrimaryKeyType =
options.sourceModelPrimaryKeyType;
this.artifactInfo.targetModelPrimaryKey =
options.destinationModelPrimaryKey;
this.artifactInfo.foreignKeyName = options.foreignKeyName;
const source = this.templatePath(CONTROLLER_TEMPLATE_PATH_HAS_MANY);
this.artifactInfo.name =
options.sourceModel + '-' + options.destinationModel;
this.artifactInfo.outFile =
utils.toFileName(this.artifactInfo.name) + '.controller.ts';
const dest = this.destinationPath(
path.join(this.artifactInfo.outDir, this.artifactInfo.outFile),
);
this.copyTemplatedFiles(source, dest, this.artifactInfo);
await relationUtils.addExportController(
this,
path.resolve(this.artifactInfo.outDir, 'index.ts'),
this.artifactInfo.controllerClassName,
utils.toFileName(this.artifactInfo.name) + '.controller',
);
}
async generateModels(options) {
// for repo to generate relation name
this.artifactInfo.relationName = options.relationName;
const modelDir = this.artifactInfo.modelDir;
const sourceModel = options.sourceModel;
const targetModel = options.destinationModel;
const relationType = options.relationType;
const relationName = options.relationName;
const fktype = options.sourceModelPrimaryKeyType;
const isForeignKeyExist = options.doesForeignKeyExist;
const foreignKeyName = options.foreignKeyName;
const isDefaultForeignKey =
foreignKeyName === utils.camelCase(options.sourceModel) + 'Id';
let modelProperty;
const project = new relationUtils.AstLoopBackProject();
const sourceFile = relationUtils.addFileToProject(
project,
modelDir,
sourceModel,
);
const sourceClass = relationUtils.getClassObj(sourceFile, sourceModel);
relationUtils.doesRelationExist(sourceClass, relationName);
modelProperty = this.getHasMany(
targetModel,
relationName,
isDefaultForeignKey,
foreignKeyName,
);
relationUtils.addProperty(sourceClass, modelProperty);
const imports = relationUtils.getRequiredImports(targetModel, relationType);
relationUtils.addRequiredImports(sourceFile, imports);
await sourceFile.save();
const targetFile = relationUtils.addFileToProject(
project,
modelDir,
targetModel,
);
const targetClass = relationUtils.getClassObj(targetFile, targetModel);
if (isForeignKeyExist) {
if (
!relationUtils.isValidPropertyType(targetClass, foreignKeyName, fktype)
) {
throw new Error('foreignKey Type Error');
}
} else {
modelProperty = relationUtils.addForeignKey(foreignKeyName, fktype);
relationUtils.addProperty(targetClass, modelProperty);
targetClass.formatText();
await targetFile.save();
}
}
getHasMany(className, relationName, isDefaultForeignKey, foreignKeyName) {
let relationDecorator = [
{
name: 'hasMany',
arguments: [`() => ${className}, {keyTo: '${foreignKeyName}'}`],
},
];
if (isDefaultForeignKey) {
relationDecorator = [
{name: 'hasMany', arguments: [`() => ${className}`]},
];
}
return {
decorators: relationDecorator,
name: relationName,
type: className + '[]',
};
}
_getRepositoryRequiredImports(dstModelClassName, dstRepositoryClassName) {
const importsArray = super._getRepositoryRequiredImports(
dstModelClassName,
dstRepositoryClassName,
);
importsArray.push({
name: 'HasManyRepositoryFactory',
module: '@loopback/repository',
});
return importsArray;
}
_getRepositoryRelationPropertyName() {
return this.artifactInfo.relationName;
}
_getRepositoryRelationPropertyType() {
return `HasManyRepositoryFactory<${utils.toClassName(
this.artifactInfo.dstModelClass,
)}, typeof ${utils.toClassName(
this.artifactInfo.srcModelClass,
)}.prototype.${this.artifactInfo.srcModelPrimaryKey}>`;
}
_addCreatorToRepositoryConstructor(classConstructor) {
const relationPropertyName = this._getRepositoryRelationPropertyName();
const statement =
`this.${relationPropertyName} = ` +
`this.createHasManyRepositoryFactoryFor('${relationPropertyName}', ` +
`${utils.camelCase(this.artifactInfo.dstRepositoryClassName)}Getter,);`;
classConstructor.insertStatements(1, statement);
}
_registerInclusionResolverForRelation(classConstructor, options) {
const relationPropertyName = this._getRepositoryRelationPropertyName();
if (options.registerInclusionResolver) {
const statement =
`this.registerInclusionResolver(` +
`'${relationPropertyName}', this.${relationPropertyName}.inclusionResolver);`;
classConstructor.insertStatements(2, statement);
}
}
};