diff --git a/ghost/moleculer-service-from-class/.eslintrc.js b/ghost/moleculer-service-from-class/.eslintrc.js deleted file mode 100644 index c9c1bcb522..0000000000 --- a/ghost/moleculer-service-from-class/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: ['ghost'], - extends: [ - 'plugin:ghost/node' - ] -}; diff --git a/ghost/moleculer-service-from-class/LICENSE b/ghost/moleculer-service-from-class/LICENSE deleted file mode 100644 index 19bcb01bef..0000000000 --- a/ghost/moleculer-service-from-class/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2013-2022 Ghost Foundation - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/ghost/moleculer-service-from-class/README.md b/ghost/moleculer-service-from-class/README.md deleted file mode 100644 index 1e9b5bcf63..0000000000 --- a/ghost/moleculer-service-from-class/README.md +++ /dev/null @@ -1,115 +0,0 @@ -# Moleculer Service From Class - -This module is used to wrap a standard JS Class as a moleculer service, exposing -the public methods as actions on the service. It will also wrap other moleculer -services in an object with methods, for each of the services actions and injects -them into the class constructor. - -This allows us to write generic code, and then use moleculer as the transport -layer between services, taking advantage of its load balancing, transporters, -logging, tracing & other niceitys - -Because moleculer is an asyncronous transport mechanism - all methods MUST be -async. Also all methods should accept params as an object with keys - this works -with moleculers concept of `ctx.params` much better. - -Private methods are prefixed with an underscore ('_') - -## Install - -`npm install @tryghost/moleculer-service-from-class --save` - -or - -`yarn add @tryghost/moleculer-service-from-class` - - -## Usage - -```js -const srvFromClass = require('@tryghost/moleculer-service-from-class'); - -class SomeService { - async capitalize({string}) { - return string.toUpperCase(); - } -} - -class MyAwesomeService { - constructor({someService, someConfig}) { - this._someService = someService; - this._someConfig = someConfig; - } - - async myCoolMethod({name}) { - const NAME = await this._someService.capitalize({string: name}); - - return `${this._someConfig.greeting}, ${NAME}`; - } -} - -/** - * Moleculer way - */ - -const { ServiceBroker } = require('moleculer'); - -const broker = new ServiceBroker(); -broker.addService(srvFromClass({ - Service: SomeService, - name: 'some-service' -})); -broker.addService(srvFromClass({ - Service: MyAwesomeService, - name: 'awesome', - serviceDeps: { - someService: 'some-service' - }, - staticDeps: { - someConfig: { greeting: 'hello' } - } -})) - -broker.start().then(() => { - broker.call('awesome.myCoolMethod', {name: 'egg'}).then(console.log); -}); - - -/** - * Generic way - */ - -const awesome = new MyAwesomeService({ - someConfig: { greeting: 'Hello' }, - someService: new SomeService() -}); - -awesome.myCoolMethod({name: 'egg'}).then(console.log); -``` - - -## Develop - -This is a mono repository, managed with [lerna](https://lernajs.io/). - -Follow the instructions for the top-level repo. -1. `git clone` this repo & `cd` into it as usual -2. Run `yarn` to install top-level dependencies. - - -## Run - -- `yarn dev` - - -## Test - -- `yarn lint` run just eslint -- `yarn test` run lint and tests - - - - -# Copyright & License - -Copyright (c) 2013-2022 Ghost Foundation - Released under the [MIT license](LICENSE). diff --git a/ghost/moleculer-service-from-class/index.js b/ghost/moleculer-service-from-class/index.js deleted file mode 100644 index b00f306367..0000000000 --- a/ghost/moleculer-service-from-class/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./lib/moleculer-service-from-class.js'); diff --git a/ghost/moleculer-service-from-class/lib/moleculer-service-from-class.js b/ghost/moleculer-service-from-class/lib/moleculer-service-from-class.js deleted file mode 100644 index 774221d650..0000000000 --- a/ghost/moleculer-service-from-class/lib/moleculer-service-from-class.js +++ /dev/null @@ -1,176 +0,0 @@ -const moleculer = require('moleculer'); - -/** - * @typedef {object} Service - */ - -/** - * @template Type - * @typedef {function(new: Type, object)} Class - */ - -/** - * Creates a naive "proxy" method which calls a moleculer service's method - * passing the first argument as the `params` to the moleculer service call - * It also binds the ctx correctly to allow for nested service calls - * - * @param {moleculer.Context} ctx - * @param {string} serviceName - The name of the service in the moleculer cluster - * @param {string} methodName - The name of the moleculer "action" on the service - * @param {string} serviceVersion - The version of the service in the moleculer cluster - * - * @returns {(params: object) => Promise} - */ -function proxy(ctx, serviceName, methodName, serviceVersion) { - return async params => ctx.call(`${serviceVersion}.${serviceName}.${methodName}`, params); -} - -/** - * Get the method names of the service - * - * @param {Class} Class - * - * @returns {string[]} A list of the methods names for Class - */ -function getClassMethods(Class) { - return Reflect.ownKeys(Class.prototype).reduce((methods, key) => { - if (typeof Class.prototype[key] !== 'function') { - return methods; - } - if (key === 'constructor' || key.toString().startsWith('_')) { - return methods; - } - return methods.concat(key); - }, []); -} - -/** - * createServiceProxy - * - * @param {moleculer.Context} ctx - * @param {string} serviceName - * @param {string} serviceVersion - * @returns {Promise} - */ -async function createServiceProxy(ctx, serviceName, serviceVersion) { - await ctx.broker.waitForServices([{ - name: serviceName, - version: serviceVersion - }]); - /** @type {{action: {name: string, rawName: string}}[]} */ - const actionsList = await ctx.call('$node.actions'); - - const serviceMethods = actionsList.filter((obj) => { - const isValidAction = obj && obj.action; - if (!isValidAction) { - ctx.broker.logger.debug(`Recieved invalid action ${JSON.stringify(obj)}`); - return false; - } - const belongsToService = obj.action.name.startsWith(`${serviceVersion}.${serviceName}.`); - - return belongsToService; - }).map(obj => obj.action.rawName); - - return serviceMethods.reduce((serviceProxy, methodName) => { - ctx.broker.logger.debug(`Creating proxy ${serviceName}.${methodName}`); - return Object.assign(serviceProxy, { - [methodName]: proxy(ctx, serviceName, methodName, serviceVersion) - }); - }, []); -} - -/** - * @typedef {Object} ServiceDefinition - * @prop {string} name - * @prop {string} version - */ - -/** - * Create a ServiceSchema compatible with moleculer - * - * @template {{_init(): Promise}} Type - * - * @param {object} params The Service to proxy via moleculer - * @param {Class} params.Service The Service to proxy via moleculer - * @param {string} params.name The name of the service in moleculer - * @param {Object.} [params.serviceDeps] A map of dependencies with a key of the param name and value of the moleculer service - * @param {Object.} [params.staticDeps] Any static dependencies which do not need to be proxied by moleculer - * @param {boolean} [params.forceSingleton=false] Forces the wrapper to only ever create once instance of Service - * @param {string} [params.version='1'] Forces the wrapper to only ever create once instance of Service - * - * @returns {moleculer.ServiceSchema} - */ -function createMoleculerServiceSchema({Service, name, serviceDeps = null, staticDeps = null, forceSingleton = false, version = '1'}) { - const methods = getClassMethods(Service); - - /** - * Creates an instance of the service - wiring and mapping any dependencies - * - * @param {moleculer.Context} ctx - * @returns {Promise} - */ - async function getDynamicServiceInstance(ctx) { - const instanceDeps = Object.create(serviceDeps); - if (serviceDeps) { - for (const dep in serviceDeps) { - const serviceDefinition = serviceDeps[dep]; - const serviceName = serviceDefinition.name; - const serviceVersion = serviceDefinition.version; - const serviceProxy = await createServiceProxy(ctx, serviceName, serviceVersion); - instanceDeps[dep] = serviceProxy; - } - } - instanceDeps.logging = ctx.broker.logger; - Object.assign(instanceDeps, staticDeps); - - const service = new Service(instanceDeps); - return service; - } - - let singleton = null; - /** - * Ensures that the Service is only instantiated once - * - * @param {moleculer.Context} ctx - * @returns {Promise} - */ - async function getSingletonServiceInstance(ctx) { - if (singleton) { - return singleton; - } - singleton = await getDynamicServiceInstance(ctx); - if (singleton._init) { - await singleton._init(); - } - return singleton; - } - - const getServiceInstance = (!serviceDeps || forceSingleton) ? getSingletonServiceInstance : getDynamicServiceInstance; - - /** @type moleculer.ServiceActionsSchema */ - const actions = { - ping() { - return 'pong'; - } - }; - - for (const method of methods) { - /** @type {(ctx: moleculer.Context) => Promise} */ - actions[method] = async function (ctx) { - const service = await getServiceInstance(ctx); - return service[method](ctx.params); - }; - } - - return { - name, - version, - actions, - async started() { - const ctx = new moleculer.Context(this.broker, null); - await getServiceInstance(ctx); - } - }; -} - -module.exports = createMoleculerServiceSchema; diff --git a/ghost/moleculer-service-from-class/package.json b/ghost/moleculer-service-from-class/package.json deleted file mode 100644 index e18905733c..0000000000 --- a/ghost/moleculer-service-from-class/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "@tryghost/moleculer-service-from-class", - "version": "0.2.28", - "repository": "https://github.com/TryGhost/Utils/tree/main/packages/moleculer-service-from-class", - "author": "Ghost Foundation", - "license": "MIT", - "main": "index.js", - "scripts": { - "dev": "echo \"Implement me!\"", - "test": "NODE_ENV=testing c8 --all --reporter text --reporter cobertura mocha './test/**/*.test.js'", - "lint": "eslint . --ext .js --cache", - "posttest": "yarn lint" - }, - "files": [ - "index.js", - "lib" - ], - "publishConfig": { - "access": "public" - }, - "devDependencies": { - "c8": "7.12.0", - "mocha": "10.0.0", - "should": "13.2.3", - "sinon": "14.0.0" - }, - "dependencies": { - "moleculer": "^0.14.11" - } -} diff --git a/ghost/moleculer-service-from-class/test/.eslintrc.js b/ghost/moleculer-service-from-class/test/.eslintrc.js deleted file mode 100644 index 829b601eb0..0000000000 --- a/ghost/moleculer-service-from-class/test/.eslintrc.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: ['ghost'], - extends: [ - 'plugin:ghost/test' - ] -}; diff --git a/ghost/moleculer-service-from-class/test/moleculer-service-from-class.test.js b/ghost/moleculer-service-from-class/test/moleculer-service-from-class.test.js deleted file mode 100644 index 466b200d82..0000000000 --- a/ghost/moleculer-service-from-class/test/moleculer-service-from-class.test.js +++ /dev/null @@ -1,91 +0,0 @@ -const should = require('should'); -const sinon = require('sinon'); -const moleculer = require('moleculer'); -const createMoleculerServiceFromClass = require('../'); - -const hasOwnProperty = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty); - -describe('MoleculerServiceFromClass', function () { - it('Exposes name & methods as actions (excluding private & constructor) as well as a default ping', function () { - class Dep { - async _init() {} - async _privateMethod() {} - async someMethod() { - return 5; - } - } - - const service = createMoleculerServiceFromClass({Service: Dep, name: 'dep', version: '1'}); - - const name = service.name; - const version = service.version; - const actions = service.actions; - - should.equal(name, 'dep'); - should.equal(version, '1'); - - should.equal(hasOwnProperty(actions, '_privateMethod'), false); - should.equal(hasOwnProperty(actions, 'constructor'), false); - - should.equal(hasOwnProperty(actions, 'someMethod'), true); - should.equal(hasOwnProperty(actions, 'ping'), true); - }); - - it('Wires up dynamic and static dependencies correctly', async function () { - const fakeStaticDep = 13; - class Dep { - async _init() {} - constructor({staticDep}) { - this._staticDep = staticDep; - should.equal(staticDep, fakeStaticDep); - } - async someMethod() { - return this._staticDep; - } - } - - class Main { - async _init() {} - /** - * @param {object} deps - * @param {Dep} deps.dep - */ - constructor({dep}) { - this._dep = dep; - } - - async someOtherMethod() { - const num = await this._dep.someMethod(); - return num * 2; - } - } - - const depService = createMoleculerServiceFromClass({Service: Dep, name: 'dep', version: '1', staticDeps: { - staticDep: fakeStaticDep - }}); - - const mainService = createMoleculerServiceFromClass({Service: Main, name: 'main', version: '1', serviceDeps: { - dep: { - name: 'dep', - version: '1' - } - }}); - - const broker = new moleculer.ServiceBroker({logger: false}); - broker.createService(depService); - broker.createService(mainService); - - await broker.start(); - - const someMethod = sinon.spy(Dep.prototype, 'someMethod'); - const someOtherMethod = sinon.spy(Main.prototype, 'someOtherMethod'); - - const result = await broker.call('1.main.someOtherMethod'); - - should.equal(someMethod.called, true); - should.equal(someOtherMethod.called, true); - should.equal(result, 26); - - await broker.stop(); - }); -}); diff --git a/ghost/moleculer-service-from-class/tsconfig.json b/ghost/moleculer-service-from-class/tsconfig.json deleted file mode 100644 index 375c68363c..0000000000 --- a/ghost/moleculer-service-from-class/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - "declaration": true, - "emitDeclarationOnly": true, - "allowJs": true, - "checkJs": true, - "module": "commonjs", - "moduleResolution": "node", - "target": "es6" - }, - "exclude": [ - "node_modules" - ] -} diff --git a/ghost/moleculer-service-from-class/types/index.d.ts b/ghost/moleculer-service-from-class/types/index.d.ts deleted file mode 100644 index a0dbee9997..0000000000 --- a/ghost/moleculer-service-from-class/types/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const _exports: typeof import("./lib/moleculer-service-from-class.js"); -export = _exports; diff --git a/ghost/moleculer-service-from-class/types/lib/moleculer-service-from-class.d.ts b/ghost/moleculer-service-from-class/types/lib/moleculer-service-from-class.d.ts deleted file mode 100644 index 2f059ea5c5..0000000000 --- a/ghost/moleculer-service-from-class/types/lib/moleculer-service-from-class.d.ts +++ /dev/null @@ -1,48 +0,0 @@ -export = createMoleculerServiceSchema; -/** - * @typedef {Object} ServiceDefinition - * @prop {string} name - * @prop {string} version - */ -/** - * Create a ServiceSchema compatible with moleculer - * - * @template {{_init(): Promise}} Type - * - * @param {object} params The Service to proxy via moleculer - * @param {Class} params.Service The Service to proxy via moleculer - * @param {string} params.name The name of the service in moleculer - * @param {Object.} [params.serviceDeps] A map of dependencies with a key of the param name and value of the moleculer service - * @param {Object.} [params.staticDeps] Any static dependencies which do not need to be proxied by moleculer - * @param {boolean} [params.forceSingleton=false] Forces the wrapper to only ever create once instance of Service - * @param {string} [params.version='1'] Forces the wrapper to only ever create once instance of Service - * - * @returns {moleculer.ServiceSchema} - */ -declare function createMoleculerServiceSchema; -}>({ Service, name, serviceDeps, staticDeps, forceSingleton, version }: { - Service: Class; - name: string; - serviceDeps?: { - [x: string]: ServiceDefinition; - }; - staticDeps?: { - [x: string]: any; - }; - forceSingleton?: boolean; - version?: string; -}): moleculer.ServiceSchema; -declare namespace createMoleculerServiceSchema { - export { Service, Class, ServiceDefinition }; -} -/** - * - */ -type Class = new (arg1: object) => Type; -type ServiceDefinition = { - name: string; - version: string; -}; -import moleculer = require("moleculer"); -type Service = object; diff --git a/ghost/moleculer-service-from-class/types/test/moleculer-service-from-class.test.d.ts b/ghost/moleculer-service-from-class/types/test/moleculer-service-from-class.test.d.ts deleted file mode 100644 index cb0ff5c3b5..0000000000 --- a/ghost/moleculer-service-from-class/types/test/moleculer-service-from-class.test.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {};