From 8e7324684f0fcd25c0a69bf83d705889763a8c70 Mon Sep 17 00:00:00 2001 From: devalexandre Date: Sun, 3 Apr 2022 10:50:20 -0300 Subject: [PATCH 1/4] feat: add create mixin --- src/create/index.js | 9 +- src/create/mixin/index.js | 124 ++++++++++++++++++ src/create/mixin/moleculer-db.mixin.template | 86 ++++++++++++ .../moleculer-db.typescript.mixin.template | 103 +++++++++++++++ test/e2e/create/mixin/create.spec.js | 99 ++++++++++++++ test/e2e/create/mixin/create_answers.json | 4 + test/e2e/create/mixin/create_answers_ts.json | 6 + test/e2e/create/mixin/mocks/ola.mixin.js | 86 ++++++++++++ test/e2e/create/mixin/mocks/ola.mixin.ts | 103 +++++++++++++++ test/e2e/create/mixin/ola.mixin.ts | 103 +++++++++++++++ 10 files changed, 721 insertions(+), 2 deletions(-) create mode 100644 src/create/mixin/index.js create mode 100644 src/create/mixin/moleculer-db.mixin.template create mode 100644 src/create/mixin/moleculer-db.typescript.mixin.template create mode 100644 test/e2e/create/mixin/create.spec.js create mode 100644 test/e2e/create/mixin/create_answers.json create mode 100644 test/e2e/create/mixin/create_answers_ts.json create mode 100644 test/e2e/create/mixin/mocks/ola.mixin.js create mode 100644 test/e2e/create/mixin/mocks/ola.mixin.ts create mode 100644 test/e2e/create/mixin/ola.mixin.ts diff --git a/src/create/index.js b/src/create/index.js index 152431c..6c9b458 100644 --- a/src/create/index.js +++ b/src/create/index.js @@ -6,17 +6,19 @@ const addService = require("./service"); +const addMixin = require("./mixin"); /** * Yargs command */ module.exports = { command: ["create", "", ""], - describe: `Create a Moleculer service `, + describe: `Create a Moleculer service or mixin `, + builder(yargs) { yargs.options({ typescript: { - describe: "Create service for typescript", + describe: "Create typescript file", type: "boolean", default: false, }, @@ -27,6 +29,9 @@ module.exports = { switch (fileType) { case "service": return addService(opts); + + case "mixin": + return addMixin(opts); } }, }; diff --git a/src/create/mixin/index.js b/src/create/mixin/index.js new file mode 100644 index 0000000..30a4a5d --- /dev/null +++ b/src/create/mixin/index.js @@ -0,0 +1,124 @@ +/* + * moleculer-cli + * Copyright (c) 2021 MoleculerJS (https://github.com/moleculerjs/moleculer-cli) + * MIT Licensed + */ + +const fs = require("fs"); +const path = require("path"); +const inquirer = require("inquirer"); +const render = require("consolidate").handlebars.render; +const ui = new inquirer.ui.BottomBar(); + +const { fail } = require("../../utils"); + +module.exports = async (opts) => { + const values = Object.assign({}, opts); + const _typescript = values.typescript ? true : false; + const name = opts._[2]; + + return ( + Promise.resolve() + .then(() => { + const answers_options = [ + { + type: "input", + name: "mixinFolder", + message: "Mixin directory", + default: "./mixins", + async validate(input) { + if (!fs.existsSync(path.resolve(input))) { + ui.log.write(`The ${input} doesn't exists!`); + fail("Aborted"); + } + return true; + }, + }, + ]; + + if (!name) + answers_options.push({ + type: "input", + name: "mixinName", + message: "Mixin name", + default: "test", + }); + + return inquirer.prompt(answers_options).then((answers) => { + answers.name = answers.mixinName; + answers.mixinName = answers.mixinName || name; + answers.mixinName = answers.mixinName.replace( + /[^\w\s]/gi, + "-" + ); + + answers.className = answers.mixinName + .replace(/(\w)(\w*)/g, function (g0, g1, g2) { + return g1.toUpperCase() + g2.toLowerCase(); + }) + .replace(/[^\w\s]/gi, ""); + + Object.assign(values, answers); + const { mixinFolder, mixinName } = values; + const file_name = `${mixinName.toLowerCase()}.mixin${ + _typescript ? ".ts" : ".js" + }`; + const newMixinPath = path.join( + mixinFolder, + `${mixinName.toLowerCase()}.mixin${ + _typescript ? ".ts" : ".js" + }` + ); + + if (fs.existsSync(newMixinPath)) { + return inquirer + .prompt([ + { + type: "confirm", + name: "sure", + message: `The file ${file_name} already exists! Do you want to overwrite it?`, + default: false, + }, + ]) + .then(({ sure }) => { + if (!sure) fail("Aborted"); + }); + } + }); + }) + .then(() => { + const templatePath = _typescript + ? path.join( + __dirname, + "moleculer-db.typescript.mixin.template" + ) + : path.join(__dirname, "moleculer-db.mixin.template"); + const template = fs.readFileSync(templatePath, "utf8"); + return new Promise((resolve, reject) => { + render(template, values, async function (err, res) { + if (err) return reject(err); + + const { mixinFolder, mixinName } = values; + const newMixinPath = path.join( + mixinFolder, + `${mixinName.toLowerCase()}.mixin${ + _typescript ? ".ts" : ".js" + }` + ); + + fs.writeFileSync( + path.resolve(`${newMixinPath}`), + res, + "utf8" + ); + + resolve(); + }); + }); + }) + + // Error handler + .catch((err) => fail(err)) + ); +}; + diff --git a/src/create/mixin/moleculer-db.mixin.template b/src/create/mixin/moleculer-db.mixin.template new file mode 100644 index 0000000..829c63b --- /dev/null +++ b/src/create/mixin/moleculer-db.mixin.template @@ -0,0 +1,86 @@ +"use strict"; + +const fs = require("fs"); +const DbService = require("moleculer-db"); + +/** + * @typedef {import('moleculer').Context} Context Moleculer's Context + */ + +module.exports = function (collection) { + const cacheCleanEventName = `cache.clean.${collection}`; + + const schema = { + mixins: [DbService], + + events: { + /** + * Subscribe to the cache clean event. If it's triggered + * clean the cache entries for this service. + * + * @param {Context} ctx + */ + async [cacheCleanEventName]() { + if (this.broker.cacher) { + await this.broker.cacher.clean(`${this.fullName}.*`); + } + }, + }, + + methods: { + /** + * Send a cache clearing event when an entity changed. + * + * @param {String} type + * @param {any} json + * @param {Context} ctx + */ + async entityChanged(type, json, ctx) { + ctx.broadcast(cacheCleanEventName); + }, + }, + + async started() { + // Check the count of items in the DB. If it's empty, + // call the `seedDB` method of the service. + if (this.seedDB) { + const count = await this.adapter.count(); + if (count == 0) { + this.logger.info( + `The '${collection}' collection is empty. Seeding the collection...` + ); + await this.seedDB(); + this.logger.info( + "Seeding is done. Number of records:", + await this.adapter.count() + ); + } + } + }, + }; + + if (process.env.MONGO_URI) { + // Mongo adapter + const MongoAdapter = require("moleculer-db-adapter-mongo"); + + schema.adapter = new MongoAdapter(process.env.MONGO_URI); + schema.collection = collection; + } else if (process.env.NODE_ENV === "test") { + // NeDB memory adapter for testing + schema.adapter = new DbService.MemoryAdapter(); + } else { + // NeDB file DB adapter + + // Create data folder + if (!fs.existsSync("./data")) { + fs.mkdirSync("./data"); + } + + schema.adapter = new DbService.MemoryAdapter({ + filename: `./data/${collection}.db`, + }); + } + + return schema; +}; + diff --git a/src/create/mixin/moleculer-db.typescript.mixin.template b/src/create/mixin/moleculer-db.typescript.mixin.template new file mode 100644 index 0000000..6462fd4 --- /dev/null +++ b/src/create/mixin/moleculer-db.typescript.mixin.template @@ -0,0 +1,103 @@ +"use strict"; + +import { existsSync } from "fs"; +import { sync } from "mkdirp"; +import { Context, Service, ServiceSchema } from "moleculer"; +import DbService from "moleculer-db"; + +export default class {{className}}Connection + implements Partial, ThisType +{ + private cacheCleanEventName: string; + private collection: string; + private schema: Partial & ThisType; + + public constructor(public collectionName: string) { + this.collection = collectionName; + this.cacheCleanEventName = `cache.clean.${this.collection}`; + this.schema = { + mixins: [DbService], + events: { + /** + * Subscribe to the cache clean event. If it's triggered + * clean the cache entries for this service. + * + */ + async [this.cacheCleanEventName]() { + if (this.broker.cacher) { + await this.broker.cacher.clean(`${this.fullName}.*`); + } + }, + }, + methods: { + /** + * Send a cache clearing event when an entity changed. + * + * @param {String} type + * @param {any} json + * @param {Context} ctx + */ + entityChanged: async ( + type: string, + json: any, + ctx: Context + ) => { + await ctx.broadcast(this.cacheCleanEventName); + }, + }, + async started() { + // Check the count of items in the DB. If it's empty, + // Call the `seedDB` method of the service. + if (this.seedDB) { + const count = await this.adapter.count(); + if (count === 0) { + this.logger.info( + `The '${this.collection}' collection is empty. Seeding the collection...` + ); + await this.seedDB(); + this.logger.info( + "Seeding is done. Number of records:", + await this.adapter.count() + ); + } + } + }, + }; + } + + public start() { + if (process.env.MONGO_URI) { + // Mongo adapter + // eslint-disable-next-line @typescript-eslint/no-var-requires + const MongoAdapter = require("moleculer-db-adapter-mongo"); + this.schema.adapter = new MongoAdapter(process.env.MONGO_URI); + this.schema.collection = this.collection; + } else if (process.env.NODE_ENV === "test") { + // NeDB memory adapter for testing + // @ts-ignore + this.schema.adapter = new DbService.MemoryAdapter(); + } else { + // NeDB file DB adapter + + // Create data folder + if (!existsSync("./data")) { + sync("./data"); + } + // @ts-ignore + this.schema.adapter = new DbService.MemoryAdapter({ + filename: `./data/${this.collection}.db`, + }); + } + + return this.schema; + } + + public get _collection(): string { + return this.collection; + } + + public set _collection(value: string) { + this.collection = value; + } +} + diff --git a/test/e2e/create/mixin/create.spec.js b/test/e2e/create/mixin/create.spec.js new file mode 100644 index 0000000..a847613 --- /dev/null +++ b/test/e2e/create/mixin/create.spec.js @@ -0,0 +1,99 @@ +const YargsPromise = require("yargs-promise"); +const yargs = require("yargs"); +const inquirer = require("inquirer"); +const path = require("path"); +const fs = require("fs"); +const create = require("../../../../src/create"); +const answers = require("./create_answers.json"); +const answers_ts = require("./create_answers_ts.json"); +const tmp = path.resolve(__dirname, "../../../../tmp"); + +describe("test create", () => { + beforeAll(() => { + if (!fs.existsSync(tmp)) { + fs.mkdirSync(tmp,{mode: 0o777}); + } + }); + afterEach(() => { + jest.clearAllMocks(); + }); + + afterAll(() => { + fs.rmdirSync(tmp, {recursive: true}); + }); + + it("create js mixin", () => { + const _path = `../../../../${answers.mixinFolder}/${answers.mixinName}.mixin.js`; + const pathAbsoluteFile = path.resolve(__dirname, _path); + const mockFileAbsolute = path.resolve( + __dirname, + `./mocks/${answers.mixinName}.mixin.js` + ); + + jest.mock("inquirer"); + inquirer.prompt = jest.fn().mockResolvedValue(answers); + yargs + .usage("Usage: $0 [options]") + .version() + .command(create) + .help().argv; + const parser = new YargsPromise(yargs); + return parser + .parse(`create mixin ${answers.mixinName}`) + .then(({ data, argv }) => { + if (!fs.existsSync(pathAbsoluteFile)) { + throw new Error("file not exist"); + } + + expect(fs.existsSync(pathAbsoluteFile)).toBeTruthy(); + expect(fs.readFileSync(pathAbsoluteFile)).toEqual( + fs.readFileSync(mockFileAbsolute) + ); + + fs.unlinkSync(pathAbsoluteFile); + }) + .catch(({ error, argv }) => { + throw new Error(error); + }); + }); + + it("create ts mixin", () => { + const _path = `../../../../${answers.mixinFolder}/${answers.mixinName}.mixin.ts`; + const pathAbsoluteFile = path.resolve(__dirname, _path); + const mockFileAbsolute = path.resolve( + __dirname, + `./mocks/${answers.mixinName}.mixin.ts` + ); + + jest.mock("inquirer"); + inquirer.prompt = jest.fn().mockResolvedValue(answers); + yargs + .usage("Usage: $0 [options]") + .version() + .command(create) + .default("--typescript", true) + .help().argv; + const parser = new YargsPromise(yargs); + return parser + .parse(`create mixin ${answers_ts.mixinName}`) + .then(({ data, argv }) => { + if (!fs.existsSync(pathAbsoluteFile)) { + fs.unlinkSync(pathAbsoluteFile); + throw new Error("file not exist"); + } + + expect(fs.existsSync(pathAbsoluteFile)).toBeTruthy(); + expect(fs.readFileSync(pathAbsoluteFile)).toEqual( + fs.readFileSync(mockFileAbsolute) + ); + fs.unlinkSync(pathAbsoluteFile); + + }) + .catch(({ error, argv }) => { + fs.unlinkSync(pathAbsoluteFile); + + throw new Error(error); + }); + }); +}); + diff --git a/test/e2e/create/mixin/create_answers.json b/test/e2e/create/mixin/create_answers.json new file mode 100644 index 0000000..4ea4d18 --- /dev/null +++ b/test/e2e/create/mixin/create_answers.json @@ -0,0 +1,4 @@ +{ + "mixinFolder": "tmp", + "mixinName": "ola" +} diff --git a/test/e2e/create/mixin/create_answers_ts.json b/test/e2e/create/mixin/create_answers_ts.json new file mode 100644 index 0000000..5c732b3 --- /dev/null +++ b/test/e2e/create/mixin/create_answers_ts.json @@ -0,0 +1,6 @@ +{ + "mixinFolder": "tmp", + "mixinName": "ola", + "typescript": true +} + diff --git a/test/e2e/create/mixin/mocks/ola.mixin.js b/test/e2e/create/mixin/mocks/ola.mixin.js new file mode 100644 index 0000000..829c63b --- /dev/null +++ b/test/e2e/create/mixin/mocks/ola.mixin.js @@ -0,0 +1,86 @@ +"use strict"; + +const fs = require("fs"); +const DbService = require("moleculer-db"); + +/** + * @typedef {import('moleculer').Context} Context Moleculer's Context + */ + +module.exports = function (collection) { + const cacheCleanEventName = `cache.clean.${collection}`; + + const schema = { + mixins: [DbService], + + events: { + /** + * Subscribe to the cache clean event. If it's triggered + * clean the cache entries for this service. + * + * @param {Context} ctx + */ + async [cacheCleanEventName]() { + if (this.broker.cacher) { + await this.broker.cacher.clean(`${this.fullName}.*`); + } + }, + }, + + methods: { + /** + * Send a cache clearing event when an entity changed. + * + * @param {String} type + * @param {any} json + * @param {Context} ctx + */ + async entityChanged(type, json, ctx) { + ctx.broadcast(cacheCleanEventName); + }, + }, + + async started() { + // Check the count of items in the DB. If it's empty, + // call the `seedDB` method of the service. + if (this.seedDB) { + const count = await this.adapter.count(); + if (count == 0) { + this.logger.info( + `The '${collection}' collection is empty. Seeding the collection...` + ); + await this.seedDB(); + this.logger.info( + "Seeding is done. Number of records:", + await this.adapter.count() + ); + } + } + }, + }; + + if (process.env.MONGO_URI) { + // Mongo adapter + const MongoAdapter = require("moleculer-db-adapter-mongo"); + + schema.adapter = new MongoAdapter(process.env.MONGO_URI); + schema.collection = collection; + } else if (process.env.NODE_ENV === "test") { + // NeDB memory adapter for testing + schema.adapter = new DbService.MemoryAdapter(); + } else { + // NeDB file DB adapter + + // Create data folder + if (!fs.existsSync("./data")) { + fs.mkdirSync("./data"); + } + + schema.adapter = new DbService.MemoryAdapter({ + filename: `./data/${collection}.db`, + }); + } + + return schema; +}; + diff --git a/test/e2e/create/mixin/mocks/ola.mixin.ts b/test/e2e/create/mixin/mocks/ola.mixin.ts new file mode 100644 index 0000000..afbde74 --- /dev/null +++ b/test/e2e/create/mixin/mocks/ola.mixin.ts @@ -0,0 +1,103 @@ +"use strict"; + +import { existsSync } from "fs"; +import { sync } from "mkdirp"; +import { Context, Service, ServiceSchema } from "moleculer"; +import DbService from "moleculer-db"; + +export default class OlaConnection + implements Partial, ThisType +{ + private cacheCleanEventName: string; + private collection: string; + private schema: Partial & ThisType; + + public constructor(public collectionName: string) { + this.collection = collectionName; + this.cacheCleanEventName = `cache.clean.${this.collection}`; + this.schema = { + mixins: [DbService], + events: { + /** + * Subscribe to the cache clean event. If it's triggered + * clean the cache entries for this service. + * + */ + async [this.cacheCleanEventName]() { + if (this.broker.cacher) { + await this.broker.cacher.clean(`${this.fullName}.*`); + } + }, + }, + methods: { + /** + * Send a cache clearing event when an entity changed. + * + * @param {String} type + * @param {any} json + * @param {Context} ctx + */ + entityChanged: async ( + type: string, + json: any, + ctx: Context + ) => { + await ctx.broadcast(this.cacheCleanEventName); + }, + }, + async started() { + // Check the count of items in the DB. If it's empty, + // Call the `seedDB` method of the service. + if (this.seedDB) { + const count = await this.adapter.count(); + if (count === 0) { + this.logger.info( + `The '${this.collection}' collection is empty. Seeding the collection...` + ); + await this.seedDB(); + this.logger.info( + "Seeding is done. Number of records:", + await this.adapter.count() + ); + } + } + }, + }; + } + + public start() { + if (process.env.MONGO_URI) { + // Mongo adapter + // eslint-disable-next-line @typescript-eslint/no-var-requires + const MongoAdapter = require("moleculer-db-adapter-mongo"); + this.schema.adapter = new MongoAdapter(process.env.MONGO_URI); + this.schema.collection = this.collection; + } else if (process.env.NODE_ENV === "test") { + // NeDB memory adapter for testing + // @ts-ignore + this.schema.adapter = new DbService.MemoryAdapter(); + } else { + // NeDB file DB adapter + + // Create data folder + if (!existsSync("./data")) { + sync("./data"); + } + // @ts-ignore + this.schema.adapter = new DbService.MemoryAdapter({ + filename: `./data/${this.collection}.db`, + }); + } + + return this.schema; + } + + public get _collection(): string { + return this.collection; + } + + public set _collection(value: string) { + this.collection = value; + } +} + diff --git a/test/e2e/create/mixin/ola.mixin.ts b/test/e2e/create/mixin/ola.mixin.ts new file mode 100644 index 0000000..afbde74 --- /dev/null +++ b/test/e2e/create/mixin/ola.mixin.ts @@ -0,0 +1,103 @@ +"use strict"; + +import { existsSync } from "fs"; +import { sync } from "mkdirp"; +import { Context, Service, ServiceSchema } from "moleculer"; +import DbService from "moleculer-db"; + +export default class OlaConnection + implements Partial, ThisType +{ + private cacheCleanEventName: string; + private collection: string; + private schema: Partial & ThisType; + + public constructor(public collectionName: string) { + this.collection = collectionName; + this.cacheCleanEventName = `cache.clean.${this.collection}`; + this.schema = { + mixins: [DbService], + events: { + /** + * Subscribe to the cache clean event. If it's triggered + * clean the cache entries for this service. + * + */ + async [this.cacheCleanEventName]() { + if (this.broker.cacher) { + await this.broker.cacher.clean(`${this.fullName}.*`); + } + }, + }, + methods: { + /** + * Send a cache clearing event when an entity changed. + * + * @param {String} type + * @param {any} json + * @param {Context} ctx + */ + entityChanged: async ( + type: string, + json: any, + ctx: Context + ) => { + await ctx.broadcast(this.cacheCleanEventName); + }, + }, + async started() { + // Check the count of items in the DB. If it's empty, + // Call the `seedDB` method of the service. + if (this.seedDB) { + const count = await this.adapter.count(); + if (count === 0) { + this.logger.info( + `The '${this.collection}' collection is empty. Seeding the collection...` + ); + await this.seedDB(); + this.logger.info( + "Seeding is done. Number of records:", + await this.adapter.count() + ); + } + } + }, + }; + } + + public start() { + if (process.env.MONGO_URI) { + // Mongo adapter + // eslint-disable-next-line @typescript-eslint/no-var-requires + const MongoAdapter = require("moleculer-db-adapter-mongo"); + this.schema.adapter = new MongoAdapter(process.env.MONGO_URI); + this.schema.collection = this.collection; + } else if (process.env.NODE_ENV === "test") { + // NeDB memory adapter for testing + // @ts-ignore + this.schema.adapter = new DbService.MemoryAdapter(); + } else { + // NeDB file DB adapter + + // Create data folder + if (!existsSync("./data")) { + sync("./data"); + } + // @ts-ignore + this.schema.adapter = new DbService.MemoryAdapter({ + filename: `./data/${this.collection}.db`, + }); + } + + return this.schema; + } + + public get _collection(): string { + return this.collection; + } + + public set _collection(value: string) { + this.collection = value; + } +} + From e017daec075f655a77f8af23a8e5b3f1b0a78ed3 Mon Sep 17 00:00:00 2001 From: devalexandre Date: Sun, 3 Apr 2022 10:50:43 -0300 Subject: [PATCH 2/4] feat: add create mixin --- test/e2e/create/mixin/ola.mixin.ts | 103 ----------------------------- 1 file changed, 103 deletions(-) delete mode 100644 test/e2e/create/mixin/ola.mixin.ts diff --git a/test/e2e/create/mixin/ola.mixin.ts b/test/e2e/create/mixin/ola.mixin.ts deleted file mode 100644 index afbde74..0000000 --- a/test/e2e/create/mixin/ola.mixin.ts +++ /dev/null @@ -1,103 +0,0 @@ -"use strict"; - -import { existsSync } from "fs"; -import { sync } from "mkdirp"; -import { Context, Service, ServiceSchema } from "moleculer"; -import DbService from "moleculer-db"; - -export default class OlaConnection - implements Partial, ThisType -{ - private cacheCleanEventName: string; - private collection: string; - private schema: Partial & ThisType; - - public constructor(public collectionName: string) { - this.collection = collectionName; - this.cacheCleanEventName = `cache.clean.${this.collection}`; - this.schema = { - mixins: [DbService], - events: { - /** - * Subscribe to the cache clean event. If it's triggered - * clean the cache entries for this service. - * - */ - async [this.cacheCleanEventName]() { - if (this.broker.cacher) { - await this.broker.cacher.clean(`${this.fullName}.*`); - } - }, - }, - methods: { - /** - * Send a cache clearing event when an entity changed. - * - * @param {String} type - * @param {any} json - * @param {Context} ctx - */ - entityChanged: async ( - type: string, - json: any, - ctx: Context - ) => { - await ctx.broadcast(this.cacheCleanEventName); - }, - }, - async started() { - // Check the count of items in the DB. If it's empty, - // Call the `seedDB` method of the service. - if (this.seedDB) { - const count = await this.adapter.count(); - if (count === 0) { - this.logger.info( - `The '${this.collection}' collection is empty. Seeding the collection...` - ); - await this.seedDB(); - this.logger.info( - "Seeding is done. Number of records:", - await this.adapter.count() - ); - } - } - }, - }; - } - - public start() { - if (process.env.MONGO_URI) { - // Mongo adapter - // eslint-disable-next-line @typescript-eslint/no-var-requires - const MongoAdapter = require("moleculer-db-adapter-mongo"); - this.schema.adapter = new MongoAdapter(process.env.MONGO_URI); - this.schema.collection = this.collection; - } else if (process.env.NODE_ENV === "test") { - // NeDB memory adapter for testing - // @ts-ignore - this.schema.adapter = new DbService.MemoryAdapter(); - } else { - // NeDB file DB adapter - - // Create data folder - if (!existsSync("./data")) { - sync("./data"); - } - // @ts-ignore - this.schema.adapter = new DbService.MemoryAdapter({ - filename: `./data/${this.collection}.db`, - }); - } - - return this.schema; - } - - public get _collection(): string { - return this.collection; - } - - public set _collection(value: string) { - this.collection = value; - } -} - From 3ed98c9a13da76acd3d1d6795c60f9867cdf42c5 Mon Sep 17 00:00:00 2001 From: devalexandre Date: Sat, 9 Apr 2022 12:47:25 -0300 Subject: [PATCH 3/4] feat: add mixin, js template --- src/create/mixin/moleculer-db.mixin.template | 116 ++++++++----------- test/e2e/create/mixin/mocks/ola.mixin.js | 116 ++++++++----------- 2 files changed, 98 insertions(+), 134 deletions(-) diff --git a/src/create/mixin/moleculer-db.mixin.template b/src/create/mixin/moleculer-db.mixin.template index 829c63b..146c2d6 100644 --- a/src/create/mixin/moleculer-db.mixin.template +++ b/src/create/mixin/moleculer-db.mixin.template @@ -1,86 +1,68 @@ "use strict"; const fs = require("fs"); -const DbService = require("moleculer-db"); - -/** - * @typedef {import('moleculer').Context} Context Moleculer's Context - */ - -module.exports = function (collection) { - const cacheCleanEventName = `cache.clean.${collection}`; +const mongoose = require("mongoose"); // npm i mongoose -S +module.exports = (collection, modelSchema) => { const schema = { - mixins: [DbService], + actions: { + create: { + rest: "POST /", + async handler(ctx) { + const { params } = ctx; + console.log(this.adapter); + this.adapter.create(params, (err, saved) => { + if (err) this.logger.error(err); + this.logger.info(saved); + }); + }, + }, + update: { + rest: "PUT /:id", + async handler(ctx) { + const { params } = ctx; + this.adapter.findOneAndUpdate( + { _id: params.id }, + params, + (err, saved) => { + if (err) this.logger.error(err); + this.logger.info(saved); + } + ); + }, + }, + list: { + rest: "GET /", + async handler(ctx) { + const { params } = ctx; + return this.adapter.find({}); + }, + }, - events: { - /** - * Subscribe to the cache clean event. If it's triggered - * clean the cache entries for this service. - * - * @param {Context} ctx - */ - async [cacheCleanEventName]() { - if (this.broker.cacher) { - await this.broker.cacher.clean(`${this.fullName}.*`); - } + delete: { + rest: "DELETE /:id", + async handler(ctx) { + const { params } = ctx; + this.adapter.deleteOne({ _id: params.id }); + }, }, }, - methods: { - /** - * Send a cache clearing event when an entity changed. - * - * @param {String} type - * @param {any} json - * @param {Context} ctx - */ - async entityChanged(type, json, ctx) { - ctx.broadcast(cacheCleanEventName); + _connect() { + return mongoose + .connect("mongodb://localhost:27017/test") + .then(() => this.logger.info("Connected")) + .catch((err) => this.logger.error(err)); }, }, async started() { - // Check the count of items in the DB. If it's empty, - // call the `seedDB` method of the service. - if (this.seedDB) { - const count = await this.adapter.count(); - if (count == 0) { - this.logger.info( - `The '${collection}' collection is empty. Seeding the collection...` - ); - await this.seedDB(); - this.logger.info( - "Seeding is done. Number of records:", - await this.adapter.count() - ); - } + this._connect(); + if (!this.adapter) { + this.adapter = mongoose.model(collection, modelSchema); } }, }; - if (process.env.MONGO_URI) { - // Mongo adapter - const MongoAdapter = require("moleculer-db-adapter-mongo"); - - schema.adapter = new MongoAdapter(process.env.MONGO_URI); - schema.collection = collection; - } else if (process.env.NODE_ENV === "test") { - // NeDB memory adapter for testing - schema.adapter = new DbService.MemoryAdapter(); - } else { - // NeDB file DB adapter - - // Create data folder - if (!fs.existsSync("./data")) { - fs.mkdirSync("./data"); - } - - schema.adapter = new DbService.MemoryAdapter({ - filename: `./data/${collection}.db`, - }); - } - return schema; }; - diff --git a/test/e2e/create/mixin/mocks/ola.mixin.js b/test/e2e/create/mixin/mocks/ola.mixin.js index 829c63b..146c2d6 100644 --- a/test/e2e/create/mixin/mocks/ola.mixin.js +++ b/test/e2e/create/mixin/mocks/ola.mixin.js @@ -1,86 +1,68 @@ "use strict"; const fs = require("fs"); -const DbService = require("moleculer-db"); - -/** - * @typedef {import('moleculer').Context} Context Moleculer's Context - */ - -module.exports = function (collection) { - const cacheCleanEventName = `cache.clean.${collection}`; +const mongoose = require("mongoose"); // npm i mongoose -S +module.exports = (collection, modelSchema) => { const schema = { - mixins: [DbService], + actions: { + create: { + rest: "POST /", + async handler(ctx) { + const { params } = ctx; + console.log(this.adapter); + this.adapter.create(params, (err, saved) => { + if (err) this.logger.error(err); + this.logger.info(saved); + }); + }, + }, + update: { + rest: "PUT /:id", + async handler(ctx) { + const { params } = ctx; + this.adapter.findOneAndUpdate( + { _id: params.id }, + params, + (err, saved) => { + if (err) this.logger.error(err); + this.logger.info(saved); + } + ); + }, + }, + list: { + rest: "GET /", + async handler(ctx) { + const { params } = ctx; + return this.adapter.find({}); + }, + }, - events: { - /** - * Subscribe to the cache clean event. If it's triggered - * clean the cache entries for this service. - * - * @param {Context} ctx - */ - async [cacheCleanEventName]() { - if (this.broker.cacher) { - await this.broker.cacher.clean(`${this.fullName}.*`); - } + delete: { + rest: "DELETE /:id", + async handler(ctx) { + const { params } = ctx; + this.adapter.deleteOne({ _id: params.id }); + }, }, }, - methods: { - /** - * Send a cache clearing event when an entity changed. - * - * @param {String} type - * @param {any} json - * @param {Context} ctx - */ - async entityChanged(type, json, ctx) { - ctx.broadcast(cacheCleanEventName); + _connect() { + return mongoose + .connect("mongodb://localhost:27017/test") + .then(() => this.logger.info("Connected")) + .catch((err) => this.logger.error(err)); }, }, async started() { - // Check the count of items in the DB. If it's empty, - // call the `seedDB` method of the service. - if (this.seedDB) { - const count = await this.adapter.count(); - if (count == 0) { - this.logger.info( - `The '${collection}' collection is empty. Seeding the collection...` - ); - await this.seedDB(); - this.logger.info( - "Seeding is done. Number of records:", - await this.adapter.count() - ); - } + this._connect(); + if (!this.adapter) { + this.adapter = mongoose.model(collection, modelSchema); } }, }; - if (process.env.MONGO_URI) { - // Mongo adapter - const MongoAdapter = require("moleculer-db-adapter-mongo"); - - schema.adapter = new MongoAdapter(process.env.MONGO_URI); - schema.collection = collection; - } else if (process.env.NODE_ENV === "test") { - // NeDB memory adapter for testing - schema.adapter = new DbService.MemoryAdapter(); - } else { - // NeDB file DB adapter - - // Create data folder - if (!fs.existsSync("./data")) { - fs.mkdirSync("./data"); - } - - schema.adapter = new DbService.MemoryAdapter({ - filename: `./data/${collection}.db`, - }); - } - return schema; }; - From 2d48b2afa343dc134957b0bc320c6fa25bcf31f4 Mon Sep 17 00:00:00 2001 From: devalexandre Date: Sat, 9 Apr 2022 14:07:46 -0300 Subject: [PATCH 4/4] feat: add mixin, ts template --- .../moleculer-db.typescript.mixin.template | 159 +++++++----------- test/e2e/create/mixin/mocks/ola.mixin.ts | 159 +++++++----------- 2 files changed, 126 insertions(+), 192 deletions(-) diff --git a/src/create/mixin/moleculer-db.typescript.mixin.template b/src/create/mixin/moleculer-db.typescript.mixin.template index 6462fd4..93e88db 100644 --- a/src/create/mixin/moleculer-db.typescript.mixin.template +++ b/src/create/mixin/moleculer-db.typescript.mixin.template @@ -1,103 +1,70 @@ -"use strict"; - -import { existsSync } from "fs"; -import { sync } from "mkdirp"; import { Context, Service, ServiceSchema } from "moleculer"; -import DbService from "moleculer-db"; +import { Document, Schema, connect, model } from "mongoose"; + +export default class {{className}}Connection implements Partial, ThisType{ -export default class {{className}}Connection - implements Partial, ThisType -{ - private cacheCleanEventName: string; - private collection: string; + private collection: string; + private modelSchema: Schema; private schema: Partial & ThisType; - public constructor(public collectionName: string) { + public constructor(public collectionName: string, modelSchema: Schema) { this.collection = collectionName; - this.cacheCleanEventName = `cache.clean.${this.collection}`; + this.modelSchema = modelSchema; this.schema = { - mixins: [DbService], - events: { - /** - * Subscribe to the cache clean event. If it's triggered - * clean the cache entries for this service. - * - */ - async [this.cacheCleanEventName]() { - if (this.broker.cacher) { - await this.broker.cacher.clean(`${this.fullName}.*`); - } - }, - }, - methods: { - /** - * Send a cache clearing event when an entity changed. - * - * @param {String} type - * @param {any} json - * @param {Context} ctx - */ - entityChanged: async ( - type: string, - json: any, - ctx: Context - ) => { - await ctx.broadcast(this.cacheCleanEventName); - }, - }, - async started() { - // Check the count of items in the DB. If it's empty, - // Call the `seedDB` method of the service. - if (this.seedDB) { - const count = await this.adapter.count(); - if (count === 0) { - this.logger.info( - `The '${this.collection}' collection is empty. Seeding the collection...` - ); - await this.seedDB(); - this.logger.info( - "Seeding is done. Number of records:", - await this.adapter.count() - ); - } - } - }, - }; - } - - public start() { - if (process.env.MONGO_URI) { - // Mongo adapter - // eslint-disable-next-line @typescript-eslint/no-var-requires - const MongoAdapter = require("moleculer-db-adapter-mongo"); - this.schema.adapter = new MongoAdapter(process.env.MONGO_URI); - this.schema.collection = this.collection; - } else if (process.env.NODE_ENV === "test") { - // NeDB memory adapter for testing - // @ts-ignore - this.schema.adapter = new DbService.MemoryAdapter(); - } else { - // NeDB file DB adapter - - // Create data folder - if (!existsSync("./data")) { - sync("./data"); - } - // @ts-ignore - this.schema.adapter = new DbService.MemoryAdapter({ - filename: `./data/${this.collection}.db`, - }); - } - - return this.schema; - } - - public get _collection(): string { - return this.collection; - } - - public set _collection(value: string) { - this.collection = value; - } -} + actions: { + create: { + rest: "POST /", + async handler(ctx: Context) { + const { params } = ctx; + this.adapter.create(params, (err: Error, saved: Document) => { + if (err) {this.logger.error(err);}; + this.logger.info(saved); + }); + }, + }, + update: { + rest: "PUT /:id", + async handler(ctx: Context<{id: string}>) { + const { params } = ctx; + this.adapter.findOneAndUpdate( + { _id: params.id }, + params, + (err: Error, saved: Document) => { + if (err) {this.logger.error(err);} + this.logger.info(saved); + } + ); + }, + }, + list: { + rest: "GET /", + async handler(ctx: Context) { + return this.adapter.find({}); + }, + }, + delete: { + rest: "DELETE /:id", + async handler(ctx: Context<{id: string}>) { + const { params } = ctx; + this.adapter.deleteOne({ _id: params.id }); + }, + }, + }, + methods: { + connectDb() { + return connect("mongodb://localhost:27017/test") + .then(() => this.logger.info("Connected")) + .catch((err: Error) => this.logger.error(err)); + }, + }, + async started(){ + this.connectDb(); + }, + }; + } + public start(){ + this.schema.adapter = model(this.collection, this.modelSchema); + return this.schema; + } +}; diff --git a/test/e2e/create/mixin/mocks/ola.mixin.ts b/test/e2e/create/mixin/mocks/ola.mixin.ts index afbde74..1aacc39 100644 --- a/test/e2e/create/mixin/mocks/ola.mixin.ts +++ b/test/e2e/create/mixin/mocks/ola.mixin.ts @@ -1,103 +1,70 @@ -"use strict"; - -import { existsSync } from "fs"; -import { sync } from "mkdirp"; import { Context, Service, ServiceSchema } from "moleculer"; -import DbService from "moleculer-db"; +import { Document, Schema, connect, model } from "mongoose"; + +export default class OlaConnection implements Partial, ThisType{ -export default class OlaConnection - implements Partial, ThisType -{ - private cacheCleanEventName: string; - private collection: string; + private collection: string; + private modelSchema: Schema; private schema: Partial & ThisType; - public constructor(public collectionName: string) { + public constructor(public collectionName: string, modelSchema: Schema) { this.collection = collectionName; - this.cacheCleanEventName = `cache.clean.${this.collection}`; + this.modelSchema = modelSchema; this.schema = { - mixins: [DbService], - events: { - /** - * Subscribe to the cache clean event. If it's triggered - * clean the cache entries for this service. - * - */ - async [this.cacheCleanEventName]() { - if (this.broker.cacher) { - await this.broker.cacher.clean(`${this.fullName}.*`); - } - }, - }, - methods: { - /** - * Send a cache clearing event when an entity changed. - * - * @param {String} type - * @param {any} json - * @param {Context} ctx - */ - entityChanged: async ( - type: string, - json: any, - ctx: Context - ) => { - await ctx.broadcast(this.cacheCleanEventName); - }, - }, - async started() { - // Check the count of items in the DB. If it's empty, - // Call the `seedDB` method of the service. - if (this.seedDB) { - const count = await this.adapter.count(); - if (count === 0) { - this.logger.info( - `The '${this.collection}' collection is empty. Seeding the collection...` - ); - await this.seedDB(); - this.logger.info( - "Seeding is done. Number of records:", - await this.adapter.count() - ); - } - } - }, - }; - } - - public start() { - if (process.env.MONGO_URI) { - // Mongo adapter - // eslint-disable-next-line @typescript-eslint/no-var-requires - const MongoAdapter = require("moleculer-db-adapter-mongo"); - this.schema.adapter = new MongoAdapter(process.env.MONGO_URI); - this.schema.collection = this.collection; - } else if (process.env.NODE_ENV === "test") { - // NeDB memory adapter for testing - // @ts-ignore - this.schema.adapter = new DbService.MemoryAdapter(); - } else { - // NeDB file DB adapter - - // Create data folder - if (!existsSync("./data")) { - sync("./data"); - } - // @ts-ignore - this.schema.adapter = new DbService.MemoryAdapter({ - filename: `./data/${this.collection}.db`, - }); - } - - return this.schema; - } - - public get _collection(): string { - return this.collection; - } - - public set _collection(value: string) { - this.collection = value; - } -} + actions: { + create: { + rest: "POST /", + async handler(ctx: Context) { + const { params } = ctx; + this.adapter.create(params, (err: Error, saved: Document) => { + if (err) {this.logger.error(err);}; + this.logger.info(saved); + }); + }, + }, + update: { + rest: "PUT /:id", + async handler(ctx: Context<{id: string}>) { + const { params } = ctx; + this.adapter.findOneAndUpdate( + { _id: params.id }, + params, + (err: Error, saved: Document) => { + if (err) {this.logger.error(err);} + this.logger.info(saved); + } + ); + }, + }, + list: { + rest: "GET /", + async handler(ctx: Context) { + return this.adapter.find({}); + }, + }, + delete: { + rest: "DELETE /:id", + async handler(ctx: Context<{id: string}>) { + const { params } = ctx; + this.adapter.deleteOne({ _id: params.id }); + }, + }, + }, + methods: { + connectDb() { + return connect("mongodb://localhost:27017/test") + .then(() => this.logger.info("Connected")) + .catch((err: Error) => this.logger.error(err)); + }, + }, + async started(){ + this.connectDb(); + }, + }; + } + public start(){ + this.schema.adapter = model(this.collection, this.modelSchema); + return this.schema; + } +};