From af5969230a7f64ff2cf266afab99d2d23fd1c9ac Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 27 Jul 2022 02:29:23 -0300 Subject: [PATCH 1/4] feat(update): automatically identify pk and fks into repository layer #26 --- .../database/repositories/repositories.js | 27 ++++++++- .../data/repository/mysql/repository.ejs | 4 +- .../data/repository/postgres/repository.ejs | 4 +- .../data/repository/sqlserver/repository.ejs | 4 +- src/tests/repositories.test.js | 55 +++++++++++++++++++ 5 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 src/tests/repositories.test.js diff --git a/src/generators/src/infra/database/repositories/repositories.js b/src/generators/src/infra/database/repositories/repositories.js index 9a806a5..b3418af 100644 --- a/src/generators/src/infra/database/repositories/repositories.js +++ b/src/generators/src/infra/database/repositories/repositories.js @@ -3,29 +3,52 @@ const camelCase = require('lodash.camelcase') const fs = require('fs') const path = require('path') +const types = {String , Boolean, Number, undefined, Symbol, Object, Null: null} + +function generateForeignKeysField(schema) { + const idFieldsNames = {} + Object.values(schema).map(field => { + if (types[field.type.name]) return + + const { schema, name } = field.type.prototype.meta + + Object.values(schema).map(idFields => { + if(idFields.options.isId) + idFieldsNames[`${camelCase(name)}_${idFields.name}`] = idFields.type.name + }) + }) + return idFieldsNames +} + async function generateRepositories(generate, filesystem, db, command) { const requires = {} + let foreignKeysFields const herbarium = requireHerbarium(command, filesystem.cwd()) const entities = herbarium.entities.all for (const entity of Array.from(entities.values())) { - const { name } = entity.entity.prototype.meta + const { name, schema } = entity.entity.prototype.meta const lowCCName = camelCase(name) const repositoryPath = path.normalize(`${filesystem.cwd()}/src/infra/data/repositories/${lowCCName}Repository.js`) + const idField = Object.values(schema).find(({ options }) => options.isId) requires[`${lowCCName}Repository`] = `await new (require('./${lowCCName}Repository.js'))(conn)` if (fs.existsSync(repositoryPath)) continue + if (db !== 'mongo') foreignKeysFields = generateForeignKeysField(schema) + await generate({ template: `infra/data/repository/${db}/repository.ejs`, target: repositoryPath, props: { name: { pascalCase: name, - camelCase: lowCCName + camelCase: lowCCName, }, + primaryKeyField: idField.name, + foreignKeysFields, table: `${lowCCName}s` } }) diff --git a/src/templates/infra/data/repository/mysql/repository.ejs b/src/templates/infra/data/repository/mysql/repository.ejs index 370c46f..03b0856 100644 --- a/src/templates/infra/data/repository/mysql/repository.ejs +++ b/src/templates/infra/data/repository/mysql/repository.ejs @@ -8,7 +8,9 @@ class <%- props.name.pascalCase %>Repository extends Repository { super({ entity: <%- props.name.pascalCase %>, table: "<%- props.table %>", - knex: connection + ids: ['<%- props.primaryKeyField %>'], + knex: connection<%if(Object.values(props.foreignKeysFields).length) {%>, + foreignKeys: [{<% for (const [key, value] of Object.entries(props.foreignKeysFields)) { %> <%= key %>: <%= value %> ,<% } %>}]<% }%> }) } } diff --git a/src/templates/infra/data/repository/postgres/repository.ejs b/src/templates/infra/data/repository/postgres/repository.ejs index 370c46f..03b0856 100644 --- a/src/templates/infra/data/repository/postgres/repository.ejs +++ b/src/templates/infra/data/repository/postgres/repository.ejs @@ -8,7 +8,9 @@ class <%- props.name.pascalCase %>Repository extends Repository { super({ entity: <%- props.name.pascalCase %>, table: "<%- props.table %>", - knex: connection + ids: ['<%- props.primaryKeyField %>'], + knex: connection<%if(Object.values(props.foreignKeysFields).length) {%>, + foreignKeys: [{<% for (const [key, value] of Object.entries(props.foreignKeysFields)) { %> <%= key %>: <%= value %> ,<% } %>}]<% }%> }) } } diff --git a/src/templates/infra/data/repository/sqlserver/repository.ejs b/src/templates/infra/data/repository/sqlserver/repository.ejs index 370c46f..03b0856 100644 --- a/src/templates/infra/data/repository/sqlserver/repository.ejs +++ b/src/templates/infra/data/repository/sqlserver/repository.ejs @@ -8,7 +8,9 @@ class <%- props.name.pascalCase %>Repository extends Repository { super({ entity: <%- props.name.pascalCase %>, table: "<%- props.table %>", - knex: connection + ids: ['<%- props.primaryKeyField %>'], + knex: connection<%if(Object.values(props.foreignKeysFields).length) {%>, + foreignKeys: [{<% for (const [key, value] of Object.entries(props.foreignKeysFields)) { %> <%= key %>: <%= value %> ,<% } %>}]<% }%> }) } } diff --git a/src/tests/repositories.test.js b/src/tests/repositories.test.js new file mode 100644 index 0000000..a7f72b2 --- /dev/null +++ b/src/tests/repositories.test.js @@ -0,0 +1,55 @@ +/* globals describe, it, afterEach */ + +const { system } = require('gluegun') +const { expect } = require('chai') +const fs = require('fs') +const path = require('path') + +const projectName = 'herbs-test-runner' + +const linknpm = () => system.run(`cd bin && npm link --force`) + +const generateProject = () => system.run(`herbs new --name ${projectName} --description "testing the herbs CLI" --author herbs --license MIT --graphql --rest no --database postgres --npmInstall yes`) +const callHerbsCli = () => system.run(`herbs -v && herbs`) + +const herbsUpdate = () => system.run(`cd ${projectName} && herbs update`) + +describe('When I generate a complete project that uses postgres', () => { + + it('must link npm herbs', async () => { + await linknpm() + await callHerbsCli() + }) + + it('Should I create a new entity with foreign key', async () => { + await generateProject() + const customerEntity = + ` + const { entity, id, field } = require('@herbsjs/herbs') + const { herbarium } = require('@herbsjs/herbarium') + const User = require('./user') + + const Customer = + entity('Customer', { + id: id(String), + description: field(String), + user: field(User) + }) + + module.exports = + herbarium.entities + .add(Customer, 'Customer') + .entity + ` + + const dir = `${path.resolve(process.cwd(), `${projectName}/src/domain/entities/customer.js`)}` + fs.writeFileSync(dir, customerEntity) + expect(fs.existsSync(dir)).to.be.true + }) + + it('I hope to have a repository file for Customer entity after running Herbs Update', async () => { + await herbsUpdate() + expect(fs.existsSync(path.resolve(process.cwd(), `${projectName}/src/infra/data/repositories/customerRepository.js`))).to.be.true + }) +}) + From b6d47af1a2867b6cbf3d81417eb8d22ccc672c21 Mon Sep 17 00:00:00 2001 From: danielsous Date: Wed, 27 Jul 2022 02:42:07 -0300 Subject: [PATCH 2/4] Remove unecessary import --- src/tests/shell.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tests/shell.test.js b/src/tests/shell.test.js index 7beb296..040fed4 100644 --- a/src/tests/shell.test.js +++ b/src/tests/shell.test.js @@ -6,7 +6,6 @@ const path = require('path') const fs = require('fs') const { expect } = require('chai') const projectName = 'herbs-test-runner' -const { exec } = require('node:child_process') const linknpm = () => system.run(`cd bin && npm link --force`) const generateProject = () => system.run(`herbs new --name ${projectName} --description "testing the herbs CLI" --author herbs --license MIT --graphql --rest --database mongo --npmInstall yes`) From bc524682b4846f95d6afd15368ea340e47d06afd Mon Sep 17 00:00:00 2001 From: danielsous Date: Wed, 27 Jul 2022 02:55:46 -0300 Subject: [PATCH 3/4] adjust test --- src/tests/repositories.test.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/tests/repositories.test.js b/src/tests/repositories.test.js index a7f72b2..74836b4 100644 --- a/src/tests/repositories.test.js +++ b/src/tests/repositories.test.js @@ -7,22 +7,19 @@ const path = require('path') const projectName = 'herbs-test-runner' -const linknpm = () => system.run(`cd bin && npm link --force`) - const generateProject = () => system.run(`herbs new --name ${projectName} --description "testing the herbs CLI" --author herbs --license MIT --graphql --rest no --database postgres --npmInstall yes`) -const callHerbsCli = () => system.run(`herbs -v && herbs`) +const callHerbsCli = () => system.run(`herbs`) const herbsUpdate = () => system.run(`cd ${projectName} && herbs update`) describe('When I generate a complete project that uses postgres', () => { - - it('must link npm herbs', async () => { - await linknpm() - await callHerbsCli() + afterEach(async () => { + fs.rmSync(path.resolve(process.cwd(), `${projectName}`), { recursive: true }) }) - it('Should I create a new entity with foreign key', async () => { + it('Should I create a new repository with foreign key', async () => { await generateProject() + await callHerbsCli() const customerEntity = ` const { entity, id, field } = require('@herbsjs/herbs') @@ -45,9 +42,6 @@ describe('When I generate a complete project that uses postgres', () => { const dir = `${path.resolve(process.cwd(), `${projectName}/src/domain/entities/customer.js`)}` fs.writeFileSync(dir, customerEntity) expect(fs.existsSync(dir)).to.be.true - }) - - it('I hope to have a repository file for Customer entity after running Herbs Update', async () => { await herbsUpdate() expect(fs.existsSync(path.resolve(process.cwd(), `${projectName}/src/infra/data/repositories/customerRepository.js`))).to.be.true }) From d9e29bc6e058aa0e6ce73b7eb072d746358d0790 Mon Sep 17 00:00:00 2001 From: danielsous Date: Thu, 4 Aug 2022 15:53:11 -0300 Subject: [PATCH 4/4] feat(repositories): add automatically other PK and adjust layout --- .../src/infra/database/repositories/repositories.js | 11 +++++++---- .../infra/data/repository/mysql/repository.ejs | 4 ++-- .../infra/data/repository/postgres/repository.ejs | 4 ++-- .../infra/data/repository/sqlserver/repository.ejs | 4 ++-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/generators/src/infra/database/repositories/repositories.js b/src/generators/src/infra/database/repositories/repositories.js index b3418af..d5bde5b 100644 --- a/src/generators/src/infra/database/repositories/repositories.js +++ b/src/generators/src/infra/database/repositories/repositories.js @@ -22,8 +22,6 @@ function generateForeignKeysField(schema) { async function generateRepositories(generate, filesystem, db, command) { const requires = {} - let foreignKeysFields - const herbarium = requireHerbarium(command, filesystem.cwd()) const entities = herbarium.entities.all @@ -31,7 +29,12 @@ async function generateRepositories(generate, filesystem, db, command) { const { name, schema } = entity.entity.prototype.meta const lowCCName = camelCase(name) const repositoryPath = path.normalize(`${filesystem.cwd()}/src/infra/data/repositories/${lowCCName}Repository.js`) - const idField = Object.values(schema).find(({ options }) => options.isId) + let foreignKeysFields + const primaryKeyFields = [] + Object.values(schema).filter(idFields => { + if(idFields.options.isId) + primaryKeyFields.push(idFields.name) + }) requires[`${lowCCName}Repository`] = `await new (require('./${lowCCName}Repository.js'))(conn)` @@ -47,7 +50,7 @@ async function generateRepositories(generate, filesystem, db, command) { pascalCase: name, camelCase: lowCCName, }, - primaryKeyField: idField.name, + primaryKeyFields, foreignKeysFields, table: `${lowCCName}s` } diff --git a/src/templates/infra/data/repository/mysql/repository.ejs b/src/templates/infra/data/repository/mysql/repository.ejs index 03b0856..e8ecb62 100644 --- a/src/templates/infra/data/repository/mysql/repository.ejs +++ b/src/templates/infra/data/repository/mysql/repository.ejs @@ -8,9 +8,9 @@ class <%- props.name.pascalCase %>Repository extends Repository { super({ entity: <%- props.name.pascalCase %>, table: "<%- props.table %>", - ids: ['<%- props.primaryKeyField %>'], + ids: [<% for (const [index, value] of Object.entries(props.primaryKeyFields)) { %><%if(index != 0) { %>, <% } %>'<%= value %>'<% } %>], knex: connection<%if(Object.values(props.foreignKeysFields).length) {%>, - foreignKeys: [{<% for (const [key, value] of Object.entries(props.foreignKeysFields)) { %> <%= key %>: <%= value %> ,<% } %>}]<% }%> + foreignKeys: [{ <% Object.entries(props.foreignKeysFields).forEach(([key, value], index) => {%><%if(index != 0) { %>, <% } %><%= key %>: <%= value %><% }) %> }]<% }%> }) } } diff --git a/src/templates/infra/data/repository/postgres/repository.ejs b/src/templates/infra/data/repository/postgres/repository.ejs index 03b0856..e8ecb62 100644 --- a/src/templates/infra/data/repository/postgres/repository.ejs +++ b/src/templates/infra/data/repository/postgres/repository.ejs @@ -8,9 +8,9 @@ class <%- props.name.pascalCase %>Repository extends Repository { super({ entity: <%- props.name.pascalCase %>, table: "<%- props.table %>", - ids: ['<%- props.primaryKeyField %>'], + ids: [<% for (const [index, value] of Object.entries(props.primaryKeyFields)) { %><%if(index != 0) { %>, <% } %>'<%= value %>'<% } %>], knex: connection<%if(Object.values(props.foreignKeysFields).length) {%>, - foreignKeys: [{<% for (const [key, value] of Object.entries(props.foreignKeysFields)) { %> <%= key %>: <%= value %> ,<% } %>}]<% }%> + foreignKeys: [{ <% Object.entries(props.foreignKeysFields).forEach(([key, value], index) => {%><%if(index != 0) { %>, <% } %><%= key %>: <%= value %><% }) %> }]<% }%> }) } } diff --git a/src/templates/infra/data/repository/sqlserver/repository.ejs b/src/templates/infra/data/repository/sqlserver/repository.ejs index 03b0856..e8ecb62 100644 --- a/src/templates/infra/data/repository/sqlserver/repository.ejs +++ b/src/templates/infra/data/repository/sqlserver/repository.ejs @@ -8,9 +8,9 @@ class <%- props.name.pascalCase %>Repository extends Repository { super({ entity: <%- props.name.pascalCase %>, table: "<%- props.table %>", - ids: ['<%- props.primaryKeyField %>'], + ids: [<% for (const [index, value] of Object.entries(props.primaryKeyFields)) { %><%if(index != 0) { %>, <% } %>'<%= value %>'<% } %>], knex: connection<%if(Object.values(props.foreignKeysFields).length) {%>, - foreignKeys: [{<% for (const [key, value] of Object.entries(props.foreignKeysFields)) { %> <%= key %>: <%= value %> ,<% } %>}]<% }%> + foreignKeys: [{ <% Object.entries(props.foreignKeysFields).forEach(([key, value], index) => {%><%if(index != 0) { %>, <% } %><%= key %>: <%= value %><% }) %> }]<% }%> }) } }