diff --git a/docs/site/Discovering-models.md b/docs/site/Discovering-models.md index 66b6eec9773a..a5064cb6a61d 100644 --- a/docs/site/Discovering-models.md +++ b/docs/site/Discovering-models.md @@ -39,6 +39,8 @@ Models can be discovered from a supported datasource by running the `--views`: Choose whether to discover views. Default is true +`--relations`: Choose whether to create relations. Default is false + `--all`: Skips the model prompt and discovers all of them `--outDir`: Specify the directory into which the `model.model.ts` files will be diff --git a/packages/cli/.yo-rc.json b/packages/cli/.yo-rc.json index f2beb46451cf..36ea32e39dc9 100644 --- a/packages/cli/.yo-rc.json +++ b/packages/cli/.yo-rc.json @@ -1251,6 +1251,13 @@ "name": "views", "hide": false }, + "relations": { + "type": "Boolean", + "description": "Discover and create relations", + "default": false, + "name": "relations", + "hide": false + }, "schema": { "type": "String", "description": "Schema to discover", diff --git a/packages/cli/generators/discover/index.js b/packages/cli/generators/discover/index.js index 9127afec5a72..5c9d7e4dedcf 100644 --- a/packages/cli/generators/discover/index.js +++ b/packages/cli/generators/discover/index.js @@ -31,6 +31,12 @@ module.exports = class DiscoveryGenerator extends ArtifactGenerator { default: true, }); + this.option('relations', { + type: Boolean, + description: g.f('Discover and create relations'), + default: false, + }); + this.option('schema', { type: String, description: g.f('Schema to discover'), @@ -289,6 +295,7 @@ module.exports = class DiscoveryGenerator extends ArtifactGenerator { { schema: modelInfo.owner, disableCamelCase: this.artifactInfo.disableCamelCase, + associations: this.options.relations, }, ); if (this.options.optionalId) { @@ -336,6 +343,46 @@ module.exports = class DiscoveryGenerator extends ArtifactGenerator { ); debug(`Writing: ${fullPath}`); + if (this.options.relations) { + const relationImports = []; + const relationDestinationImports = []; + const foreignKeys = {}; + for (const relationName in templateData.settings.relations) { + const relation = templateData.settings.relations[relationName]; + const targetModel = this.artifactInfo.modelDefinitions.find( + model => model.name === relation.model, + ); + // If targetModel is not in discovered models, skip creating relation + if (targetModel) { + Object.assign(templateData.properties[relation.foreignKey], { + relation, + }); + relationImports.push(relation.type); + relationDestinationImports.push(relation.model); + + foreignKeys[relationName] = {}; + Object.assign(foreignKeys[relationName], { + name: relationName, + entity: relation.model, + entityKey: Object.entries(targetModel.properties).find( + x => x?.[1].id === 1, + )?.[0], + foreignKey: relation.foreignKey, + }); + } + } + templateData.relationImports = relationImports; + templateData.relationDestinationImports = relationDestinationImports; + // Delete relation from modelSettings + delete templateData.settings.relations; + if (Object.keys(foreignKeys)?.length > 0) { + Object.assign(templateData.settings, {foreignKeys}); + } + templateData.modelSettings = utils.stringifyModelSettings( + templateData.settings, + ); + } + this.copyTemplatedFiles( modelDiscoverer.MODEL_TEMPLATE_PATH, fullPath, diff --git a/packages/cli/generators/model/templates/model.ts.ejs b/packages/cli/generators/model/templates/model.ts.ejs index 29ab455353aa..59c8e40815e5 100644 --- a/packages/cli/generators/model/templates/model.ts.ejs +++ b/packages/cli/generators/model/templates/model.ts.ejs @@ -1,9 +1,12 @@ <% if (isModelBaseBuiltin) { -%> -import {<%= modelBaseClass %>, model, property} from '@loopback/repository'; +import {<%= modelBaseClass %>, model, property<%if (locals.relationImports) {%><% relationImports.forEach((relation) => {-%>, <%= relation %> <%_})-%><%}%>} from '@loopback/repository'; <% } else { -%> -import {model, property} from '@loopback/repository'; +import {model, property<%if (locals.relationImports) {%><% relationImports.forEach((relation) => {-%> , <%= relation %> <%_})-%><%}%>} from '@loopback/repository'; import {<%= modelBaseClass %>} from '.'; <% } -%> +<%_ if (locals.relationDestinationImports && locals.relationDestinationImports.length > 0) { -%> +import {<% relationDestinationImports.forEach((model, index) => {-%><%= model %><%if (index!==relationDestinationImports.length-1) {%>,<% } %> <%_}) -%>} from '.'; +<%_ } -%> <% if (modelSettings) { -%> @model(<%- modelSettings %>) @@ -12,13 +15,17 @@ import {<%= modelBaseClass %>} from '.'; <% } -%> export class <%= className %> extends <%= modelBaseClass %> { <% Object.entries(properties).forEach(([key, val]) => { -%> +<% if (val.relation) { -%> + @<%= val.relation.type %>(() => <%= val.relation.model %>) +<% } else { -%> @property({ <%_ Object.entries(val).forEach(([propKey, propVal]) => { -%> - <%_ if (!['tsType'].includes(propKey)) { -%> + <%_ if (!['tsType', 'relation'].includes(propKey)) { -%> <%= propKey %>: <%- propVal %>, - <%_ } -%> + <%_ } -%> <%_ }) -%> }) +<% } -%> <%= key %><%if (!val.required) {%>?<% } %>: <%= val.tsType %>; <% }) -%> diff --git a/packages/cli/snapshots/integration/cli/cli.integration.snapshots.js b/packages/cli/snapshots/integration/cli/cli.integration.snapshots.js index 904f7b5a0322..902b96b7db2b 100644 --- a/packages/cli/snapshots/integration/cli/cli.integration.snapshots.js +++ b/packages/cli/snapshots/integration/cli/cli.integration.snapshots.js @@ -1309,6 +1309,13 @@ exports[`cli saves command metadata to .yo-rc.json 1`] = ` "name": "views", "hide": false }, + "relations": { + "type": "Boolean", + "description": "Discover and create relations", + "default": false, + "name": "relations", + "hide": false + }, "schema": { "type": "String", "description": "Schema to discover",