diff --git a/docs/site/Relation-generator.md b/docs/site/Relation-generator.md index e5c07ed9f091..5999104b5d94 100644 --- a/docs/site/Relation-generator.md +++ b/docs/site/Relation-generator.md @@ -60,6 +60,17 @@ lb4 relation --sourceModel= - `` - Name of the relation that will be created. +After setting up the definition for the relation, you need to confirm if you +want to register the inclusion resolver for this relation: + +```ts +? Allow queries to include data from related instances? (Y/n) +``` + +Check the site [Relations](HasMany-relation.md) and the +[Querying Related Models](HasMany-relation.md#querying-related-models) section +in each relation for more use cases. + ### Interactive Prompts The tool will prompt you for: diff --git a/packages/cli/generators/relation/base-relation.generator.js b/packages/cli/generators/relation/base-relation.generator.js index 730603313cb8..d1127d2f425a 100644 --- a/packages/cli/generators/relation/base-relation.generator.js +++ b/packages/cli/generators/relation/base-relation.generator.js @@ -70,6 +70,7 @@ module.exports = class BaseRelationGenerator extends ArtifactGenerator { ); this._addParametersToRepositoryConstructor(classConstructor); this._addCreatorToRepositoryConstructor(classConstructor); + this._registerInclusionResolverForRelation(classConstructor, options); await this.artifactInfo.srcRepositoryFileObj.save(); } @@ -134,6 +135,11 @@ module.exports = class BaseRelationGenerator extends ArtifactGenerator { throw new Error('Not implemented'); } + _registerInclusionResolverForRelation(classConstructor, options) { + /* istanbul ignore next */ + throw new Error('Not implemented'); + } + _initializeProperties(options) { // src configuration. this.artifactInfo.srcModelPrimaryKey = options.sourceModelPrimaryKey; diff --git a/packages/cli/generators/relation/belongs-to-relation.generator.js b/packages/cli/generators/relation/belongs-to-relation.generator.js index b175d4d5ca58..dc85a55fae09 100644 --- a/packages/cli/generators/relation/belongs-to-relation.generator.js +++ b/packages/cli/generators/relation/belongs-to-relation.generator.js @@ -103,7 +103,7 @@ module.exports = class BelongsToRelationGenerator extends BaseRelationGenerator getBelongsTo(className, relationName, fktype) { return { - decorators: [{name: 'belongsTo', arguments: ['() => ' + className]}], + decorators: [{name: 'belongsTo', arguments: [`() => ${className}`]}], name: relationName, type: fktype, }; @@ -132,26 +132,31 @@ module.exports = class BelongsToRelationGenerator extends BaseRelationGenerator _getRepositoryRelationPropertyType() { return ( - 'BelongsToAccessor<' + - utils.toClassName(this.artifactInfo.dstModelClass) + - ', typeof ' + - utils.toClassName(this.artifactInfo.srcModelClass) + - '.prototype.' + - this.artifactInfo.srcModelPrimaryKey + - '>' + `BelongsToAccessor<` + + `${utils.toClassName(this.artifactInfo.dstModelClass)}` + + `, typeof ${utils.toClassName(this.artifactInfo.srcModelClass)}` + + `.prototype.${this.artifactInfo.srcModelPrimaryKey}>` ); } _addCreatorToRepositoryConstructor(classConstructor) { + const relationPropertyName = this._getRepositoryRelationPropertyName(); const statement = - 'this.' + - this._getRepositoryRelationPropertyName() + - ' = ' + - "this.createBelongsToAccessorFor('" + - this.artifactInfo.relationName.replace(/Id$/, '') + - "', " + - utils.camelCase(this.artifactInfo.dstRepositoryClassName) + - 'Getter,);'; + `this.${relationPropertyName} = ` + + `this.createBelongsToAccessorFor('` + + `${this.artifactInfo.relationName.replace(/Id$/, '')}',` + + ` ${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); + } + } }; diff --git a/packages/cli/generators/relation/has-many-relation.generator.js b/packages/cli/generators/relation/has-many-relation.generator.js index eeef90a2fd77..3c2e4199b169 100644 --- a/packages/cli/generators/relation/has-many-relation.generator.js +++ b/packages/cli/generators/relation/has-many-relation.generator.js @@ -137,14 +137,12 @@ module.exports = class HasManyRelationGenerator extends BaseRelationGenerator { let relationDecorator = [ { name: 'hasMany', - arguments: [ - '() => ' + className + " ,{keyTo: '" + foreignKeyName + "'}", - ], + arguments: [`() => ${className}, {keyTo: '${foreignKeyName}'}`], }, ]; if (isDefaultForeignKey) { relationDecorator = [ - {name: 'hasMany', arguments: ['() => ' + className]}, + {name: 'hasMany', arguments: [`() => ${className}`]}, ]; } @@ -172,28 +170,29 @@ module.exports = class HasManyRelationGenerator extends BaseRelationGenerator { } _getRepositoryRelationPropertyType() { - return ( - 'HasManyRepositoryFactory<' + - utils.toClassName(this.artifactInfo.dstModelClass) + - ', typeof ' + - utils.toClassName(this.artifactInfo.srcModelClass) + - '.prototype.' + - this.artifactInfo.srcModelPrimaryKey + - '>' - ); + 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,);'; + `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); + } + } }; diff --git a/packages/cli/generators/relation/index.js b/packages/cli/generators/relation/index.js index d8acea99f8e0..9b65bc41926b 100644 --- a/packages/cli/generators/relation/index.js +++ b/packages/cli/generators/relation/index.js @@ -73,6 +73,13 @@ module.exports = class RelationGenerator extends ArtifactGenerator { required: false, description: 'Relation name', }); + + this.option('registerInclusionResolver', { + type: Boolean, + required: false, + description: + 'Allow queries to include data from related ', + }); this.artifactInfo = { type: 'relation', rootDir: utils.sourceRootDir, @@ -374,6 +381,24 @@ module.exports = class RelationGenerator extends ArtifactGenerator { }); } + async promptRegisterInclusionResolver() { + const props = await this.prompt([ + { + type: 'confirm', + name: 'registerInclusionResolver', + message: `Allow ${chalk.yellow( + this.artifactInfo.sourceModel, + )} queries to include data from related ${chalk.yellow( + this.artifactInfo.destinationModel, + )} instances?`, + default: true, + }, + ]); + debug(`props after inclusion resolver promps: ${inspect(props)}`); + Object.assign(this.artifactInfo, props); + return props; + } + async scaffold() { if (this.shouldExit()) return false; diff --git a/packages/cli/snapshots/integration/generators/relation.belongs-to.integration.snapshots.js b/packages/cli/snapshots/integration/generators/relation.belongs-to.integration.snapshots.js index 35bdba32059e..0876c78d3123 100644 --- a/packages/cli/snapshots/integration/generators/relation.belongs-to.integration.snapshots.js +++ b/packages/cli/snapshots/integration/generators/relation.belongs-to.integration.snapshots.js @@ -26,13 +26,14 @@ export class OrderRepository extends DefaultCrudRepository< constructor(@inject('datasources.db') dataSource: DbDataSource, @repository.getter('CustomerRepository') protected customerRepositoryGetter: Getter,) { super(Order, dataSource); this.customer = this.createBelongsToAccessorFor('customer', customerRepositoryGetter,); + this.registerInclusionResolver('customer', this.customer.inclusionResolver); } } `; exports[ - `lb4 relation checks generated source class repository answers {"relationType":"belongsTo","sourceModel":"OrderClass","destinationModel":"CustomerClass"} generates OrderClass repository file with different inputs 1` + `lb4 relation checks generated source class repository answers {"relationType":"belongsTo","sourceModel":"OrderClass","destinationModel":"CustomerClass","registerInclusionResolver":true} generates OrderClass repository file with different inputs 1` ] = ` import {DefaultCrudRepository, repository, BelongsToAccessor} from '@loopback/repository'; import {OrderClass, CustomerClass} from '../models'; @@ -50,13 +51,14 @@ export class OrderClassRepository extends DefaultCrudRepository< constructor(@inject('datasources.myDB') dataSource: MyDBDataSource, @repository.getter('CustomerClassRepository') protected customerClassRepositoryGetter: Getter,) { super(OrderClass, dataSource); this.customerClass = this.createBelongsToAccessorFor('customerClassCustNumber', customerClassRepositoryGetter,); + this.registerInclusionResolver('customerClass', this.customerClass.inclusionResolver); } } `; exports[ - `lb4 relation checks generated source class repository answers {"relationType":"belongsTo","sourceModel":"OrderClassType","destinationModel":"CustomerClassType"} generates OrderClassType repository file with different inputs 1` + `lb4 relation checks generated source class repository answers {"relationType":"belongsTo","sourceModel":"OrderClassType","destinationModel":"CustomerClassType","registerInclusionResolver":false} generates OrderClassType repository file with different inputs 1` ] = ` import {DefaultCrudRepository, repository, BelongsToAccessor} from '@loopback/repository'; import {OrderClassType, CustomerClassType} from '../models'; diff --git a/packages/cli/snapshots/integration/generators/relation.has-many.integration.snapshots.js b/packages/cli/snapshots/integration/generators/relation.has-many.integration.snapshots.js index 499cae62ee40..ea4cc772e472 100644 --- a/packages/cli/snapshots/integration/generators/relation.has-many.integration.snapshots.js +++ b/packages/cli/snapshots/integration/generators/relation.has-many.integration.snapshots.js @@ -26,13 +26,14 @@ export class CustomerRepository extends DefaultCrudRepository< constructor(@inject('datasources.db') dataSource: DbDataSource, @repository.getter('OrderRepository') protected orderRepositoryGetter: Getter,) { super(Customer, dataSource); this.orders = this.createHasManyRepositoryFactoryFor('orders', orderRepositoryGetter,); + this.registerInclusionResolver('orders', this.orders.inclusionResolver); } } `; exports[ - `lb4 relation HasMany checks generated source class repository answers {"relationType":"hasMany","sourceModel":"CustomerClass","destinationModel":"OrderClass"} generates CustomerClass repository file with different inputs 1` + `lb4 relation HasMany checks generated source class repository answers {"relationType":"hasMany","sourceModel":"CustomerClass","destinationModel":"OrderClass","registerInclusionResolver":true} generates CustomerClass repository file with different inputs 1` ] = ` import {DefaultCrudRepository, repository, HasManyRepositoryFactory} from '@loopback/repository'; import {CustomerClass, OrderClass} from '../models'; @@ -50,13 +51,14 @@ export class CustomerClassRepository extends DefaultCrudRepository< constructor(@inject('datasources.myDB') dataSource: MyDBDataSource, @repository.getter('OrderClassRepository') protected orderClassRepositoryGetter: Getter,) { super(CustomerClass, dataSource); this.orderClasses = this.createHasManyRepositoryFactoryFor('orderClasses', orderClassRepositoryGetter,); + this.registerInclusionResolver('orderClasses', this.orderClasses.inclusionResolver); } } `; exports[ - `lb4 relation HasMany checks generated source class repository answers {"relationType":"hasMany","sourceModel":"CustomerClassType","destinationModel":"OrderClassType"} generates CustomerClassType repository file with different inputs 1` + `lb4 relation HasMany checks generated source class repository answers {"relationType":"hasMany","sourceModel":"CustomerClassType","destinationModel":"OrderClassType","registerInclusionResolver":false} generates CustomerClassType repository file with different inputs 1` ] = ` import {DefaultCrudRepository, repository, HasManyRepositoryFactory} from '@loopback/repository'; import {CustomerClassType, OrderClassType} from '../models'; @@ -447,7 +449,7 @@ export class Customer extends Entity { }) name?: string; - @hasMany(() => Order ,{keyTo: 'mykey'}) + @hasMany(() => Order, {keyTo: 'mykey'}) orders: Order[]; constructor(data?: Partial) { @@ -507,7 +509,7 @@ export class CustomerClass extends Entity { }) name?: string; - @hasMany(() => OrderClass ,{keyTo: 'mykey'}) + @hasMany(() => OrderClass, {keyTo: 'mykey'}) orderClasses: OrderClass[]; constructor(data?: Partial) { @@ -566,7 +568,7 @@ export class CustomerClassType extends Entity { }) name?: string; - @hasMany(() => OrderClassType ,{keyTo: 'mykey'}) + @hasMany(() => OrderClassType, {keyTo: 'mykey'}) orderClassTypes: OrderClassType[]; constructor(data?: Partial) { @@ -686,7 +688,7 @@ export class CustomerClass extends Entity { }) name?: string; - @hasMany(() => OrderClass ,{keyTo: 'customerClassCustNumber'}) + @hasMany(() => OrderClass, {keyTo: 'customerClassCustNumber'}) myOrders: OrderClass[]; constructor(data?: Partial) { @@ -745,7 +747,7 @@ export class CustomerClassType extends Entity { }) name?: string; - @hasMany(() => OrderClassType ,{keyTo: 'customerClassTypeCustNumber'}) + @hasMany(() => OrderClassType, {keyTo: 'customerClassTypeCustNumber'}) myOrders: OrderClassType[]; constructor(data?: Partial) { @@ -834,7 +836,7 @@ export class CustomerClass extends Entity { }) name?: string; - @hasMany(() => OrderClass ,{keyTo: 'customerClassCustNumber'}) + @hasMany(() => OrderClass, {keyTo: 'customerClassCustNumber'}) orderClasses: OrderClass[]; constructor(data?: Partial) { @@ -863,7 +865,7 @@ export class CustomerClassType extends Entity { }) name?: string; - @hasMany(() => OrderClassType ,{keyTo: 'customerClassTypeCustNumber'}) + @hasMany(() => OrderClassType, {keyTo: 'customerClassTypeCustNumber'}) orderClassTypes: OrderClassType[]; constructor(data?: Partial) { diff --git a/packages/cli/test/integration/generators/relation.belongs-to.integration.js b/packages/cli/test/integration/generators/relation.belongs-to.integration.js index 47e61e9ad5e5..54b1e2cb519a 100644 --- a/packages/cli/test/integration/generators/relation.belongs-to.integration.js +++ b/packages/cli/test/integration/generators/relation.belongs-to.integration.js @@ -343,11 +343,13 @@ describe('lb4 relation', function() { relationType: 'belongsTo', sourceModel: 'OrderClass', destinationModel: 'CustomerClass', + registerInclusionResolver: true, }, { relationType: 'belongsTo', sourceModel: 'OrderClassType', destinationModel: 'CustomerClassType', + registerInclusionResolver: false, }, ]; diff --git a/packages/cli/test/integration/generators/relation.has-many.integration.js b/packages/cli/test/integration/generators/relation.has-many.integration.js index bcba038b0e75..d49e9041a1f8 100644 --- a/packages/cli/test/integration/generators/relation.has-many.integration.js +++ b/packages/cli/test/integration/generators/relation.has-many.integration.js @@ -338,11 +338,13 @@ describe('lb4 relation HasMany', function() { relationType: 'hasMany', sourceModel: 'CustomerClass', destinationModel: 'OrderClass', + registerInclusionResolver: true, }, { relationType: 'hasMany', sourceModel: 'CustomerClassType', destinationModel: 'OrderClassType', + registerInclusionResolver: false, }, ];