From fa4856a288a9df6c51b58f233c24f0a6595bf8b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 30 Jan 2018 13:34:19 +0100 Subject: [PATCH 1/8] docs: add example-microservices Move https://github.com/strongloop/loopback-next-example to monorepo. --- packages/example-microservices/LICENSE | 25 +++ packages/example-microservices/README.md | 59 +++++ packages/example-microservices/bin/build | 9 + .../example-microservices/bin/get-account | 3 + .../bin/get-account-summary | 2 + packages/example-microservices/bin/start | 21 ++ packages/example-microservices/bin/stop | 3 + packages/example-microservices/bin/test.js | 57 +++++ packages/example-microservices/package.json | 36 +++ .../account-without-juggler/README.md | 129 +++++++++++ .../account-without-juggler/bin/get-account | 2 + .../controllers/AccountController.api.ts | 144 ++++++++++++ .../controllers/AccountController.ts | 30 +++ .../services/account-without-juggler/index.ts | 42 ++++ .../account-without-juggler/package.json | 35 +++ .../account/datasources/mysql.json | 7 + .../account/datasources/mysqlconn.ts | 205 ++++++++++++++++++ .../account/datasources/mysqlds.ts | 15 ++ .../repositories/account/index.ts | 11 + .../repositories/account/models/Account.ts | 27 +++ .../models/account/model-definition.js | 34 +++ .../account-without-juggler/test/mocha.opts | 2 + .../test/unit/account-controller.ts | 74 +++++++ .../tsconfig.common.json | 16 ++ .../account-without-juggler/tsconfig.json | 30 +++ .../services/account/bin/create-account | 2 + .../services/account/bin/delete-account | 2 + .../services/account/bin/get-account | 2 + .../services/account/bin/update-account | 2 + .../controllers/AccountController.api.ts | 114 ++++++++++ .../account/controllers/AccountController.ts | 29 +++ .../services/account/index.ts | 39 ++++ .../services/account/package.json | 32 +++ .../account/datasources/local-fs/data.json | 13 ++ .../account/repositories/account/index.ts | 31 +++ .../models/account/model-definition.json | 34 +++ .../services/account/test/mocha.opts | 2 + .../services/account/test/unit/test.data.json | 13 ++ .../services/account/test/unit/test.ts | 145 +++++++++++++ .../services/account/tsconfig.json | 29 +++ .../services/customer/bin/get-customers | 2 + .../controllers/CustomerController.api.ts | 74 +++++++ .../controllers/CustomerController.ts | 16 ++ .../services/customer/index.ts | 39 ++++ .../services/customer/package.json | 26 +++ .../customer/datasources/local-fs/data.json | 11 + .../customer/repositories/customer/index.ts | 18 ++ .../models/customer/model-definition.json | 37 ++++ .../services/customer/tsconfig.json | 29 +++ .../AccountManagementController.api.ts | 153 +++++++++++++ .../AccountManagementController.ts | 39 ++++ .../services/facade/index.ts | 37 ++++ .../services/facade/package.json | 27 +++ .../facade/repositories/account/index.ts | 27 +++ .../facade/repositories/account/swagger.json | 69 ++++++ .../facade/repositories/customer/index.ts | 23 ++ .../facade/repositories/customer/swagger.json | 44 ++++ .../facade/repositories/transaction/index.ts | 23 ++ .../repositories/transaction/swagger.json | 44 ++++ .../services/facade/tsconfig.json | 29 +++ .../services/todo-legacy/README.md | 175 +++++++++++++++ .../services/todo-legacy/application.ts | 42 ++++ .../controllers/todo-controller.api.ts | 184 ++++++++++++++++ .../controllers/todo-controller.ts | 77 +++++++ .../services/todo-legacy/datasources.ts | 57 +++++ .../services/todo-legacy/index.ts | 14 ++ .../services/todo-legacy/models/todo.ts | 34 +++ .../services/todo-legacy/package.json | 29 +++ .../test/controller/todo-controller.test.ts | 144 ++++++++++++ .../services/todo-legacy/tsconfig.json | 11 + .../services/transaction/bin/get-transactions | 2 + .../controllers/TransactionController.api.ts | 54 +++++ .../controllers/TransactionController.ts | 21 ++ .../services/transaction/index.ts | 39 ++++ .../services/transaction/package.json | 26 +++ .../datasources/local-fs/data.json | 20 ++ .../repositories/transaction/index.ts | 18 ++ .../models/transaction/model-definition.json | 26 +++ .../services/transaction/tsconfig.json | 29 +++ packages/example-microservices/tsconfig.json | 22 ++ 80 files changed, 3298 insertions(+) create mode 100644 packages/example-microservices/LICENSE create mode 100644 packages/example-microservices/README.md create mode 100755 packages/example-microservices/bin/build create mode 100755 packages/example-microservices/bin/get-account create mode 100755 packages/example-microservices/bin/get-account-summary create mode 100755 packages/example-microservices/bin/start create mode 100755 packages/example-microservices/bin/stop create mode 100644 packages/example-microservices/bin/test.js create mode 100644 packages/example-microservices/package.json create mode 100644 packages/example-microservices/services/account-without-juggler/README.md create mode 100755 packages/example-microservices/services/account-without-juggler/bin/get-account create mode 100644 packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts create mode 100644 packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts create mode 100644 packages/example-microservices/services/account-without-juggler/index.ts create mode 100644 packages/example-microservices/services/account-without-juggler/package.json create mode 100644 packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysql.json create mode 100644 packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts create mode 100644 packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlds.ts create mode 100644 packages/example-microservices/services/account-without-juggler/repositories/account/index.ts create mode 100644 packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts create mode 100644 packages/example-microservices/services/account-without-juggler/repositories/account/models/account/model-definition.js create mode 100644 packages/example-microservices/services/account-without-juggler/test/mocha.opts create mode 100644 packages/example-microservices/services/account-without-juggler/test/unit/account-controller.ts create mode 100644 packages/example-microservices/services/account-without-juggler/tsconfig.common.json create mode 100644 packages/example-microservices/services/account-without-juggler/tsconfig.json create mode 100755 packages/example-microservices/services/account/bin/create-account create mode 100755 packages/example-microservices/services/account/bin/delete-account create mode 100755 packages/example-microservices/services/account/bin/get-account create mode 100755 packages/example-microservices/services/account/bin/update-account create mode 100644 packages/example-microservices/services/account/controllers/AccountController.api.ts create mode 100644 packages/example-microservices/services/account/controllers/AccountController.ts create mode 100644 packages/example-microservices/services/account/index.ts create mode 100644 packages/example-microservices/services/account/package.json create mode 100644 packages/example-microservices/services/account/repositories/account/datasources/local-fs/data.json create mode 100644 packages/example-microservices/services/account/repositories/account/index.ts create mode 100644 packages/example-microservices/services/account/repositories/account/models/account/model-definition.json create mode 100644 packages/example-microservices/services/account/test/mocha.opts create mode 100644 packages/example-microservices/services/account/test/unit/test.data.json create mode 100644 packages/example-microservices/services/account/test/unit/test.ts create mode 100644 packages/example-microservices/services/account/tsconfig.json create mode 100755 packages/example-microservices/services/customer/bin/get-customers create mode 100644 packages/example-microservices/services/customer/controllers/CustomerController.api.ts create mode 100644 packages/example-microservices/services/customer/controllers/CustomerController.ts create mode 100644 packages/example-microservices/services/customer/index.ts create mode 100644 packages/example-microservices/services/customer/package.json create mode 100644 packages/example-microservices/services/customer/repositories/customer/datasources/local-fs/data.json create mode 100644 packages/example-microservices/services/customer/repositories/customer/index.ts create mode 100644 packages/example-microservices/services/customer/repositories/customer/models/customer/model-definition.json create mode 100644 packages/example-microservices/services/customer/tsconfig.json create mode 100644 packages/example-microservices/services/facade/controllers/AccountManagementController.api.ts create mode 100644 packages/example-microservices/services/facade/controllers/AccountManagementController.ts create mode 100644 packages/example-microservices/services/facade/index.ts create mode 100644 packages/example-microservices/services/facade/package.json create mode 100644 packages/example-microservices/services/facade/repositories/account/index.ts create mode 100644 packages/example-microservices/services/facade/repositories/account/swagger.json create mode 100644 packages/example-microservices/services/facade/repositories/customer/index.ts create mode 100644 packages/example-microservices/services/facade/repositories/customer/swagger.json create mode 100644 packages/example-microservices/services/facade/repositories/transaction/index.ts create mode 100644 packages/example-microservices/services/facade/repositories/transaction/swagger.json create mode 100644 packages/example-microservices/services/facade/tsconfig.json create mode 100644 packages/example-microservices/services/todo-legacy/README.md create mode 100644 packages/example-microservices/services/todo-legacy/application.ts create mode 100644 packages/example-microservices/services/todo-legacy/controllers/todo-controller.api.ts create mode 100644 packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts create mode 100644 packages/example-microservices/services/todo-legacy/datasources.ts create mode 100644 packages/example-microservices/services/todo-legacy/index.ts create mode 100644 packages/example-microservices/services/todo-legacy/models/todo.ts create mode 100644 packages/example-microservices/services/todo-legacy/package.json create mode 100644 packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts create mode 100644 packages/example-microservices/services/todo-legacy/tsconfig.json create mode 100755 packages/example-microservices/services/transaction/bin/get-transactions create mode 100644 packages/example-microservices/services/transaction/controllers/TransactionController.api.ts create mode 100644 packages/example-microservices/services/transaction/controllers/TransactionController.ts create mode 100644 packages/example-microservices/services/transaction/index.ts create mode 100644 packages/example-microservices/services/transaction/package.json create mode 100644 packages/example-microservices/services/transaction/repositories/transaction/datasources/local-fs/data.json create mode 100644 packages/example-microservices/services/transaction/repositories/transaction/index.ts create mode 100644 packages/example-microservices/services/transaction/repositories/transaction/models/transaction/model-definition.json create mode 100644 packages/example-microservices/services/transaction/tsconfig.json create mode 100644 packages/example-microservices/tsconfig.json diff --git a/packages/example-microservices/LICENSE b/packages/example-microservices/LICENSE new file mode 100644 index 000000000000..f0dbf91b8b05 --- /dev/null +++ b/packages/example-microservices/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) IBM Corp. 2013,2016. All Rights Reserved. +Node module: loopback-next-example +This project is licensed under the MIT License, full text below. + +-------- + +MIT license + +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/packages/example-microservices/README.md b/packages/example-microservices/README.md new file mode 100644 index 000000000000..8692019bf907 --- /dev/null +++ b/packages/example-microservices/README.md @@ -0,0 +1,59 @@ +# loopback-next-example + +[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/strongloop/loopback) + +How to build scalable microservices using LoopBack.next. + +> What's the difference between LoopBack.next and the current version of +> Loopback? See [LoopBack 3 vs LoopBack 4](https://github.com/strongloop/loopback-next/wiki/FAQ#loopback-3-vs-loopback-4). + +## Installation + +Make sure you have the following installed: + +- [Node.js](https://nodejs.org/en/download/) >= 7.0.0 +- [TypeScript](https://www.typescriptlang.org/index.html#download-links) >= 2.0.0 `npm i -g typescript` +- [TypeScript Node](https://github.com/TypeStrong/ts-node#installation) >= 3.0.0 `npm i -g ts-node` + +```shell +# install loopback-next-example +git clone https://github.com/strongloop/loopback-next-example +cd loopback-next-example +npm run build +``` + +## Basic use + +```shell +# start all microservices +npm start + +# perform GET request to retrieve account summary data +curl localhost:3000/account/summary?accountNumber=CHK52321122 # or npm test + +# perform GET request to retrieve account data +curl localhost:3001/accounts?accountNumber=CHK52321122 + +# stop all microservices +npm stop +``` + +> Helper scripts for the above commands are in [`/bin`](https://github.com/strongloop/loopback-next-example/tree/master/bin) +directory. + +# Team + +Ritchie Martori|Simon Ho|Siddhi Pai|Mahesh Patsute|Deepak Rajamohan +:-:|:-:|:-:|:-:|:-: +[](http://github.com/ritch)|[](http://github.com/superkhau)|[](http://github.com/siddhipai)|[](http://github.com/mpatsute)|[](http://github.com/deepakrkris) + +[See all contributors](https://github.com/strongloop/loopback-next-example/graphs/contributors) + +# Contributing + +- [Guidelines](https://github.com/strongloop/loopback-next/wiki/Contributing) +- [Join the team](https://github.com/strongloop/loopback-next/wiki/Contributing#join-the-team) + +# License + +MIT diff --git a/packages/example-microservices/bin/build b/packages/example-microservices/bin/build new file mode 100755 index 000000000000..db11cf2512ef --- /dev/null +++ b/packages/example-microservices/bin/build @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +cd services/account +npm i +cd ../customer +npm i +cd ../transaction +npm i +cd ../facade +npm i diff --git a/packages/example-microservices/bin/get-account b/packages/example-microservices/bin/get-account new file mode 100755 index 000000000000..7f09cc5c57aa --- /dev/null +++ b/packages/example-microservices/bin/get-account @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +curl -s "http://localhost:3001/accounts?accountNumber=CHK52321122" | jq . + diff --git a/packages/example-microservices/bin/get-account-summary b/packages/example-microservices/bin/get-account-summary new file mode 100755 index 000000000000..8f0eab1c7e49 --- /dev/null +++ b/packages/example-microservices/bin/get-account-summary @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +curl -s "http://localhost:3000/account/summary?accountNumber=CHK52321122" | jq . diff --git a/packages/example-microservices/bin/start b/packages/example-microservices/bin/start new file mode 100755 index 000000000000..aeb75d448918 --- /dev/null +++ b/packages/example-microservices/bin/start @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +./bin/stop + +echo "Starting microservices..." +cd services/account +ts-node index.ts lb-next-account-micro-serv & +echo "Started Account microservice, PID: $!" +cd ../customer && \ +ts-node index.ts lb-next-customer-micro-serv & +echo "Started Customer microservice, PID: $!" +cd ../transaction && \ +ts-node index.ts lb-next-transaction-micro-serv & +echo "Started Transaction microservice, PID: $!" +cd ../facade && \ +ts-node index.ts lb-next-facade-micro-serv & +echo "Started Facade microservice, PID: $!" + +sleep 5 + +echo 'All microservices started successfully.' +echo 'To test the application, run "npm test".' diff --git a/packages/example-microservices/bin/stop b/packages/example-microservices/bin/stop new file mode 100755 index 000000000000..08c3ad336ecd --- /dev/null +++ b/packages/example-microservices/bin/stop @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +ps -ef | grep "[l]b-next" | awk '{print $2}' | xargs kill -9 +echo "All microservices stopped successfully." diff --git a/packages/example-microservices/bin/test.js b/packages/example-microservices/bin/test.js new file mode 100644 index 000000000000..5462e37dbad3 --- /dev/null +++ b/packages/example-microservices/bin/test.js @@ -0,0 +1,57 @@ +'use strict'; + +let Promise = require('bluebird'); +let exec = require('child_process').execSync; +let spawn = require('child_process').spawn; +let fs = Promise.promisifyAll(require('fs')); +let path = require('path'); + +let cmd = path.resolve(__dirname, '..', 'node_modules', '.bin', '_mocha'); +let args = ['--compilers', 'ts:ts-node/register,tsx:ts-node/register']; + +let services = path.resolve('services'); +return fs.readdirAsync(services).then(folders => { + return Promise.each(folders, f => { + let dir = path.resolve(services, f); + return fs + .readdirAsync(dir) + .then(subfolders => { + if (subfolders.indexOf('test') > -1) { + return new Promise((resolve, reject) => { + console.log('RUN TESTS - %s:', f); + let testArgs = args.push(path.resolve(dir, 'test/**/*test.ts')); + // Install dependencies + exec('npm i', { + cwd: dir + }); + let test = spawn(cmd, args); + test.stdout.on('data', out => { + console.log(out.toString()); + }); + + test.stderr.on('data', out => { + console.error(out.toString()); + }); + + test.on('close', code => { + if (code) { + return reject(code); + } else { + console.log('TEST SUCCESS - %s', f); + return resolve(); + } + }); + }); + } else { + console.log('No "test" folder was found in %s', f); + return Promise.resolve(); + } + }) + .catch(code => { + console.error('TESTS FAILED - %s, exit code %s', f, code); + return process.exit(code); + }); + }).then(() => { + console.log('TESTS COMPLETE'); + }); +}); diff --git a/packages/example-microservices/package.json b/packages/example-microservices/package.json new file mode 100644 index 000000000000..bdb47be68caf --- /dev/null +++ b/packages/example-microservices/package.json @@ -0,0 +1,36 @@ +{ + "name": "loopback-next-example", + "version": "1.0.0", + "description": "How to use LoopBack.next and some recommended best practices.", + "main": "facade/index.js", + "scripts": { + "build": "bin/build", + "restart": "npm run stop && npm run build && npm run start", + "start": "bin/start", + "stop": "bin/stop", + "test": "node bin/test.js" + }, + "engines": { + "node": ">=8" + }, + "keywords": [ + "loopback-next", + "example" + ], + "author": "IBM", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/strongloop/loopback-next-example.git" + }, + "bugs": { + "url": "https://github.com/strongloop/loopback-next-example/issues" + }, + "homepage": "https://github.com/strongloop/loopback-next-example#readme", + "dependencies": { + "bluebird": "^3.5.0", + "mocha": "^4.0.0", + "ts-node": "^3.1.0", + "typescript": "^2.4.1" + } +} diff --git a/packages/example-microservices/services/account-without-juggler/README.md b/packages/example-microservices/services/account-without-juggler/README.md new file mode 100644 index 000000000000..b36d61b705bc --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/README.md @@ -0,0 +1,129 @@ +# Account Service with Custom MySQL Datasource and Connector + +## Summary +A REST API for managing bank accounts. + +## Installation +This example requires Node.js 8.x or higher. +Install dependencies: +``` +npm i +``` + +After you've installed your dependencies, you'll need to configure the +`repositories/account/datasources/mysql.json` file to point towards +your MySQL database. + +By default, this example is configured with credentials defined by +loopback-connector-mysql module's `setup.sh` file (which we use for +unit tests). + + +## Overview + +This sample application demonstrates a simple CRUD API for an Account model. +This application does not currently have an explorer component. + +Perform a series of CRUD operations using GET, POST, PATCH and DELETE +on the `localhost:3001/accounts` route! + +In this example app, the AccountRepository is using a custom MySQL connector +implementation in `repositories/account/datasources/mysqlconn.ts` which +has the methods to run corresponding queries in the underlying database +for the CRUD operations without the use of LoopBack's juggler module. + +## Use + +Run the app (from the root directory i.e. inside `account-without-juggler`): +``` +npm start +``` + +Use `cURL` or your favourite REST client to access the endpoints! + +### Create an Account + +`POST /accounts/create` + +Body: +```json +{ +"id": "30", +"customerNumber": "600", +"balance": 220, +"branch": "TO", +"type": "Savings", +"avgBalance": 100, +"minimumBalance": 30 +} +``` + +Returns: +```json +{ +"id": "30", +"customerNumber": "600", +"balance": 220, +"branch": "TO", +"type": "Savings", +"avgBalance": 100, +"minimumBalance": 30 +} +``` + +### Get Account by ID + +`GET /accounts/?filter={"where": {"id": "30"}}` + +Returns: +```json +{ +"id": "30", +"customerNumber": "600", +"balance": 220, +"branch": "TO", +"type": "Savings", +"avgBalance": 100, +"minimumBalance": 30 +} +``` + +You can also filter by other fields by changing the value of the where filter +in the above example. If you specify an empty filter (i.e. `{}`), you will get +all the account instances in the database. + +### Update Account by ID + +`PATCH /accounts/update?id=30` +Body: +```json +{ + "customerNumber": "601" +} +``` + +Returns: +```json +{ + "count": 1 +} +``` + +### Delete an Account by ID + +`DELETE /accounts/delete?id=30` + +Returns: +```json +{ + "count": 1 +} +``` + +## Tests +Run tests with `npm test`! + +## What's Next? +Now that you've got a working example to play with, you can try to +implement your own custom connector using other databases such as +MsSQL or Oracle or improve this example. \ No newline at end of file diff --git a/packages/example-microservices/services/account-without-juggler/bin/get-account b/packages/example-microservices/services/account-without-juggler/bin/get-account new file mode 100755 index 000000000000..2b96d8972efe --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/bin/get-account @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +curl -s localhost:3001/accounts | jq diff --git a/packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts new file mode 100644 index 000000000000..1cd67a2b5fdf --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts @@ -0,0 +1,144 @@ +export const def = { + basePath: '/', + paths: { + '/accounts': { + get: { + 'x-operation-name': 'getAccount', + parameters: [ + { + name: 'filter', + in: 'query', + description: + 'The criteria used to narrow down the number of accounts returned.', + required: true, + type: 'object' + } + ], + responses: { + 200: { + schema: { + type: 'array', + $ref: '#/definitions/Account' + } + } + } + } + }, + '/accounts/create': { + post: { + 'x-operation-name': 'createAccount', + parameters: [ + { + name: 'accountInstance', + in: 'body', + description: 'The account instance to create.', + required: true, + type: 'object' + } + ], + responses: { + 200: { + schema: { + $ref: '#/definitions/Account' + } + } + } + } + }, + '/accounts/update': { + post: { + 'x-operation-name': 'updateById', + parameters: [ + { + name: 'id', + in: 'query', + description: 'The id of the model instance to be updated.', + required: true, + type: 'string' + }, + { + name: 'data', + in: 'body', + description: 'An object of model property name/value pairs.', + required: true, + type: 'object' + } + ], + responses: { + 200: { + schema: { + type: 'object', + description: 'Information about the updated record.', + properties: { + count: { + type: 'number', + description: 'The number of records updated.' + } + } + } + } + } + } + }, + '/accounts/delete': { + delete: { + 'x-operation-name': 'deleteById', + parameters: [ + { + name: 'id', + in: 'query', + description: 'The ID for the model instance to be deleted.', + required: true, + type: 'object' + } + ], + responses: { + 200: { + schema: { + type: 'object', + description: 'Information on the deleted record.', + properties: { + count: { + type: 'number', + description: 'The number of records deleted.' + } + } + } + } + } + } + } + }, + definitions: { + Account: { + id: { + type: 'string', + description: 'The ID for the account instance.' + }, + customerNumber: { + type: 'string', + description: 'The customer ID for the account instance.' + }, + balance: { + type: 'number', + description: 'The current balance for the account instance.' + }, + branch: { + type: 'string', + description: 'The branch location for the account instance.' + }, + type: { + type: 'string', + description: 'The type of banking account.' + }, + avgBalance: { + type: 'number', + description: 'The average balance for the account instance.' + }, + minimumBalance: { + type: 'number', + description: 'The minimum balance for the account instance.' + } + } + } +}; diff --git a/packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts new file mode 100644 index 000000000000..c21ea080c5f2 --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts @@ -0,0 +1,30 @@ +import { api } from '@loopback/core'; +import { def } from './AccountController.api'; +import { AccountRepository } from '../repositories/account'; +import { inject } from '@loopback/context'; +import { Account } from '../repositories/account/models/Account'; + +@api(def) +export class AccountController { + @inject('repositories.account') private repository: AccountRepository + constructor() {} + + //fixme figure out how to use Filter interface + //fixme filter is string even though swagger spec + //defines it as object type + async getAccount(filter: string): Promise { + return await this.repository.find(JSON.parse(filter)); + } + + async createAccount(accountInstance: Object): Promise { + return await this.repository.create(accountInstance); + } + + async updateById(id: string, data: Object) { + return await this.repository.updateById(id, data); + } + + async deleteById(id: string) { + return await this.repository.deleteById(id); + } +} diff --git a/packages/example-microservices/services/account-without-juggler/index.ts b/packages/example-microservices/services/account-without-juggler/index.ts new file mode 100644 index 000000000000..be5f65a07a18 --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/index.ts @@ -0,0 +1,42 @@ +import { Application } from '@loopback/core'; +import { AccountController } from './controllers/AccountController'; +import { AccountRepository } from './repositories/account'; + +class AccountMicroservice extends Application { + private _startTime: Date; + + constructor() { + super(); + + const app = this; + app.controller(AccountController); + app.bind('http.port').to(3001); + app.bind('repositories.account').toClass(AccountRepository); + } + + async start() { + this._startTime = new Date(); + return super.start(); + } + + async info() { + const port: Number = await this.get('http.port'); + + return { + appName: "account-without-juggler", + uptime: Date.now() - this._startTime.getTime(), + url: 'http://127.0.0.1:' + port, + }; + } +} + +async function main(): Promise { + const app = new AccountMicroservice(); + await app.start(); + console.log('Application Info:', await app.info()); +} + +main().catch(err => { + console.log('Cannot start the app.', err); + process.exit(1); +}); diff --git a/packages/example-microservices/services/account-without-juggler/package.json b/packages/example-microservices/services/account-without-juggler/package.json new file mode 100644 index 000000000000..1801affe1214 --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/package.json @@ -0,0 +1,35 @@ +{ + "name": "account", + "version": "1.0.0", + "description": "The Account microservice.", + "main": "index.ts", + "dependencies": { + "mysql": "^2.13.0", + "mysql-promise": "^4.1.0", + "@loopback/core": "^4.0.0-alpha.10", + "@loopback/repository": "^4.0.0-alpha.5", + "loopback-datasource-juggler": "^3.4.1" + }, + "devDependencies": { + "@loopback/testlab": "^4.0.0-alpha.6", + "@types/node": "^7.0.12", + "debug": "^2.6.8", + "lodash": "^4.17.4", + "mocha": "^3.4.2", + "ts-node": "^3.0.4", + "tslint": "^5.4.3", + "typescript": "^2.4.1" + }, + "scripts": { + "start": "ts-node index.ts", + "test": "mocha --opts test/mocha.opts 'test/unit/*.ts'" + }, + "keywords": [ + "loopback-next", + "example", + "account", + "microservice" + ], + "author": "IBM", + "license": "MIT" +} diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysql.json b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysql.json new file mode 100644 index 000000000000..2a9e75938555 --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysql.json @@ -0,0 +1,7 @@ +{ + "host":"localhost", + "user":"root", + "password": "pass", + "database": "testdb", + "port": 3306 +} diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts new file mode 100644 index 000000000000..a46b93160f49 --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts @@ -0,0 +1,205 @@ +const debug = require('debug')('loopback:repositories:account:datasources:connections:mysql'); +const mysql = require('mysql'); +const db = require('mysql-promise')(); +import { + Class, + CrudConnector, + DataSource, + Entity, + EntityData, + Filter, + ObjectType, + Options, + Where +} from '@loopback/repository'; + +export class MySqlConn implements CrudConnector { + //fixme make connection strongly typed + private connection: any + + constructor(config: Object) { + db.configure(config, mysql); + this.connection = db; + } + name: 'mysql'; + interfaces?: string[]; + connect(): Promise { + return this.connection.connect(); + } + disconnect(): Promise { + return this.connection.end(); + } + ping(): Promise { + return this.connection.ping(); + } + + updateAll( + modelClass: Class, + data: EntityData, + where: Where, + options: Options + ): Promise { + throw new Error('Not implemented yet.'); + } + + create( + modelClass: Class, + entity: EntityData, + options: Options + ): Promise { + let self = this; + let placeHolders = []; + for (var prop in modelClass.definition.properties) { + placeHolders.push('?'); + } + let createQuery = 'INSERT INTO ?? VALUES (' + placeHolders.join(',') + ')'; + var vals = [modelClass.modelName]; + for (var prop in entity) { + vals.push(entity[prop]); + } + let sqlStmt = mysql.format(createQuery, vals); + debug('Insert ', sqlStmt); + + return self.connection.query(sqlStmt).spread(function(result: any) { + if (result) { + //MySQL returns count of affected rows, but as part of our API + //definition, we return the instance we used to create the row + return entity; + } + }); + } + + save( + modelClass: Class, + entity: EntityData, + options: Options + ): Promise { + throw new Error('Not implemented yet.'); + } + + find( + modelClass: Class, + filter: Filter, + options: Options + ): Promise { + let self = this; + let findQuery = 'SELECT * FROM ?? '; + findQuery = mysql.format(findQuery, [modelClass.modelName]); + if (filter.where) { + let whereClause = '?? = ?'; + for (var key in filter.where) { + whereClause = mysql.format(whereClause, [key, filter.where[key]]); + } + findQuery += ' WHERE ' + whereClause; + } + debug('Find ', findQuery); + return self.connection.query(findQuery).spread(function(rows: any) { + return rows; + }); + } + + findById( + modelClass: Class, + id: any, + options: Options + ): Promise { + throw new Error('Not implemented yet.'); + } + + update( + modelClass: Class, + entity: EntityData, + options: Options + ): Promise { + throw new Error('Not implemented yet.'); + } + + delete( + modelClass: Class, + entity: EntityData, + options: Options + ): Promise { + throw new Error('Not implemented yet.'); + } + + createAll( + modelClass: Class, + entities: EntityData[], + options: Options + ): Promise { + throw new Error('Not implemented yet.'); + } + + updateById( + modelClass: Class, + id: any, + data: EntityData, + options: Options + ): Promise { + let self = this; + let updateQuery = 'UPDATE ?? SET '; + updateQuery = mysql.format(updateQuery, [modelClass.modelName]); + let updateClause = []; + for (var prop in data) { + updateClause.push(mysql.format('??=?', [prop, data[prop]])); + } + updateQuery += updateClause.join(','); + let whereClause = mysql.format(' WHERE ??=?', ['id', id]); + updateQuery += whereClause; + + debug('updateById ', updateQuery); + return self.connection.query(updateQuery).spread(function(result: any) { + return result.affectedRows; + }); + } + + replaceById( + modelClass: Class, + id: any, + data: EntityData, + options: Options + ): Promise { + throw new Error('Not implemented yet.'); + } + + deleteAll( + modelClass: Class, + where: Where, + options: Options + ): Promise { + throw new Error('Not implemented yet.'); + } + + deleteById( + modelClass: Class, + id: any, + options: Options + ): Promise { + let self = this; + let deleteQuery = 'DELETE FROM ?? '; + deleteQuery = mysql.format(deleteQuery, modelClass.modelName); + let whereClause = mysql.format(' WHERE ??=?', ['id', id]); + deleteQuery += whereClause; + + debug('deleteById ', deleteQuery); + return self.connection.query(deleteQuery).spread(function(result: any) { + return result.affectedRows; + }); + } + + count( + modelClass: Class, + where: Where, + options: Options + ): Promise { + throw new Error('Not implemented yet.'); + } + + exists( + modelClass: Class, + id: any, + options: Options + ): Promise { + throw new Error('Not implemented yet.'); + } +} diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlds.ts b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlds.ts new file mode 100644 index 000000000000..7754c9c655ed --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlds.ts @@ -0,0 +1,15 @@ +import { MySqlConn } from './mysqlconn'; +import { DataSource } from '@loopback/repository'; +const mysqlCreds = require('./mysql.json'); + +export class MySqlDs implements DataSource { + name: 'mysqlDs'; + connector: MySqlConn; + settings: Object; + + constructor() { + this.settings = mysqlCreds; + this.connector = new MySqlConn(this.settings); + } +} + diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/index.ts b/packages/example-microservices/services/account-without-juggler/repositories/account/index.ts new file mode 100644 index 000000000000..202245b0c1c3 --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/index.ts @@ -0,0 +1,11 @@ +import { CrudRepositoryImpl } from '@loopback/repository'; +import { MySqlDs } from './datasources/mysqlds'; +import { Account } from './models/Account'; + + +export class AccountRepository extends CrudRepositoryImpl { + constructor() { + const ds = new MySqlDs(); + super(ds, Account); + } +} diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts b/packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts new file mode 100644 index 000000000000..0f75d9fdbe87 --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts @@ -0,0 +1,27 @@ +import { + Entity, + model, + ModelDefinition, + PropertyDefinition +} from '@loopback/repository'; + +@model(require('./account/model-definition')) +export class Account extends Entity { + static definition = new ModelDefinition('Account', require('./account/model-definition').properties); + static modelName = 'Account'; + + id: string; + customerNumber: string; + balance: number; + branch: string; + type: string; + avgBalance: number; + minimumBalance: number; + + constructor(body?: Partial) { + super(); + if (body) { + Object.assign(this, body); + } + } +} diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/models/account/model-definition.js b/packages/example-microservices/services/account-without-juggler/repositories/account/models/account/model-definition.js new file mode 100644 index 000000000000..c0ee62a739f9 --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/models/account/model-definition.js @@ -0,0 +1,34 @@ +module.exports = { + name: 'Account', + properties: { + id: { + type: 'string', + required: true, + id: true + }, + customerNumber: { + type: 'string', + required: true + }, + balance: { + type: 'number', + required: true + }, + branch: { + type: 'string', + required: true + }, + type: { + type: 'string', + required: true + }, + avgBalance: { + type: 'number', + required: true + }, + minimumBalance: { + type: 'number', + required: true + } + } +}; diff --git a/packages/example-microservices/services/account-without-juggler/test/mocha.opts b/packages/example-microservices/services/account-without-juggler/test/mocha.opts new file mode 100644 index 000000000000..e6331ca64c27 --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/test/mocha.opts @@ -0,0 +1,2 @@ +--compilers ts:ts-node/register +--recursive diff --git a/packages/example-microservices/services/account-without-juggler/test/unit/account-controller.ts b/packages/example-microservices/services/account-without-juggler/test/unit/account-controller.ts new file mode 100644 index 000000000000..8ad854929b3d --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/test/unit/account-controller.ts @@ -0,0 +1,74 @@ +import 'mocha'; +import { AccountController } from '../../controllers/AccountController'; +import { expect } from '@loopback/testlab'; +import { AccountRepository } from '../../repositories/account'; + +let testController: any; + +const testAcc = { + id: 'test1', + customerNumber: '1234', + balance: 1000, + branch: 'Toronto', + type: 'Chequing', + avgBalance: 500, + minimumBalance: 0 +}; + +const brokenAcc = { + customerNumber: '123456', + balance: 1000, + branch: 'Broke City', + type: 'Chequing' +}; + +describe('AccountController Unit Test Suite', () => { + before(createAccountController); + + it('creates an account instance', async () => { + const result = await testController.createAccount(testAcc); + expect(result).to.deepEqual(testAcc); + const getResult = await testController.getAccount('{"where":{"id":"test1"}}'); + expect(getResult).to.not.be.empty(); + expect(getResult).have.lengthOf(1); + expect(getResult[0]).to.deepEqual(testAcc); + }); + it('should not create an invalid instance', async () => { + try { + await testController.createAccount(brokenAcc); + } catch (err) { + expect(err).to.not.be.empty(); + } + }); + it('should not accept invalid args', async () => { + try { + await testController.getAccount(''); + } catch (err) { + expect(err).to.not.be.empty(); + } + }); + + it('updates an account instance', async () => { + const result = await testController.updateAccount('{"id":"test1"}}', { + balance: 2000 + }); + expect(result.count).to.be.equal(1); + const getResult = await testController.getAccount('{"where":{"id":"test1"}}'); + expect(getResult).to.not.be.empty(); + expect(getResult).have.lengthOf(1); + expect(getResult[0].id).to.be.equal(testAcc.id); + expect(getResult[0].toObject().balance).to.be.equal(2000); + }); + + it('deletes an account instance', async () => { + const result = await testController.deleteAccount('{"id":"test1"}}'); + expect(result.count).to.be.equal(1); + const getResult = await testController.getAccount('{"where":{"id":"test1"}}'); + expect(getResult).to.be.empty(); + }); +}); + +function createAccountController() { + testController = new AccountController(); + testController.repository = new AccountRepository(); +} diff --git a/packages/example-microservices/services/account-without-juggler/tsconfig.common.json b/packages/example-microservices/services/account-without-juggler/tsconfig.common.json new file mode 100644 index 000000000000..1d8e30dea1ea --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/tsconfig.common.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + "compilerOptions": { + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "noImplicitAny": true, + "strictNullChecks": true, + + "lib": ["es2017", "dom"], + "module": "commonjs", + "moduleResolution": "node", + "target": "es6", + "sourceMap": true, + "declaration": true + } +} diff --git a/packages/example-microservices/services/account-without-juggler/tsconfig.json b/packages/example-microservices/services/account-without-juggler/tsconfig.json new file mode 100644 index 000000000000..81928c0155b1 --- /dev/null +++ b/packages/example-microservices/services/account-without-juggler/tsconfig.json @@ -0,0 +1,30 @@ +{ + "extends": "./tsconfig.common.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "*": [ + "*" + ] + }, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "lib": [ + "es6", + "dom" + ], + "module": "commonjs", + "moduleResolution": "node", + "target": "es6", + "typeRoots": [ + "node_modules/@types" + ], + "inlineSources": true + }, + "include": [ + "**/*" + ], + "exclude": [ + "**/*.d.ts" + ] +} diff --git a/packages/example-microservices/services/account/bin/create-account b/packages/example-microservices/services/account/bin/create-account new file mode 100755 index 000000000000..2626106f872b --- /dev/null +++ b/packages/example-microservices/services/account/bin/create-account @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +curl -H "Content-Type: application/json" -X POST -d '{"id":"test1", "customerNumber": "1234", "balance":1000, "branch":"Toronto", "type":"Chequing", "avgBalance":500, "minimumBalance":0}' 'http://localhost:3001/accounts/create' | jq diff --git a/packages/example-microservices/services/account/bin/delete-account b/packages/example-microservices/services/account/bin/delete-account new file mode 100755 index 000000000000..feac770cf061 --- /dev/null +++ b/packages/example-microservices/services/account/bin/delete-account @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +curl -g -H "Content-Type: application/json" -X DELETE 'http://localhost:3001/accounts/delete?where={"id":"test1"}' | jq diff --git a/packages/example-microservices/services/account/bin/get-account b/packages/example-microservices/services/account/bin/get-account new file mode 100755 index 000000000000..7a85db2be743 --- /dev/null +++ b/packages/example-microservices/services/account/bin/get-account @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +curl -g 'http://localhost:3001/accounts?filter={"where":{"id":"test1"}}' | jq diff --git a/packages/example-microservices/services/account/bin/update-account b/packages/example-microservices/services/account/bin/update-account new file mode 100755 index 000000000000..143f8f368e17 --- /dev/null +++ b/packages/example-microservices/services/account/bin/update-account @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +curl -g -H "Content-Type: application/json" -X POST -d '{"balance":2000}' 'http://localhost:3001/accounts/update?where={"id":"test1"}' | jq diff --git a/packages/example-microservices/services/account/controllers/AccountController.api.ts b/packages/example-microservices/services/account/controllers/AccountController.api.ts new file mode 100644 index 000000000000..bb4dacfe281b --- /dev/null +++ b/packages/example-microservices/services/account/controllers/AccountController.api.ts @@ -0,0 +1,114 @@ +export const def = { + basePath: '/', + paths: { + '/accounts': { + get: { + 'x-operation-name': 'getAccount', + parameters: [ + { + name: 'filter', + in: 'query', + description: 'The criteria used to narrow down the number of accounts returned.', + required: false, + type: 'object' + } + ], + responses: { + 200: { + schema: { + type: 'array', + items: '#/definitions/Account' + }, + }, + }, + }, + }, + '/accounts/create': { + post: { + 'x-operation-name': 'createAccount', + parameters: [ + { + name: 'accountInstance', + in: 'body', + description: 'The account instance to create.', + required: true, + type: 'object' + }, + ], + responses: { + 200: { + schema: { + accountInstance: "#/definitions/Account" + }, + }, + }, + }, + }, + '/accounts/update': { + post: { + 'x-operation-name': 'updateAccount', + parameters: [ + { + name: 'where', + in: 'query', + description: 'The criteria used to narrow down the number of accounts returned.', + required: false, + type: 'object' + }, + { + name: 'data', + in: 'body', + description: 'An object of model property name/value pairs', + required: true, + type: 'object' + } + ], + responses: { + 200: { + schema: { + type: 'object', + description: 'update information', + properties: { + count: { + type: 'number', + description: 'number of records updated' + } + } + }, + }, + }, + } + }, + '/accounts/delete': { + delete: { + 'x-operation-name': 'deleteAccount', + parameters: [ + { + name: 'where', + in: 'query', + description: 'The criteria used to narrow down which account instances to delete.', + required: true, + type:'object' + } + ], + responses: { + 200: { + schema: { + type: 'object', + description: 'delete information', + properties: { + count: { + type: 'number', + description: 'number of records deleted' + } + } + }, + }, + }, + } + } + }, + definitions: { + Account: require('../repositories/account/models/account/model-definition.json') + } +}; diff --git a/packages/example-microservices/services/account/controllers/AccountController.ts b/packages/example-microservices/services/account/controllers/AccountController.ts new file mode 100644 index 000000000000..269a68d69f72 --- /dev/null +++ b/packages/example-microservices/services/account/controllers/AccountController.ts @@ -0,0 +1,29 @@ +import { Filter, Where } from '@loopback/repository'; +import { api } from '@loopback/core'; +import { def } from './AccountController.api'; +import { AccountRepository } from '../repositories/account'; + +@api(def) +export class AccountController { + repository: AccountRepository; + + constructor() { + this.repository = new AccountRepository(); + } + + async getAccount(filter) { + return await this.repository.find(JSON.parse(filter)); + } + + async createAccount(accountInstance) { + return await this.repository.create(accountInstance); + } + + async updateAccount(where, data) { + return await this.repository.update(JSON.parse(where), data); + } + + async deleteAccount(where) { + return await this.repository.deleteAccount(JSON.parse(where)); + } +} diff --git a/packages/example-microservices/services/account/index.ts b/packages/example-microservices/services/account/index.ts new file mode 100644 index 000000000000..34f870e9540c --- /dev/null +++ b/packages/example-microservices/services/account/index.ts @@ -0,0 +1,39 @@ +import { Application } from '@loopback/core'; +import { AccountController } from './controllers/AccountController'; + +class AccountMicroservice extends Application { + private _startTime: Date; + + constructor() { + super(); + const app = this; + app.bind('http.port').to(3001); + app.controller(AccountController); + } + + async start() { + this._startTime = new Date(); + return super.start(); + } + + async info() { + const port: Number = await this.get('http.port'); + + return { + appName: "account", + uptime: Date.now() - this._startTime.getTime(), + url: 'http://127.0.0.1:' + port, + }; + } +} + +async function main(): Promise { + const app = new AccountMicroservice(); + await app.start(); + console.log('Application Info:', await app.info()); +} + +main().catch(err => { + console.log('Cannot start the app.', err); + process.exit(1); +}); diff --git a/packages/example-microservices/services/account/package.json b/packages/example-microservices/services/account/package.json new file mode 100644 index 000000000000..1baf06330012 --- /dev/null +++ b/packages/example-microservices/services/account/package.json @@ -0,0 +1,32 @@ +{ + "name": "account", + "version": "1.0.0", + "description": "The Account microservice.", + "main": "index.ts", + "dependencies": { + "@loopback/core": "^4.0.0-alpha.10", + "@loopback/repository": "^4.0.0-alpha.5", + "loopback-connector-mysql": "^4.2.0", + "loopback-datasource-juggler": "^3.4.1", + "ts-node": "^3.1.0", + "typescript": "^2.3.4" + }, + "devDependencies": { + "@loopback/testlab": "^4.0.0-alpha.6", + "@types/node": "^7.0.12", + "mocha": "^3.4.2", + "@types/mocha": "^2.2.41" + }, + "scripts": { + "start": "ts-node index.ts", + "test": "mocha --opts test/mocha.opts 'test/unit/*.ts'" + }, + "keywords": [ + "loopback-next", + "example", + "account", + "microservice" + ], + "author": "IBM", + "license": "MIT" +} diff --git a/packages/example-microservices/services/account/repositories/account/datasources/local-fs/data.json b/packages/example-microservices/services/account/repositories/account/datasources/local-fs/data.json new file mode 100644 index 000000000000..91fbc61d791b --- /dev/null +++ b/packages/example-microservices/services/account/repositories/account/datasources/local-fs/data.json @@ -0,0 +1,13 @@ +{ + "ids": { + "Account": 4 + }, + "models": { + "Account": { + "CHK52321122": "{\"id\":\"CHK52321122\",\"customerNumber\":\"000343223\",\"balance\":85.84,\"branch\":\"Foster City\",\"type\":\"Checking\",\"avgBalance\":398.93,\"minimumBalance\":10}", + "CHK54520000": "{\"id\":\"CHK54520000\",\"customerNumber\":\"003499223\",\"balance\":99.99,\"branch\":\"Foster City\",\"type\":\"Checking\",\"avgBalance\":500.93,\"minimumBalance\":10}", + "CHK52321199": "{\"id\":\"CHK52321199\",\"customerNumber\":\"000343223\",\"balance\":109.89,\"branch\":\"Foster City\",\"type\":\"Checking\",\"avgBalance\":100.93,\"minimumBalance\":10}", + "CHK99999999": "{\"id\":\"CHK99999999\",\"customerNumber\":\"0002444422\",\"balance\":100.89,\"branch\":\"Foster City\",\"type\":\"Checking\",\"avgBalance\":100.93,\"minimumBalance\":10}" + } + } +} \ No newline at end of file diff --git a/packages/example-microservices/services/account/repositories/account/index.ts b/packages/example-microservices/services/account/repositories/account/index.ts new file mode 100644 index 000000000000..34c64827dd2a --- /dev/null +++ b/packages/example-microservices/services/account/repositories/account/index.ts @@ -0,0 +1,31 @@ +import { juggler, DataSourceConstructor } from '@loopback/repository'; +const modelDefinition = require('./models/account/model-definition.json'); + +export class AccountRepository { + model; + + constructor(file?:string) { + const ds: juggler.DataSource = new DataSourceConstructor('local-fs', { + connector: 'memory', + file: file || './repositories/account/datasources/local-fs/data.json' + }); + + this.model = ds.createModel('Account', modelDefinition.properties, {}); + } + + async find(filter): Promise { + return await this.model.find(filter); + } + + async create(accountInstance): Promise { + return await this.model.create(accountInstance); + } + + async update(where, data): Promise { + return await this.model.updateAll(where, data, {}); + } + + async deleteAccount(where): Promise { + return await this.model.destroyAll(where); + } +} diff --git a/packages/example-microservices/services/account/repositories/account/models/account/model-definition.json b/packages/example-microservices/services/account/repositories/account/models/account/model-definition.json new file mode 100644 index 000000000000..dc4ffad065c9 --- /dev/null +++ b/packages/example-microservices/services/account/repositories/account/models/account/model-definition.json @@ -0,0 +1,34 @@ +{ + "name": "Account", + "properties": { + "id": { + "type": "string", + "required": true, + "id": true + }, + "customerNumber": { + "type": "string", + "required": true + }, + "balance": { + "type": "number", + "required": true + }, + "branch": { + "type": "string", + "required": true + }, + "type": { + "type": "string", + "required": true + }, + "avgBalance": { + "type": "number", + "required": true + }, + "minimumBalance": { + "type": "number", + "required": true + } + } +} \ No newline at end of file diff --git a/packages/example-microservices/services/account/test/mocha.opts b/packages/example-microservices/services/account/test/mocha.opts new file mode 100644 index 000000000000..e6331ca64c27 --- /dev/null +++ b/packages/example-microservices/services/account/test/mocha.opts @@ -0,0 +1,2 @@ +--compilers ts:ts-node/register +--recursive diff --git a/packages/example-microservices/services/account/test/unit/test.data.json b/packages/example-microservices/services/account/test/unit/test.data.json new file mode 100644 index 000000000000..42e6f55d2753 --- /dev/null +++ b/packages/example-microservices/services/account/test/unit/test.data.json @@ -0,0 +1,13 @@ +{ + "ids": { + "Account": 17 + }, + "models": { + "Account": { + "CHK52321122": "{\"id\":\"CHK52321122\",\"customerNumber\":\"000343223\",\"balance\":85.84,\"branch\":\"Foster City\",\"type\":\"Checking\",\"avgBalance\":398.93,\"minimumBalance\":10}", + "CHK54520000": "{\"id\":\"CHK54520000\",\"customerNumber\":\"003499223\",\"balance\":99.99,\"branch\":\"Foster City\",\"type\":\"Checking\",\"avgBalance\":500.93,\"minimumBalance\":10}", + "CHK52321199": "{\"id\":\"CHK52321199\",\"customerNumber\":\"000343223\",\"balance\":109.89,\"branch\":\"Foster City\",\"type\":\"Checking\",\"avgBalance\":100.93,\"minimumBalance\":10}", + "CHK99999999": "{\"id\":\"CHK99999999\",\"customerNumber\":\"0002444422\",\"balance\":100.89,\"branch\":\"Foster City\",\"type\":\"Checking\",\"avgBalance\":100.93,\"minimumBalance\":10}" + } + } +} diff --git a/packages/example-microservices/services/account/test/unit/test.ts b/packages/example-microservices/services/account/test/unit/test.ts new file mode 100644 index 000000000000..d505e706ad8c --- /dev/null +++ b/packages/example-microservices/services/account/test/unit/test.ts @@ -0,0 +1,145 @@ +// test/unit/test.js +import 'mocha'; +import { AccountController } from "../../controllers/AccountController"; +import { expect } from "@loopback/testlab"; +import { AccountRepository } from "../../repositories/account"; +import * as path from 'path'; + +let accCtrl; + +const testAcc = { + id: 'test1', + customerNumber: '1234', + balance: 1000, + branch: 'Toronto', + type: 'Chequing', + avgBalance: 500, + minimumBalance: 0 +}; + +const brokenAcc = { + customerNumber: '123456', + balance: 1000, + branch: 'Broke City', + type: 'Chequing' +}; + +describe("AccountController Unit Test Suite", () => { + before(createAccountController); + + describe('AccountController.getAccount("{}")', () => { + it("returns an array of all accounts", async () => { + const result = await accCtrl.getAccount("{}"); + expect(result).to.not.be.empty(); + expect(result).have.lengthOf(4); + expect(result[0].id).to.equalOneOf([ + "CHK52321122", + "CHK54520000", + "CHK52321199", + "CHK99999999" + ]); + }); + }); + + describe('AccountController.getAccount("")', () => { + it("rejects promise for invalid args", async () => { + let flag = true; + try { + await accCtrl.getAccount(""); + } catch (err) { + flag = false; + } + expect(flag).to.be.false(); + }); + }); + + describe('AccountController.getAccount("{"where":{"id":"test1"}}")', () => { + it("searches and returns an empty array", async () => { + const result = await accCtrl.getAccount('{"where":{"id":"test1"}}'); + expect(result).to.be.empty(); + }); + }); + + describe("AccountController.createAccount(testAcc)", () => { + it("should create an account", async () => { + const result = await accCtrl.createAccount(testAcc); + expect(JSON.stringify(result)).to.equal(JSON.stringify(testAcc)); + }); + }); + + describe('AccountController.getAccount("{"where":{"id":"test1"}}")', () => { + it("searches and returns newly created account", async () => { + const result = await accCtrl.getAccount('{"where":{"id":"test1"}}'); + expect(result).to.not.be.empty(); + expect(result).have.lengthOf(1); + expect(result[0].id).to.be.equal(testAcc.id); + }); + }); + + describe("AccountController.createAccount(brokenAcc)", () => { + it("fails to create with an Invalid Account instance.", async () => { + let works = true; + try { + await accCtrl.createAccount(brokenAcc); + } catch (err) { + works = false; + } + expect(works).to.be.false(); + }); + }); + + describe('AccountController.updateAccount("{"id":"test1"}", {"balance":2000})', () => { + it("updates an Account instance", async () => { + const result = await accCtrl.updateAccount('{"id":"test1"}', { + balance: 2000 + }); + expect(result.count).to.be.equal(1); + }); + }); + + describe('AccountController.getAccount("{"where":{"id":"test1"}}")', () => { + it("returns account with updated balance", async () => { + const result = await accCtrl.getAccount('{"where":{"id":"test1"}}'); + expect(result).to.not.be.empty(); + expect(result).have.lengthOf(1); + expect(result[0].id).to.be.equal(testAcc.id); + expect(JSON.parse(JSON.stringify(result[0])).balance).to.be.equal(2000); + }); + }); + + describe('AccountController.deleteAccount("{"id":"test1"}")', () => { + it("deletes the Account instance", async () => { + const result = await accCtrl.deleteAccount('{"id":"test1"}'); + expect(result.count).to.be.equal(1); + }); + }); + + describe('AccountController.getAccount("{"where":{"id":"test1"}}")', () => { + it("searches and returns an empty array", async () => { + const result = await accCtrl.getAccount('{"where":{"id":"test1"}}'); + expect(result).to.be.empty(); + }); + }); + + describe('AccountController.getAccount("{}")', () => { + it("returns an array of all accounts", async () => { + const result = await accCtrl.getAccount("{}"); + expect(result).to.not.be.empty(); + expect(result).have.lengthOf(4); + expect(result[0].id).to.equalOneOf([ + "CHK52321122", + "CHK54520000", + "CHK52321199", + "CHK99999999" + ]); + }); + }); +}); + +function createAccountController() { + accCtrl = new AccountController(); + + accCtrl.repository = new AccountRepository( + path.resolve(__dirname, "test.data.json") + ); +} diff --git a/packages/example-microservices/services/account/tsconfig.json b/packages/example-microservices/services/account/tsconfig.json new file mode 100644 index 000000000000..741c22be99a3 --- /dev/null +++ b/packages/example-microservices/services/account/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "*": [ + "*" + ] + }, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "lib": [ + "es6", + "dom" + ], + "module": "commonjs", + "moduleResolution": "node", + "target": "es6", + "typeRoots": [ + "node_modules/@types" + ], + "inlineSources": true + }, + "include": [ + "**/*" + ], + "exclude": [ + "**/*.d.ts" + ] +} \ No newline at end of file diff --git a/packages/example-microservices/services/customer/bin/get-customers b/packages/example-microservices/services/customer/bin/get-customers new file mode 100755 index 000000000000..e54e10272d76 --- /dev/null +++ b/packages/example-microservices/services/customer/bin/get-customers @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +curl -s localhost:3002/customers | jq \ No newline at end of file diff --git a/packages/example-microservices/services/customer/controllers/CustomerController.api.ts b/packages/example-microservices/services/customer/controllers/CustomerController.api.ts new file mode 100644 index 000000000000..384f642d4ef7 --- /dev/null +++ b/packages/example-microservices/services/customer/controllers/CustomerController.api.ts @@ -0,0 +1,74 @@ +export const def = { + basePath: '/', + paths: { + '/customers': { + get: { + 'x-operation-name': 'getCustomers', + parameters: [ + { + name: 'id', + in: 'query', + description: 'The customer id.', + required: true, + type: 'string', + format: 'JSON' + }, + { + name: 'filter', + in: 'query', + description: 'The criteria used to narrow down the number of customers returned.', + required: false, + type: 'string', + format: 'JSON' + } + ], + responses: { + 200: { + schema: { + customerNumber: { + type: 'string', + description: 'The customer number.', + }, + firstName: { + type: 'string', + description: 'The customer\'s first name.', + }, + lastName: { + type: 'string', + description: 'The customer\'s last name.', + }, + ssn: { + type: 'string', + description: 'The customer\'s social security number.', + }, + customerSince: { + type: 'datetime', + description: 'The customer\'s registration date.' + }, + street: { + type: 'string', + description: 'The street name of the customer\'s address.', + }, + state: { + type: 'string', + description: 'The state of the customer\'s address.', + }, + city: { + type: 'string', + description: 'The city of the customer\'s address.', + }, + zip: { + type: 'string', + description: 'The zip code of the customer\'s address.', + }, + lastUpdated: { + type: 'string', + description: 'The last time the customer\'s information was updated.', + }, + }, + }, + }, + }, + }, + }, +}; diff --git a/packages/example-microservices/services/customer/controllers/CustomerController.ts b/packages/example-microservices/services/customer/controllers/CustomerController.ts new file mode 100644 index 000000000000..e798ed1f1424 --- /dev/null +++ b/packages/example-microservices/services/customer/controllers/CustomerController.ts @@ -0,0 +1,16 @@ +import { api } from '@loopback/core'; +import { def } from './CustomerController.api'; +import { CustomerRepository } from '../repositories/customer'; + +@api(def) +export class CustomerController { + repository: CustomerRepository; + + constructor() { + this.repository = new CustomerRepository(); + } + + async getCustomers(filter): Promise { + return await this.repository.find(filter); + } +} diff --git a/packages/example-microservices/services/customer/index.ts b/packages/example-microservices/services/customer/index.ts new file mode 100644 index 000000000000..7a7bbd2e8d56 --- /dev/null +++ b/packages/example-microservices/services/customer/index.ts @@ -0,0 +1,39 @@ +import { Application } from '@loopback/core'; +import { CustomerController } from './controllers/CustomerController'; + +class CustomerApplication extends Application { + private _startTime: Date; + + constructor() { + super(); + const app = this; + app.controller(CustomerController); + app.bind('http.port').to(3002); + } + + async start() { + this._startTime = new Date(); + return super.start(); + } + + async info() { + const port: Number = await this.get('http.port'); + + return { + appName: "customer", + uptime: Date.now() - this._startTime.getTime(), + url: 'http://127.0.0.1:' + port, + }; + } +} + +async function main(): Promise { + const app = new CustomerApplication(); + await app.start(); + console.log('Application Info:', await app.info()); +} + +main().catch(err => { + console.log('Cannot start the app.', err); + process.exit(1); +}); diff --git a/packages/example-microservices/services/customer/package.json b/packages/example-microservices/services/customer/package.json new file mode 100644 index 000000000000..93ef3dddc302 --- /dev/null +++ b/packages/example-microservices/services/customer/package.json @@ -0,0 +1,26 @@ +{ + "name": "customer", + "version": "1.0.0", + "description": "The Customer microservice.", + "main": "index.ts", + "dependencies": { + "@loopback/core": "^4.0.0-alpha.10", + "@loopback/repository": "^4.0.0-alpha.5", + "loopback-datasource-juggler": "^3.4.1" + }, + "devDependencies": { + "@types/node": "^7.0.12" + }, + "scripts": { + "start": "ts-node index.ts", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "loopback-next", + "example", + "customer", + "microservice" + ], + "author": "IBM", + "license": "MIT" +} diff --git a/packages/example-microservices/services/customer/repositories/customer/datasources/local-fs/data.json b/packages/example-microservices/services/customer/repositories/customer/datasources/local-fs/data.json new file mode 100644 index 000000000000..2059b6d325c6 --- /dev/null +++ b/packages/example-microservices/services/customer/repositories/customer/datasources/local-fs/data.json @@ -0,0 +1,11 @@ +{ + "ids": { + "Customer": 2 + }, + "models": { + "Customer": { + "000343223": "{\"id\":\"000343223\",\"firstName\":\"Ron\",\"lastName\":\"Simpson\",\"ssn\":\"141-XX-X800\",\"customerSince\":\"2017-03-14T23:05:18.779Z\",\"street\":\"742 Evergreen Terrace\",\"state\":\"OR\",\"city\":\"Springfield\",\"zip\":\"95555\",\"lastUpdated\":\"2017-03-14T23:05:18.599Z\"}", + "003499223": "{\"id\":\"003499223\",\"firstName\":\"Raymond\",\"lastName\":\"Freerich\",\"ssn\":\"342-XX-XX24\",\"customerSince\":\"2017-03-14T23:26:45.225Z\",\"street\":\"3005 chapel ave\",\"state\":\"NewJersey\",\"city\":\"Chapel Ave\",\"zip\":\"08002\",\"lastUpdated\":\"2017-03-14T23:26:45.225Z\"}" + } + } +} \ No newline at end of file diff --git a/packages/example-microservices/services/customer/repositories/customer/index.ts b/packages/example-microservices/services/customer/repositories/customer/index.ts new file mode 100644 index 000000000000..3de894daaeb4 --- /dev/null +++ b/packages/example-microservices/services/customer/repositories/customer/index.ts @@ -0,0 +1,18 @@ +import { juggler, DataSourceConstructor } from '@loopback/repository'; +const modelDefinition = require('./models/customer/model-definition.json'); + +export class CustomerRepository  { + model; + + constructor() { + const ds: juggler.DataSource = new DataSourceConstructor('local-fs', { + connector: 'memory', + file: './repositories/customer/datasources/local-fs/data.json' + }); + this.model = ds.createModel('Customer', modelDefinition); + } + + async find(id): Promise { + return await this.model.find({where: {id: id}}); + } +} diff --git a/packages/example-microservices/services/customer/repositories/customer/models/customer/model-definition.json b/packages/example-microservices/services/customer/repositories/customer/models/customer/model-definition.json new file mode 100644 index 000000000000..9325ca4c326e --- /dev/null +++ b/packages/example-microservices/services/customer/repositories/customer/models/customer/model-definition.json @@ -0,0 +1,37 @@ +{ + "name": "Customer", + "properties": { + "id": { + "type": "string", + "id": true, + "required": true + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "ssn": { + "type": "string" + }, + "customerSince": { + "type": "date" + }, + "street": { + "type": "string" + }, + "state": { + "type": "string" + }, + "city": { + "type": "string" + }, + "zip": { + "type": "string" + }, + "lastUpdated": { + "type": "date" + } + } +} \ No newline at end of file diff --git a/packages/example-microservices/services/customer/tsconfig.json b/packages/example-microservices/services/customer/tsconfig.json new file mode 100644 index 000000000000..741c22be99a3 --- /dev/null +++ b/packages/example-microservices/services/customer/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "*": [ + "*" + ] + }, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "lib": [ + "es6", + "dom" + ], + "module": "commonjs", + "moduleResolution": "node", + "target": "es6", + "typeRoots": [ + "node_modules/@types" + ], + "inlineSources": true + }, + "include": [ + "**/*" + ], + "exclude": [ + "**/*.d.ts" + ] +} \ No newline at end of file diff --git a/packages/example-microservices/services/facade/controllers/AccountManagementController.api.ts b/packages/example-microservices/services/facade/controllers/AccountManagementController.api.ts new file mode 100644 index 000000000000..b652d57706dd --- /dev/null +++ b/packages/example-microservices/services/facade/controllers/AccountManagementController.api.ts @@ -0,0 +1,153 @@ +export const def = { + basePath: '/', + paths: { + '/account/summary': { + get: { + 'x-operation-name': 'getSummary', + parameters: [ + { + name: 'accountNumber', + in: 'query', + description: 'The account number to use when retrieving data from the underlying microservices.', + required: true, + type: 'string', + } + ], + responses: { + 200: { + schema: { + accounts: { + type: 'object', + properties: { + accountNumber: { + type: 'string', + description: 'account number', + }, + customerNumber: { + type: 'string', + description: 'customer number', + }, + type: { + type: 'string', + description: 'savings or checking', + }, + balance: { + type: 'number', + description: 'balance amount', + }, + minimumBalance: { + type: 'number', + description: 'account minimum balance', + }, + avgBalance: { + type: 'number', + description: 'average balance', + } + } + }, + customer: { + type: 'array', + items: [ + { + type: 'object', + properties: { + customerNumber: { + type: 'string', + description: 'The information of customers', + }, + firstName: { + type: 'string', + description: 'Fist Name of a customer', + }, + lastName: { + type: 'string', + description: 'Last Name of a customer', + }, + ssn: { + type: 'string', + description: 'SSN of a customer', + }, + customerSince: { + type: 'datetime', + description: 'Duration of a customer', + }, + street: { + type: 'string', + description: 'street of a customer', + }, + state: { + type: 'string', + description: 'state of a customer', + }, + city: { + type: 'string', + description: 'city of a customer', + }, + zip: { + type: 'string', + description: 'zip of a customer', + }, + lastUpdated: { + type: 'string', + description: 'lastUpdated date of address of customer', + }, + } + } + ] + } + }, + }, + }, + }, + }, + '/account/create': { + post: { + 'x-operation-name': 'createAccount', + parameters: [ + { + name: 'accountInstance', + in: 'body', + description: 'The account instance.', + required: true, + type: 'object', + format: 'JSON' + } + ], + responses: { + 200: { + schema: { + id: { + type: 'string', + description: 'The account id.', + }, + customerNumber: { + type: 'string', + description: 'The customer number.', + }, + balance: { + type: 'number', + description: 'The balance of the account.', + }, + branch: { + type: 'string', + description: 'The bank branch.', + }, + type: { + type: 'string', + description: 'The type of account ("savings" or "chequing").', + }, + minimumBalance: { + type: 'number', + description: 'The minimum balance for the account.', + }, + avgBalance: { + type: 'number', + description: 'The average balance of the account.', + }, + }, + }, + }, + }, + } + }, +}; diff --git a/packages/example-microservices/services/facade/controllers/AccountManagementController.ts b/packages/example-microservices/services/facade/controllers/AccountManagementController.ts new file mode 100644 index 000000000000..4f075dbd7b21 --- /dev/null +++ b/packages/example-microservices/services/facade/controllers/AccountManagementController.ts @@ -0,0 +1,39 @@ +import { api } from '@loopback/core'; +import { def } from './AccountManagementController.api'; +import { AccountRepository } from '../repositories/account'; +import { CustomerRepository } from '../repositories/customer'; +import { TransactionRepository } from '../repositories/transaction'; +import bluebird = require('bluebird'); + +@api(def) +export class AccountController { + accountRepository: AccountRepository; + customerRepository: CustomerRepository; + transactionRepository: TransactionRepository; + + constructor() { + this.accountRepository = new AccountRepository(); + this.customerRepository = new CustomerRepository(); + this.transactionRepository = new TransactionRepository(); + } + + async getSummary(accountNumber): Promise { + const account = await this.accountRepository.find(accountNumber); + const summary = await bluebird.props({ + account: account, + customer: this.customerRepository.find(account.customerNumber), + transaction: this.transactionRepository.find(accountNumber), + }); + return JSON.stringify(summary); + } + + async getAccount(accountNumber): Promise { + const account = await this.accountRepository.find(accountNumber); + return JSON.stringify(account); + } + + async createAccount(accountInstance): Promise { + const account = await this.accountRepository.create(accountInstance); + return JSON.stringify(account); + } +} diff --git a/packages/example-microservices/services/facade/index.ts b/packages/example-microservices/services/facade/index.ts new file mode 100644 index 000000000000..65c1a999450a --- /dev/null +++ b/packages/example-microservices/services/facade/index.ts @@ -0,0 +1,37 @@ +import { Application } from '@loopback/core'; +import { AccountController } from './controllers/AccountManagementController'; + +class FacadeMicroservice extends Application { + private _startTime: Date; + + constructor() { + super(); + this.controller(AccountController); + } + + async start() { + this._startTime = new Date(); + return super.start(); + } + + async info() { + const port: Number = await this.get('http.port'); + + return { + appName: "facade", + uptime: Date.now() - this._startTime.getTime(), + url: 'http://127.0.0.1:' + port, + }; + } +} + +async function main(): Promise { + const app = new FacadeMicroservice(); + await app.start(); + console.log('Application Info:', await app.info()); +} + +main().catch(err => { + console.log('Cannot start the app.', err); + process.exit(1); +}); diff --git a/packages/example-microservices/services/facade/package.json b/packages/example-microservices/services/facade/package.json new file mode 100644 index 000000000000..b78b3a22c3c4 --- /dev/null +++ b/packages/example-microservices/services/facade/package.json @@ -0,0 +1,27 @@ +{ + "name": "facade", + "version": "1.0.0", + "description": "The Facade microservice.", + "main": "index.ts", + "dependencies": { + "loopback-connector-swagger": "^3.2.0", + "loopback-datasource-juggler": "^3.4.1", + "@loopback/core": "^4.0.0-alpha.10", + "@loopback/repository": "^4.0.0-alpha.5" + }, + "devDependencies": { + "@types/node": "^7.0.12" + }, + "scripts": { + "start": "ts-node index.ts", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "loopback-next", + "example", + "facade", + "microservice" + ], + "author": "IBM", + "license": "MIT" +} diff --git a/packages/example-microservices/services/facade/repositories/account/index.ts b/packages/example-microservices/services/facade/repositories/account/index.ts new file mode 100644 index 000000000000..b1ae656f8ed6 --- /dev/null +++ b/packages/example-microservices/services/facade/repositories/account/index.ts @@ -0,0 +1,27 @@ +import { juggler, DataSourceConstructor } from '@loopback/repository'; + +// mixin of data source into service is not yet available, swagger.json needs to +// be loaded synchronously (ie. can't instantiate in the class constructor) + +const ds = new DataSourceConstructor('AccountService', { + connector: 'swagger', + spec: 'repositories/account/swagger.json' +}); + +export class AccountRepository { + model; + + constructor() { + this.model = ds.createModel('AccountService', {}); + } + + async find(accountNumber) { + const response = await this.model.findById({id: accountNumber}); + const accounts = response && response.obj || []; + return accounts.length ? accounts[0] : {}; + } + + async create(accountInstance): Promise { + return await this.model.create(accountInstance); + } +} diff --git a/packages/example-microservices/services/facade/repositories/account/swagger.json b/packages/example-microservices/services/facade/repositories/account/swagger.json new file mode 100644 index 000000000000..f8fe3682d88e --- /dev/null +++ b/packages/example-microservices/services/facade/repositories/account/swagger.json @@ -0,0 +1,69 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "account" + }, + "host": "localhost:3001", + "basePath": "/", + "paths": { + "/accounts": { + "get": { + "summary": "Find all instances of the model matched by filter from the data source.", + "operationId": "findById", + "parameters": [ + { + "name": "id", + "in": "query", + "description": "Model id", + "required": true, + "type": "string", + "format": "JSON" + }, + { + "name": "filter", + "in": "query", + "description": "Filter defining fields and include - must be a JSON-encoded string ({\"something\":\"value\"})", + "required": false, + "type": "string", + "format": "JSON" + } + ], + "responses": { + "200": { + "description": "Request was successful", + "schema": { + "type": "string" + } + } + }, + "deprecated": false + } + }, + "/accounts/create": { + "post": { + "summary": "Create an account instance.", + "operationId": "create", + "parameters": [ + { + "name": "accountInstance", + "in": "body", + "description": "The account instance.", + "required": true, + "type": "object", + "format": "JSON" + } + ], + "responses": { + "200": { + "description": "Request was successful", + "schema": { + "type": "string" + } + } + }, + "deprecated": false + } + } + } +} \ No newline at end of file diff --git a/packages/example-microservices/services/facade/repositories/customer/index.ts b/packages/example-microservices/services/facade/repositories/customer/index.ts new file mode 100644 index 000000000000..226b2bab3036 --- /dev/null +++ b/packages/example-microservices/services/facade/repositories/customer/index.ts @@ -0,0 +1,23 @@ +import { juggler, DataSourceConstructor } from '@loopback/repository'; + +// mixin of data source into service is not yet available, swagger.json needs to +// be loaded synchronously (ie. can't instantiate in the class constructor) + +var SwaggerClient = require('swagger-client'); +const ds = new DataSourceConstructor('CustomerService', { + connector: 'swagger', + spec: 'repositories/customer/swagger.json' +}); + +export class CustomerRepository { + model; + + constructor() { + this.model = ds.createModel('CustomerService', {}); + } + + async find(customerNumber) { + const response = await this.model.findById({ id: customerNumber }); + return response && response.obj || []; + } +} diff --git a/packages/example-microservices/services/facade/repositories/customer/swagger.json b/packages/example-microservices/services/facade/repositories/customer/swagger.json new file mode 100644 index 000000000000..0306176eb0c9 --- /dev/null +++ b/packages/example-microservices/services/facade/repositories/customer/swagger.json @@ -0,0 +1,44 @@ +{ + "swagger":"2.0", + "info":{ + "version":"1.0.0", + "title":"customer" + }, + "host": "localhost:3002", + "basePath":"/", + "paths":{ + "/customers":{ + "get":{ + "summary":"Find all instances of the model matched by filter from the data source.", + "operationId":"findById", + "parameters":[ + { + "name":"id", + "in":"query", + "description":"Model id", + "required":true, + "type":"string", + "format":"JSON" + }, + { + "name":"filter", + "in":"query", + "description":"Filter defining fields and include - must be a JSON-encoded string ({\"something\":\"value\"})", + "required":false, + "type":"string", + "format":"JSON" + } + ], + "responses":{ + "200":{ + "description":"Request was successful", + "schema":{ + "type":"string" + } + } + }, + "deprecated":false + } + } + } +} diff --git a/packages/example-microservices/services/facade/repositories/transaction/index.ts b/packages/example-microservices/services/facade/repositories/transaction/index.ts new file mode 100644 index 000000000000..83212536f72f --- /dev/null +++ b/packages/example-microservices/services/facade/repositories/transaction/index.ts @@ -0,0 +1,23 @@ +import { juggler, DataSourceConstructor } from '@loopback/repository'; + +// mixin of data source into service is not yet available, swagger.json needs to +// be loaded synchronously (ie. can't instantiate in the class constructor) + +var SwaggerClient = require('swagger-client'); +const ds = new DataSourceConstructor('TransactionService', { + connector: 'swagger', + spec: 'repositories/transaction/swagger.json' +}); + +export class TransactionRepository { + model; + + constructor() { + this.model = ds.createModel('TransactionService', {}); + } + + async find(accountNumber) { + const response = await this.model.findById({ id: accountNumber }); + return response && response.obj || []; + } +} diff --git a/packages/example-microservices/services/facade/repositories/transaction/swagger.json b/packages/example-microservices/services/facade/repositories/transaction/swagger.json new file mode 100644 index 000000000000..170cc482a2ad --- /dev/null +++ b/packages/example-microservices/services/facade/repositories/transaction/swagger.json @@ -0,0 +1,44 @@ +{ + "swagger":"2.0", + "info":{ + "version":"1.0.0", + "title":"transaction" + }, + "host": "localhost:3003", + "basePath":"/", + "paths":{ + "/transactions":{ + "get":{ + "summary":"Find all instances of the model matched by filter from the data source.", + "operationId":"findById", + "parameters":[ + { + "name":"id", + "in":"query", + "description":"Model id", + "required":true, + "type":"string", + "format":"JSON" + }, + { + "name":"filter", + "in":"query", + "description":"Filter defining fields and include - must be a JSON-encoded string ({\"something\":\"value\"})", + "required":false, + "type":"string", + "format":"JSON" + } + ], + "responses":{ + "200":{ + "description":"Request was successful", + "schema":{ + "type":"string" + } + } + }, + "deprecated":false + } + } + } +} diff --git a/packages/example-microservices/services/facade/tsconfig.json b/packages/example-microservices/services/facade/tsconfig.json new file mode 100644 index 000000000000..429a600c5bb4 --- /dev/null +++ b/packages/example-microservices/services/facade/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "*": [ + "node_modules/loopback-next/packages/*" + ] + }, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "lib": [ + "es6", + "dom" + ], + "module": "commonjs", + "moduleResolution": "node", + "target": "es6", + "typeRoots": [ + "node_modules/@types" + ], + "inlineSources": true + }, + "include": [ + "**/*" + ], + "exclude": [ + "**/*.d.ts" + ] +} \ No newline at end of file diff --git a/packages/example-microservices/services/todo-legacy/README.md b/packages/example-microservices/services/todo-legacy/README.md new file mode 100644 index 000000000000..8769ee14d6d9 --- /dev/null +++ b/packages/example-microservices/services/todo-legacy/README.md @@ -0,0 +1,175 @@ +# Todo Application with Legacy Juggler + +## Summary +A REST API for managing Todo entries. + +## Installation +This example requires Node.js 8.x or higher. +Install dependencies: +``` +npm i +``` + +After you've installed your dependencies, you'll need to configure the +`datasource.ts` file to point towards your target database. + +By default, this example is configured to use the loopback-connector-mysql +module, with credentials defined by its `setup.sh` file (which we use for +unit tests). + +Feel free to install a different connector, and change `datasource.ts` +accordingly! + +## Overview + +This sample application demonstrates a simple CRUD API for a Todo list. +This application does not currently have an explorer component. + +Perform a series of CRUD operations using GET, POST, PUT, PATCH and DELETE +on the `localhost:/todo` route! + +Currently, only the title filter is supported: +(ex. `GET localhost:3000/todo?title=foo`) + +In this example app, the TodoRepository is using the injected datasource +configuration (which uses the loopback@3.x format) in combination with the +DataSourceConstructor provided by `legacy-juggler-bridge.ts/js`. +Additionally, it injects a loopback@3.x model definition to construct the +Todo model class at runtime. + +## Use + +Run the app: +``` +npm start +``` + +By default, it will be on port 3000, but you can specify the PORT environment +variable to change this: +``` +# There, now it's different! +PORT=3001 npm start +``` + +Use `curl` or your favourite REST client to access the endpoints! + +### Create a Todo + +`POST /todo` + +Body: +```json +{ + "title": "Make GUI", + "body": "So that I don't have to keep using curl to make my todo entries..." +} +``` + +Returns: +```json +{ + "id": 1, + "title": "Make GUI", + "body": "So that I don't have to keep using curl to make my todo entries..." +} +``` + +### Get Todo by ID + +`GET /todo/1` + +Returns: +```json +{ + "id": 1, + "title": "Make GUI", + "body": "So that I don't have to keep using curl to make my todo entries..." +} +``` + +### Find Todo by Title + +`GET /todo?title=Make%20%GUI` + +Returns: + +```json +[ + { + "id": 1, + "title": "Make GUI", + "body": "So that I don't have to keep using curl to make my todo entries..." + }, + { + "id": 2, + "title": "Make GUI", + "body": "Wait, I think I already made this todo... :S" + }, +] +``` + +### Replace Todo +`PUT /todo/2` +Body: +```json +{ + "title": "Make GUI Shiny", + "body": "Yeah, definitely a shiny GUI!" +} +``` + +Returns: +```json + { + "id": 2, + "title": "Make Shiny GUI", + "body": "Yeah, definitely a shiny GUI!" + }, +``` + +### Update Todo + +`PATCH /todo/2` +Body: +```json +{ + "title": "Make simple GUI" +} +``` + +Returns: +``` + { + "id": 2, + "title": "Make simple GUI", + "body": "Wait, I think I already made this todo... :S" + }, +``` + +### Delete a Todo + +`DELETE /todo/{id}` + +Returns: +```json +{ + "count": 1 +} +``` + +### Delete Todo by Title +`DELETE /todo?title=Make%GUI` + +Returns: +```json +{ + "count": 3 +} +``` + +## Tests +Run tests with `npm test`! + +## What's Next? +Now that you've got a working example to play with, feel free to +begin hacking on it! diff --git a/packages/example-microservices/services/todo-legacy/application.ts b/packages/example-microservices/services/todo-legacy/application.ts new file mode 100644 index 000000000000..23ee289059c8 --- /dev/null +++ b/packages/example-microservices/services/todo-legacy/application.ts @@ -0,0 +1,42 @@ +import { Application } from '@loopback/core'; +import { TodoController } from './controllers/todo-controller'; +import { + juggler, + DataSourceConstructor, + DefaultCrudRepository +} from '@loopback/repository'; +import { datasources } from './datasources'; + +export class TodoApplication extends Application { + private _startTime: Date; + + constructor() { + super(); + const app = this; + let ds = datasources['ds']; + // Controller bindings + app.controller(TodoController); + + let datasource = new DataSourceConstructor('ds', ds); + app.bind('datasources.ds').to(datasource); + + // Server protocol bindings + app.bind('servers.http.enabled').to(true); + app.bind('servers.https.enabled').to(true); + } + + async start() : Promise { + this._startTime = new Date(); + return super.start(); + } + + async info() { + const port: Number = await this.get('http.port'); + + return JSON.stringify({ + appName: "todo-legecy", + uptime: Date.now() - this._startTime.getTime(), + url: 'http://127.0.0.1:' + port, + }, null, 2); + } +} diff --git a/packages/example-microservices/services/todo-legacy/controllers/todo-controller.api.ts b/packages/example-microservices/services/todo-legacy/controllers/todo-controller.api.ts new file mode 100644 index 000000000000..a7c271ce7c48 --- /dev/null +++ b/packages/example-microservices/services/todo-legacy/controllers/todo-controller.api.ts @@ -0,0 +1,184 @@ +export const def = { + basePath: '/', + definitions: { + todo: { + id: { + type: 'number', + description: 'The ID number of the Todo entry.' + }, + title: { + type: 'string', + description: 'The title of the Todo entry.' + }, + body: { + type: 'string', + description: 'The body of the Todo entry.' + } + } + }, + paths: { + '/todo': { + get: { + 'x-operation-name': 'get', + parameters: [ + { + name: 'title', + in: 'query', + description: 'The title of the todo entry.', + required: false, + type: 'string', + format: 'JSON' + }, + { + name: 'filter', + in: 'query', + description: 'The title of the todo entry.', + required: false, + type: 'object', + format: 'JSON' + } + ], + responses: { + 200: { + schema: { + $ref: '#/definitions/todo' + } + } + } + }, + post: { + 'x-operation-name': 'create', + parameters: [ + { + name: 'data', + in: 'body', + description: 'The Todo model instance.', + required: true, + format: 'JSON' + } + ], + responses: { + 201: { + schema: { + $ref: '#/definitions/todo' + } + } + } + }, + delete: { + 'x-operation-name': 'delete', + parameters: [ + { + name: 'title', + in: 'query', + description: + 'The criteria used to narrow down the number of customers returned.', + required: false, + type: 'string', + format: 'JSON' + } + ], + responses: { + 204: { + description: 'The resource has been deleted.' + } + } + } + }, + '/todo/{id}': { + get: { + 'x-operation-name': 'getById', + parameters: [ + { + name: 'id', + in: 'path', + description: + 'The criteria used to narrow down the number of customers returned.', + required: false, + type: 'string', + format: 'JSON' + } + ], + responses: { + 200: { + schema: { + $ref: '#/definitions/todo' + } + } + } + }, + put: { + 'x-operation-name': 'replace', + parameters: [ + { + name: 'id', + in: 'path', + description: 'The todo ID.', + required: true, + type: 'string', + format: 'JSON' + }, + { + name: 'data', + in: 'body', + description: 'The Todo model instance.', + required: true, + format: 'JSON' + } + ], + responses: { + 201: { + schema: { + $ref: '#/definitions/todo' + } + } + } + }, + patch: { + 'x-operation-name': 'update', + parameters: [ + { + name: 'id', + in: 'path', + description: 'The todo ID.', + required: true, + type: 'string', + format: 'JSON' + }, + { + name: 'data', + in: 'body', + description: 'The Todo model instance.', + required: true, + format: 'JSON' + } + ], + responses: { + 200: { + schema: { + $ref: '#/definitions/todo' + } + } + } + }, + delete: { + 'x-operation-name': 'deleteById', + parameters: [ + { + name: 'id', + in: 'path', + description: 'The todo ID.', + required: false, + type: 'string', + format: 'JSON' + } + ], + responses: { + 204: { + description: 'The resource has been deleted.' + } + } + } + } + } +}; diff --git a/packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts b/packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts new file mode 100644 index 000000000000..6a4c5c6c72cd --- /dev/null +++ b/packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts @@ -0,0 +1,77 @@ +import { api, Application, inject } from '@loopback/core'; +import { def } from './todo-controller.api'; +import { Todo } from '../models/todo'; +import * as util from 'util'; +import { EntityCrudRepository, repository } from '@loopback/repository'; + +@api(def) +export class TodoController { + @repository(Todo, 'ds') + repository: EntityCrudRepository; + + constructor() {} + + async get(title?: string): Promise { + let filter = title ? { where: { title: title } } : {}; + return await this.repository.find(filter); + } + + async getById(id: number): Promise { + return await this.repository.find({ where: { id: id } }); + } + + async create(body: Object) { + return await this.repository.create(new Todo(body)); + } + + async update(id: number, body: Object): Promise { + let success: boolean; + if (id) { + let todo = new Todo(body); + todo.id = id; + success = await this.repository.updateById(id, todo); + // FIXME(kev): Unhandled error is thrown if you attempt to return + // the boolean value that the repository.update method returns. + return Promise.resolve({ count: success ? 1 : 0 }); + } else if (body) { + let result = await this.repository.updateAll(new Todo(body)); + return Promise.resolve({ count: result }); + } else { + return Promise.reject( + new Error('Cowardly refusing to update all todos!') + ); + } + } + + async replace(id: number, body: Todo): Promise { + let success = await this.repository.replaceById(id, new Todo(body)); + // FIXME(kev): Unhandled error is thrown if you attempt to return + // the boolean value that the repository.replaceById method returns. + return Promise.resolve({ count: success ? 1 : 0 }); + } + + async delete(title: string): Promise { + if (!title) { + return Promise.reject(new Error('You must provide a filter query!')); + } + let filter = { where: { title: title } }; + let result = await this.repository.deleteAll(filter); + return Promise.resolve({ count: result }); + } + + async deleteById(id: number): Promise { + let success = await this.repository.deleteById(id); + // FIXME(kev): Unhandled error is thrown if you attempt to return + // the boolean value that the repository.replaceById method returns. + return Promise.resolve({ count: success ? 1 : 0 }); + } +} +/** + * Helper class to define the return type of operations that return + * affected item counts. + * + * @class AffectedItems + */ +class AffectedItems { + constructor(public count: number) {} +} diff --git a/packages/example-microservices/services/todo-legacy/datasources.ts b/packages/example-microservices/services/todo-legacy/datasources.ts new file mode 100644 index 000000000000..fe0d4058f887 --- /dev/null +++ b/packages/example-microservices/services/todo-legacy/datasources.ts @@ -0,0 +1,57 @@ +export const datasources: DataSourceConfig = { + ds: { + name: 'ds', + connector: 'mysql', + host: 'localhost', + port: 3306, + database: 'testdb', + password: 'pass', + user: 'root', + } + }; + +export interface DataSourceConfig { + [datasource: string]: DataSourceDefinition +} + +/** + * The parameters required to define a MySQL datasource. + * + * @export + * @interface DataSourceDefinition + */ +export interface DataSourceDefinition { + /** + * The name of the connection (for programmatic reference). + * + * @type {string} + * @memberof DataSourceDefinition + */ + name: string; + /** + * The identifying name of the legacy connector module used to interact with + * the datasource. + * (ex. "mysql", "mongodb", "db2", etc...) + * + * @type {string} + * @memberof DataSourceDefinition + */ + connector: string; + /** + * + * The accessible machine name, domain address or IP address of the + * datasource. + * @type {string} + * @memberof DataSourceDefinition + */ + host: string; + /** + * The port number on which the datasource is listening for connections. + * + * @type {number} + * @memberof DataSourceDefinition + */ + port: number; + // Allow arbitrary extension of object. + [extras: string]: any; +} diff --git a/packages/example-microservices/services/todo-legacy/index.ts b/packages/example-microservices/services/todo-legacy/index.ts new file mode 100644 index 000000000000..494f799384e7 --- /dev/null +++ b/packages/example-microservices/services/todo-legacy/index.ts @@ -0,0 +1,14 @@ +import { Application } from '@loopback/core'; +import { TodoApplication } from './application'; + + +async function main(): Promise { + const app = new TodoApplication(); + await app.start(); + console.log('Application Info:', await app.info()); +} + +main().catch(err => { + console.log('Cannot start the app.', err); + process.exit(1); +}); diff --git a/packages/example-microservices/services/todo-legacy/models/todo.ts b/packages/example-microservices/services/todo-legacy/models/todo.ts new file mode 100644 index 000000000000..fcb1c40dacb6 --- /dev/null +++ b/packages/example-microservices/services/todo-legacy/models/todo.ts @@ -0,0 +1,34 @@ +import { Entity, model, property } from '@loopback/repository'; + +@model() +export class Todo extends Entity { + @property({ + type: "number", + id: true, + description: "The ID number of the Todo entry.", + }) + id: number; + + @property({ + type: "string", + description: "The title of the todo.", + }) + title: string; + + @property({ + type: "string", + description: "The main body of the todo.", + }) + body: string; + + constructor(body?: Object) { + super(); + if (body) { + Object.assign(this, body); + } + } + + getId() { + return this.id; + } +} diff --git a/packages/example-microservices/services/todo-legacy/package.json b/packages/example-microservices/services/todo-legacy/package.json new file mode 100644 index 000000000000..698a47c51c30 --- /dev/null +++ b/packages/example-microservices/services/todo-legacy/package.json @@ -0,0 +1,29 @@ +{ + "name": "todo", + "version": "1.0.0", + "description": "Example Todo list application", + "main": "index", + "scripts": { + "start": "ts-node index.ts", + "test": "mocha --compilers ts:ts-node/register,tsx:ts-node/register test/**/*.test.ts" + }, + "keywords": [], + "author": "", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "devDependencies": { + "@loopback/testlab": "^4.0.0-alpha.6", + "@types/mocha": "^2.2.43", + "mocha": "^3.5.3" + }, + "dependencies": { + "@loopback/core": "^4.0.0-alpha.13", + "@loopback/repository": "^4.0.0-alpha.5", + "@types/lodash": "^4.14.67", + "loopback-connector-mysql": "^4.2.0", + "ts-node": "^3.0.4", + "typescript": "^2.4.1" + } +} diff --git a/packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts b/packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts new file mode 100644 index 000000000000..1acc2bd8a2ae --- /dev/null +++ b/packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts @@ -0,0 +1,144 @@ +import 'mocha'; +import * as _ from 'lodash'; +import { expect, sinon } from '@loopback/testlab'; +import { + DefaultCrudRepository, + DataSourceConstructor, + ModelBaseConstructor, +} from '@loopback/repository'; +import { TodoController } from '../../controllers/todo-controller'; +import { Todo } from '../../models/todo'; + +import * as util from 'util'; + +describe('TodoController', () => { + let controller = new TodoController(); + // NOTE: Creating the datasource and model definition with + // the real functions, and then stubbing them is easier than + // building the stubs and fakes by hand! + let datasource = new DataSourceConstructor({ + name: 'ds', + connector: 'memory' + }); + let repository = new DefaultCrudRepository( + Todo, + datasource + ); + controller.repository = repository; + + describe('getTodo', () => { + let sandbox = sinon.sandbox.create(); + beforeEach(() => { + sandbox.restore(); + }); + it('returns all todos when called without ID', async () => { + let stub = sandbox.stub(repository, 'find'); + let result = await controller.get(); + expect.ok(stub.called, 'find was called'); + expect.deepEqual(stub.getCall(0).args, [{}], 'args were correct'); + }); + it('returns the correct todo by ID', async () => { + let stub = sandbox.stub(repository, 'find'); + let result = await controller.getById(1); + expect.ok(stub.called, 'find was called'); + }); + it('can filter by title', async () => { + let stub = sandbox.stub(repository, 'find'); + let result = await controller.get('test2'); + expect.ok(stub.called, 'find was called'); + expect.deepEqual( + stub.getCall(0).args, + [ + { + where: { title: 'test2' } + } + ], + 'controller created correct filter object' + ); + }); + }); + + describe('createTodo', () => { + let sandbox = sinon.sandbox.create(); + beforeEach(() => { + sandbox.restore(); + }); + it('calls create on the repository', async () => { + let stub = sandbox.stub(repository, 'create'); + let result = await controller.create({ + title: 'foo', + body: 'bar' + }); + expect.ok(stub.called, 'create was called'); + }); + }); + + describe('replaceTodo', () => { + let sandbox = sinon.sandbox.create(); + beforeEach(() => { + sandbox.restore(); + }); + it('returns an affected item count of 1 on success', async () => { + let stub = sandbox.stub(controller.repository, 'replaceById'); + let replacement = new Todo(); + Object.assign(replacement, { + id: 1, + title: 'foo', + body: 'bar' + }); + let result = await controller.replace(1, replacement); + expect.ok(stub.called, 'replace was called'); + }); + }); + + describe('updateTodo', () => { + let sandbox = sinon.sandbox.create(); + beforeEach(() => { + sandbox.restore(); + }); + it('returns the updated version of the object', async () => { + let stub = sandbox.stub(controller.repository, 'updateById'); + let replacement = { + id: 1, + title: 'foo' + }; + let expected = _.merge({ id: 1 }, replacement); + let result = await controller.update(1, replacement); + expect.ok(stub.called, 'update was called'); + }); + // There's no unhappy path tests here for missing ID, because + // it's handled at the repository layer, which we are stubbing! + }); + + describe('deleteTodo', () => { + let sandbox = sinon.sandbox.create(); + beforeEach(() => { + sandbox.restore(); + }); + it('works on one item', async () => { + let stub = sandbox.stub(controller.repository, 'deleteById'); + let result = await controller.deleteById(1); + expect.ok(stub.called, 'delete was called'); + // The null filter is automatically replaced with an empty object in + // controller layer! + expect.deepEqual(stub.getCall(0).args, [1], 'args were correct'); + }); + + it('can filter by title', async () => { + let stub = sandbox.stub(controller.repository, 'deleteAll'); + let result = await controller.delete('test2'); + expect.ok(stub.called, 'result exists'); + expect.deepEqual( + stub.getCall(0).args, + [ + { + where: { + title: 'test2' + } + } + ], + 'controller created correct filter object' + ); + }); + }); +}); diff --git a/packages/example-microservices/services/todo-legacy/tsconfig.json b/packages/example-microservices/services/todo-legacy/tsconfig.json new file mode 100644 index 000000000000..6d1b5e1a9e1d --- /dev/null +++ b/packages/example-microservices/services/todo-legacy/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2017", + "sourceMap": true, + "outDir": "lib", + "experimentalDecorators": true, + "strict": true + }, + "compileOnSave": true +} diff --git a/packages/example-microservices/services/transaction/bin/get-transactions b/packages/example-microservices/services/transaction/bin/get-transactions new file mode 100755 index 000000000000..e2f1e666493c --- /dev/null +++ b/packages/example-microservices/services/transaction/bin/get-transactions @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +curl -s localhost:3003/transactions | jq \ No newline at end of file diff --git a/packages/example-microservices/services/transaction/controllers/TransactionController.api.ts b/packages/example-microservices/services/transaction/controllers/TransactionController.api.ts new file mode 100644 index 000000000000..0edfdd664025 --- /dev/null +++ b/packages/example-microservices/services/transaction/controllers/TransactionController.api.ts @@ -0,0 +1,54 @@ +export const def = { + basePath: '/', + paths: { + '/transactions': { + get: { + 'x-operation-name': 'getTransactions', + parameters: [ + { + name: 'id', + in: 'query', + description: 'The transaction id', + required: true, + type: 'string', + format: 'JSON' + }, + { + name: 'filter', + in: 'query', + description: 'The criteria used to narrow down the number of transactions returned.', + required: false, + type: 'string', + format: 'JSON' + } + ], + responses: { + 200: { + schema: { + TransactionId: { + type: 'string', + description: 'The unique identifier for the transaction.', + }, + dateTime: { + type: 'date', + description: 'The date and time of the transaction.' + }, + accountNo: { + type: 'string', + description: 'The associated account number.' + }, + amount: { + type: 'number', + description: 'The amount being consider in the transaction.' + }, + transactionType: { + type: 'string', + description: 'The type of transaction. Can be "credit" or "debit".' + } + }, + }, + }, + }, + }, + }, +}; diff --git a/packages/example-microservices/services/transaction/controllers/TransactionController.ts b/packages/example-microservices/services/transaction/controllers/TransactionController.ts new file mode 100644 index 000000000000..e912ce6e6a77 --- /dev/null +++ b/packages/example-microservices/services/transaction/controllers/TransactionController.ts @@ -0,0 +1,21 @@ +import { api } from '@loopback/core'; +import { def } from './TransactionController.api'; +import { TransactionRepository } from '../repositories/transaction'; + +@api(def) +export class TransactionController { + repository: TransactionRepository; + + constructor() { + this.repository = new TransactionRepository(); + } + + async getTransactions(filter): Promise { + const transactions = await this.repository.find(filter); + const response = []; + transactions.forEach(transaction => { + response.push(transaction.toJSON()); + }); + return response; + } +} diff --git a/packages/example-microservices/services/transaction/index.ts b/packages/example-microservices/services/transaction/index.ts new file mode 100644 index 000000000000..f36e5155dd76 --- /dev/null +++ b/packages/example-microservices/services/transaction/index.ts @@ -0,0 +1,39 @@ +import { Application } from '@loopback/core'; +import { TransactionController } from './controllers/TransactionController'; + +class TransactionApplication extends Application { + private _startTime: Date; + + constructor() { + super(); + const app = this; + app.controller(TransactionController); + app.bind('http.port').to(3003); + } + + async start() { + this._startTime = new Date(); + return super.start(); + } + + async info() { + const port: Number = await this.get('http.port'); + + return { + appName: "transaction", + uptime: Date.now() - this._startTime.getTime(), + url: 'http://127.0.0.1:' + port, + }; + } +} + +async function main(): Promise { + const app = new TransactionApplication(); + await app.start(); + console.log('Application Info:', await app.info()); +} + +main().catch(err => { + console.log('Cannot start the app.', err); + process.exit(1); +}); \ No newline at end of file diff --git a/packages/example-microservices/services/transaction/package.json b/packages/example-microservices/services/transaction/package.json new file mode 100644 index 000000000000..4369fc7ef8c1 --- /dev/null +++ b/packages/example-microservices/services/transaction/package.json @@ -0,0 +1,26 @@ +{ + "name": "transaction", + "version": "1.0.0", + "description": "The Transaction microservice.", + "main": "index.ts", + "dependencies": { + "loopback-datasource-juggler": "^3.4.1", + "@loopback/repository": "^4.0.0-alpha.5", + "@loopback/core": "^4.0.0-alpha.10" + }, + "devDependencies": { + "@types/node": "^7.0.12" + }, + "scripts": { + "start": "ts-node index.ts", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "loopback-next", + "example", + "transaction", + "microservice" + ], + "author": "IBM", + "license": "MIT" +} diff --git a/packages/example-microservices/services/transaction/repositories/transaction/datasources/local-fs/data.json b/packages/example-microservices/services/transaction/repositories/transaction/datasources/local-fs/data.json new file mode 100644 index 000000000000..de9bffb20f53 --- /dev/null +++ b/packages/example-microservices/services/transaction/repositories/transaction/datasources/local-fs/data.json @@ -0,0 +1,20 @@ +{ + "ids": { + "Transaction": 13 + }, + "models": { + "Transaction": { + "DEBIT0001": "{\"TransactionId\":\"DEBIT0001\",\"dateTime\":\"2017-03-11T00:27:52.422Z\",\"accountNo\":\"CHK52321122\",\"amount\":20,\"transactionType\":\"debit\"}", + "DEBIT0002": "{\"TransactionId\":\"DEBIT0002\",\"dateTime\":\"2017-03-11T00:27:52.422Z\",\"accountNo\":\"CHK52321122\",\"amount\":20,\"transactionType\":\"debit\"}", + "DEBIT0003": "{\"TransactionId\":\"DEBIT0003\",\"dateTime\":\"2017-03-11T00:27:52.422Z\",\"accountNo\":\"CHK52321122\",\"amount\":40,\"transactionType\":\"credit\"}", + "DEBIT0004": "{\"TransactionId\":\"DEBIT0004\",\"dateTime\":\"2017-03-11T00:27:52.422Z\",\"accountNo\":\"CHK52321122\",\"amount\":50,\"transactionType\":\"credit\"}", + "DEBIT0005": "{\"TransactionId\":\"DEBIT0005\",\"dateTime\":\"2017-03-11T00:27:52.422Z\",\"accountNo\":\"CHK52321122\",\"amount\":50,\"transactionType\":\"credit\"}", + "DEBIT0007": "{\"TransactionId\":\"DEBIT0007\",\"dateTime\":\"2017-03-11T00:27:52.422Z\",\"accountNo\":\"CHK54520000\",\"amount\":100,\"transactionType\":\"credit\"}", + "DEBIT0009": "{\"TransactionId\":\"DEBIT0009\",\"dateTime\":\"2017-03-11T00:27:52.422Z\",\"accountNo\":\"CHK52321199\",\"amount\":140,\"transactionType\":\"credit\"}", + "DEBIT0010": "{\"TransactionId\":\"DEBIT0010\",\"dateTime\":\"2017-03-11T00:27:52.422Z\",\"accountNo\":\"CHK52321199\",\"amount\":20,\"transactionType\":\"debit\"}", + "DEBIT0011": "{\"TransactionId\":\"DEBIT0011\",\"dateTime\":\"2017-03-11T00:27:52.422Z\",\"accountNo\":\"CHK99999999\",\"amount\":300,\"transactionType\":\"credit\"}", + "DEBIT0012": "{\"TransactionId\":\"DEBIT0012\",\"dateTime\":\"2017-03-11T00:27:52.422Z\",\"accountNo\":\"CHK99999999\",\"amount\":100,\"transactionType\":\"debit\"}", + "DEBIT0013": "{\"TransactionId\":\"DEBIT0013\",\"dateTime\":\"2017-03-11T00:27:52.422Z\",\"accountNo\":\"CHK99999999\",\"amount\":50,\"transactionType\":\"debit\"}" + } + } +} \ No newline at end of file diff --git a/packages/example-microservices/services/transaction/repositories/transaction/index.ts b/packages/example-microservices/services/transaction/repositories/transaction/index.ts new file mode 100644 index 000000000000..616996fdca14 --- /dev/null +++ b/packages/example-microservices/services/transaction/repositories/transaction/index.ts @@ -0,0 +1,18 @@ +import { juggler, DataSourceConstructor } from '@loopback/repository'; +const modelDefinition = require('./models/transaction/model-definition.json'); + +export class TransactionRepository { + model; + + constructor() { + const ds = new DataSourceConstructor('local-fs', { + connector: 'memory', + file: './repositories/transaction/datasources/local-fs/data.json' + }); + this.model = ds.createModel('Transaction', modelDefinition); + } + + async find(id): Promise { + return await this.model.find({ where: { accountNo: id } }); + } +} diff --git a/packages/example-microservices/services/transaction/repositories/transaction/models/transaction/model-definition.json b/packages/example-microservices/services/transaction/repositories/transaction/models/transaction/model-definition.json new file mode 100644 index 000000000000..0d1116d50a45 --- /dev/null +++ b/packages/example-microservices/services/transaction/repositories/transaction/models/transaction/model-definition.json @@ -0,0 +1,26 @@ +{ + "name": "Transaction", + "properties": { + "TransactionId": { + "type": "string", + "required": true, + "id": true + }, + "dateTime": { + "type": "date", + "required": true + }, + "accountNo": { + "type": "string", + "required": true + }, + "amount": { + "type": "number", + "required": true + }, + "transactionType": { + "type": "string", + "required": true + } + } +} \ No newline at end of file diff --git a/packages/example-microservices/services/transaction/tsconfig.json b/packages/example-microservices/services/transaction/tsconfig.json new file mode 100644 index 000000000000..741c22be99a3 --- /dev/null +++ b/packages/example-microservices/services/transaction/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "*": [ + "*" + ] + }, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "lib": [ + "es6", + "dom" + ], + "module": "commonjs", + "moduleResolution": "node", + "target": "es6", + "typeRoots": [ + "node_modules/@types" + ], + "inlineSources": true + }, + "include": [ + "**/*" + ], + "exclude": [ + "**/*.d.ts" + ] +} \ No newline at end of file diff --git a/packages/example-microservices/tsconfig.json b/packages/example-microservices/tsconfig.json new file mode 100644 index 000000000000..20496bb27106 --- /dev/null +++ b/packages/example-microservices/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "*": ["node_modules/loopback-next/packages/*"] + }, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "lib": ["es6", "dom"], + "module": "commonjs", + "moduleResolution": "node", + "target": "es6", + "typeRoots": ["node_modules/@types"], + "inlineSources": true + }, + "include": [ + "**/*" + ], + "exclude": [ + "**/*.d.ts" + ] +} From 7b13322f90ec26d6b655961ab7ef9ec2c4f3c084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 30 Jan 2018 13:39:42 +0100 Subject: [PATCH 2/8] fixup! Update LICENSE and copyright headers --- packages/example-microservices/LICENSE | 4 ++-- packages/example-microservices/bin/test.js | 5 +++++ packages/example-microservices/package.json | 4 ++-- .../controllers/AccountController.api.ts | 5 +++++ .../account-without-juggler/controllers/AccountController.ts | 5 +++++ .../services/account-without-juggler/index.ts | 5 +++++ .../repositories/account/datasources/mysqlconn.ts | 5 +++++ .../repositories/account/datasources/mysqlds.ts | 5 +++++ .../account-without-juggler/repositories/account/index.ts | 5 +++++ .../repositories/account/models/Account.ts | 5 +++++ .../repositories/account/models/account/model-definition.js | 5 +++++ .../account-without-juggler/test/unit/account-controller.ts | 5 +++++ .../services/account/controllers/AccountController.api.ts | 5 +++++ .../services/account/controllers/AccountController.ts | 5 +++++ packages/example-microservices/services/account/index.ts | 5 +++++ .../services/account/repositories/account/index.ts | 5 +++++ .../example-microservices/services/account/test/unit/test.ts | 5 +++++ .../services/customer/controllers/CustomerController.api.ts | 5 +++++ .../services/customer/controllers/CustomerController.ts | 5 +++++ packages/example-microservices/services/customer/index.ts | 5 +++++ .../services/customer/repositories/customer/index.ts | 5 +++++ .../facade/controllers/AccountManagementController.api.ts | 5 +++++ .../facade/controllers/AccountManagementController.ts | 5 +++++ packages/example-microservices/services/facade/index.ts | 5 +++++ .../services/facade/repositories/account/index.ts | 5 +++++ .../services/facade/repositories/customer/index.ts | 5 +++++ .../services/facade/repositories/transaction/index.ts | 5 +++++ .../services/todo-legacy/application.ts | 5 +++++ .../services/todo-legacy/controllers/todo-controller.api.ts | 5 +++++ .../services/todo-legacy/controllers/todo-controller.ts | 5 +++++ .../services/todo-legacy/datasources.ts | 5 +++++ packages/example-microservices/services/todo-legacy/index.ts | 5 +++++ .../services/todo-legacy/models/todo.ts | 5 +++++ .../todo-legacy/test/controller/todo-controller.test.ts | 5 +++++ .../transaction/controllers/TransactionController.api.ts | 5 +++++ .../transaction/controllers/TransactionController.ts | 5 +++++ packages/example-microservices/services/transaction/index.ts | 5 +++++ .../services/transaction/repositories/transaction/index.ts | 5 +++++ 38 files changed, 184 insertions(+), 4 deletions(-) diff --git a/packages/example-microservices/LICENSE b/packages/example-microservices/LICENSE index f0dbf91b8b05..363c0a82c3b0 100644 --- a/packages/example-microservices/LICENSE +++ b/packages/example-microservices/LICENSE @@ -1,5 +1,5 @@ -Copyright (c) IBM Corp. 2013,2016. All Rights Reserved. -Node module: loopback-next-example +Copyright (c) IBM Corp. 2018. All Rights Reserved. +Node module: @loopback/example-microservices This project is licensed under the MIT License, full text below. -------- diff --git a/packages/example-microservices/bin/test.js b/packages/example-microservices/bin/test.js index 5462e37dbad3..1aad2c421e9e 100644 --- a/packages/example-microservices/bin/test.js +++ b/packages/example-microservices/bin/test.js @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + 'use strict'; let Promise = require('bluebird'); diff --git a/packages/example-microservices/package.json b/packages/example-microservices/package.json index bdb47be68caf..e4f1b98d08f9 100644 --- a/packages/example-microservices/package.json +++ b/packages/example-microservices/package.json @@ -1,6 +1,6 @@ { - "name": "loopback-next-example", - "version": "1.0.0", + "name": "@loopback/example-microservices", + "version": "4.0.0-alpha.0", "description": "How to use LoopBack.next and some recommended best practices.", "main": "facade/index.js", "scripts": { diff --git a/packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts index 1cd67a2b5fdf..2bb11d8b9701 100644 --- a/packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts +++ b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + export const def = { basePath: '/', paths: { diff --git a/packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts index c21ea080c5f2..cabfce82159c 100644 --- a/packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts +++ b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { api } from '@loopback/core'; import { def } from './AccountController.api'; import { AccountRepository } from '../repositories/account'; diff --git a/packages/example-microservices/services/account-without-juggler/index.ts b/packages/example-microservices/services/account-without-juggler/index.ts index be5f65a07a18..96c027a26125 100644 --- a/packages/example-microservices/services/account-without-juggler/index.ts +++ b/packages/example-microservices/services/account-without-juggler/index.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { Application } from '@loopback/core'; import { AccountController } from './controllers/AccountController'; import { AccountRepository } from './repositories/account'; diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts index a46b93160f49..4a6be5e38fdf 100644 --- a/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + const debug = require('debug')('loopback:repositories:account:datasources:connections:mysql'); const mysql = require('mysql'); const db = require('mysql-promise')(); diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlds.ts b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlds.ts index 7754c9c655ed..3d27a2f73b22 100644 --- a/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlds.ts +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlds.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { MySqlConn } from './mysqlconn'; import { DataSource } from '@loopback/repository'; const mysqlCreds = require('./mysql.json'); diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/index.ts b/packages/example-microservices/services/account-without-juggler/repositories/account/index.ts index 202245b0c1c3..2f4384e0e319 100644 --- a/packages/example-microservices/services/account-without-juggler/repositories/account/index.ts +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/index.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { CrudRepositoryImpl } from '@loopback/repository'; import { MySqlDs } from './datasources/mysqlds'; import { Account } from './models/Account'; diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts b/packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts index 0f75d9fdbe87..3e72b9155385 100644 --- a/packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { Entity, model, diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/models/account/model-definition.js b/packages/example-microservices/services/account-without-juggler/repositories/account/models/account/model-definition.js index c0ee62a739f9..6b55b9c7e5bf 100644 --- a/packages/example-microservices/services/account-without-juggler/repositories/account/models/account/model-definition.js +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/models/account/model-definition.js @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + module.exports = { name: 'Account', properties: { diff --git a/packages/example-microservices/services/account-without-juggler/test/unit/account-controller.ts b/packages/example-microservices/services/account-without-juggler/test/unit/account-controller.ts index 8ad854929b3d..89f72327bf18 100644 --- a/packages/example-microservices/services/account-without-juggler/test/unit/account-controller.ts +++ b/packages/example-microservices/services/account-without-juggler/test/unit/account-controller.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import 'mocha'; import { AccountController } from '../../controllers/AccountController'; import { expect } from '@loopback/testlab'; diff --git a/packages/example-microservices/services/account/controllers/AccountController.api.ts b/packages/example-microservices/services/account/controllers/AccountController.api.ts index bb4dacfe281b..79f7b9495827 100644 --- a/packages/example-microservices/services/account/controllers/AccountController.api.ts +++ b/packages/example-microservices/services/account/controllers/AccountController.api.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + export const def = { basePath: '/', paths: { diff --git a/packages/example-microservices/services/account/controllers/AccountController.ts b/packages/example-microservices/services/account/controllers/AccountController.ts index 269a68d69f72..2f9edbf22fe0 100644 --- a/packages/example-microservices/services/account/controllers/AccountController.ts +++ b/packages/example-microservices/services/account/controllers/AccountController.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { Filter, Where } from '@loopback/repository'; import { api } from '@loopback/core'; import { def } from './AccountController.api'; diff --git a/packages/example-microservices/services/account/index.ts b/packages/example-microservices/services/account/index.ts index 34f870e9540c..75140d20ba32 100644 --- a/packages/example-microservices/services/account/index.ts +++ b/packages/example-microservices/services/account/index.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { Application } from '@loopback/core'; import { AccountController } from './controllers/AccountController'; diff --git a/packages/example-microservices/services/account/repositories/account/index.ts b/packages/example-microservices/services/account/repositories/account/index.ts index 34c64827dd2a..49689e62e3f2 100644 --- a/packages/example-microservices/services/account/repositories/account/index.ts +++ b/packages/example-microservices/services/account/repositories/account/index.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { juggler, DataSourceConstructor } from '@loopback/repository'; const modelDefinition = require('./models/account/model-definition.json'); diff --git a/packages/example-microservices/services/account/test/unit/test.ts b/packages/example-microservices/services/account/test/unit/test.ts index d505e706ad8c..e7089a21ee41 100644 --- a/packages/example-microservices/services/account/test/unit/test.ts +++ b/packages/example-microservices/services/account/test/unit/test.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + // test/unit/test.js import 'mocha'; import { AccountController } from "../../controllers/AccountController"; diff --git a/packages/example-microservices/services/customer/controllers/CustomerController.api.ts b/packages/example-microservices/services/customer/controllers/CustomerController.api.ts index 384f642d4ef7..9c561071a0b4 100644 --- a/packages/example-microservices/services/customer/controllers/CustomerController.api.ts +++ b/packages/example-microservices/services/customer/controllers/CustomerController.api.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + export const def = { basePath: '/', paths: { diff --git a/packages/example-microservices/services/customer/controllers/CustomerController.ts b/packages/example-microservices/services/customer/controllers/CustomerController.ts index e798ed1f1424..89772457115b 100644 --- a/packages/example-microservices/services/customer/controllers/CustomerController.ts +++ b/packages/example-microservices/services/customer/controllers/CustomerController.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { api } from '@loopback/core'; import { def } from './CustomerController.api'; import { CustomerRepository } from '../repositories/customer'; diff --git a/packages/example-microservices/services/customer/index.ts b/packages/example-microservices/services/customer/index.ts index 7a7bbd2e8d56..99ea0e404914 100644 --- a/packages/example-microservices/services/customer/index.ts +++ b/packages/example-microservices/services/customer/index.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { Application } from '@loopback/core'; import { CustomerController } from './controllers/CustomerController'; diff --git a/packages/example-microservices/services/customer/repositories/customer/index.ts b/packages/example-microservices/services/customer/repositories/customer/index.ts index 3de894daaeb4..63091ec18d56 100644 --- a/packages/example-microservices/services/customer/repositories/customer/index.ts +++ b/packages/example-microservices/services/customer/repositories/customer/index.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { juggler, DataSourceConstructor } from '@loopback/repository'; const modelDefinition = require('./models/customer/model-definition.json'); diff --git a/packages/example-microservices/services/facade/controllers/AccountManagementController.api.ts b/packages/example-microservices/services/facade/controllers/AccountManagementController.api.ts index b652d57706dd..6a7ffde92229 100644 --- a/packages/example-microservices/services/facade/controllers/AccountManagementController.api.ts +++ b/packages/example-microservices/services/facade/controllers/AccountManagementController.api.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + export const def = { basePath: '/', paths: { diff --git a/packages/example-microservices/services/facade/controllers/AccountManagementController.ts b/packages/example-microservices/services/facade/controllers/AccountManagementController.ts index 4f075dbd7b21..9234f448e4c0 100644 --- a/packages/example-microservices/services/facade/controllers/AccountManagementController.ts +++ b/packages/example-microservices/services/facade/controllers/AccountManagementController.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { api } from '@loopback/core'; import { def } from './AccountManagementController.api'; import { AccountRepository } from '../repositories/account'; diff --git a/packages/example-microservices/services/facade/index.ts b/packages/example-microservices/services/facade/index.ts index 65c1a999450a..6fea05c99414 100644 --- a/packages/example-microservices/services/facade/index.ts +++ b/packages/example-microservices/services/facade/index.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { Application } from '@loopback/core'; import { AccountController } from './controllers/AccountManagementController'; diff --git a/packages/example-microservices/services/facade/repositories/account/index.ts b/packages/example-microservices/services/facade/repositories/account/index.ts index b1ae656f8ed6..ae5e5c505fef 100644 --- a/packages/example-microservices/services/facade/repositories/account/index.ts +++ b/packages/example-microservices/services/facade/repositories/account/index.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { juggler, DataSourceConstructor } from '@loopback/repository'; // mixin of data source into service is not yet available, swagger.json needs to diff --git a/packages/example-microservices/services/facade/repositories/customer/index.ts b/packages/example-microservices/services/facade/repositories/customer/index.ts index 226b2bab3036..17d43069077c 100644 --- a/packages/example-microservices/services/facade/repositories/customer/index.ts +++ b/packages/example-microservices/services/facade/repositories/customer/index.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { juggler, DataSourceConstructor } from '@loopback/repository'; // mixin of data source into service is not yet available, swagger.json needs to diff --git a/packages/example-microservices/services/facade/repositories/transaction/index.ts b/packages/example-microservices/services/facade/repositories/transaction/index.ts index 83212536f72f..3ce75c9da8c1 100644 --- a/packages/example-microservices/services/facade/repositories/transaction/index.ts +++ b/packages/example-microservices/services/facade/repositories/transaction/index.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { juggler, DataSourceConstructor } from '@loopback/repository'; // mixin of data source into service is not yet available, swagger.json needs to diff --git a/packages/example-microservices/services/todo-legacy/application.ts b/packages/example-microservices/services/todo-legacy/application.ts index 23ee289059c8..289d7c7e4d2c 100644 --- a/packages/example-microservices/services/todo-legacy/application.ts +++ b/packages/example-microservices/services/todo-legacy/application.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { Application } from '@loopback/core'; import { TodoController } from './controllers/todo-controller'; import { diff --git a/packages/example-microservices/services/todo-legacy/controllers/todo-controller.api.ts b/packages/example-microservices/services/todo-legacy/controllers/todo-controller.api.ts index a7c271ce7c48..e48f0c005ce8 100644 --- a/packages/example-microservices/services/todo-legacy/controllers/todo-controller.api.ts +++ b/packages/example-microservices/services/todo-legacy/controllers/todo-controller.api.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + export const def = { basePath: '/', definitions: { diff --git a/packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts b/packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts index 6a4c5c6c72cd..edc35c8e2714 100644 --- a/packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts +++ b/packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { api, Application, inject } from '@loopback/core'; import { def } from './todo-controller.api'; import { Todo } from '../models/todo'; diff --git a/packages/example-microservices/services/todo-legacy/datasources.ts b/packages/example-microservices/services/todo-legacy/datasources.ts index fe0d4058f887..fb3b89b00dc9 100644 --- a/packages/example-microservices/services/todo-legacy/datasources.ts +++ b/packages/example-microservices/services/todo-legacy/datasources.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + export const datasources: DataSourceConfig = { ds: { name: 'ds', diff --git a/packages/example-microservices/services/todo-legacy/index.ts b/packages/example-microservices/services/todo-legacy/index.ts index 494f799384e7..999d5c206b0b 100644 --- a/packages/example-microservices/services/todo-legacy/index.ts +++ b/packages/example-microservices/services/todo-legacy/index.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { Application } from '@loopback/core'; import { TodoApplication } from './application'; diff --git a/packages/example-microservices/services/todo-legacy/models/todo.ts b/packages/example-microservices/services/todo-legacy/models/todo.ts index fcb1c40dacb6..23d563c97686 100644 --- a/packages/example-microservices/services/todo-legacy/models/todo.ts +++ b/packages/example-microservices/services/todo-legacy/models/todo.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { Entity, model, property } from '@loopback/repository'; @model() diff --git a/packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts b/packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts index 1acc2bd8a2ae..fc45c1bbd863 100644 --- a/packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts +++ b/packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import 'mocha'; import * as _ from 'lodash'; import { expect, sinon } from '@loopback/testlab'; diff --git a/packages/example-microservices/services/transaction/controllers/TransactionController.api.ts b/packages/example-microservices/services/transaction/controllers/TransactionController.api.ts index 0edfdd664025..d5cd885a3abf 100644 --- a/packages/example-microservices/services/transaction/controllers/TransactionController.api.ts +++ b/packages/example-microservices/services/transaction/controllers/TransactionController.api.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + export const def = { basePath: '/', paths: { diff --git a/packages/example-microservices/services/transaction/controllers/TransactionController.ts b/packages/example-microservices/services/transaction/controllers/TransactionController.ts index e912ce6e6a77..ec2ec78d2e26 100644 --- a/packages/example-microservices/services/transaction/controllers/TransactionController.ts +++ b/packages/example-microservices/services/transaction/controllers/TransactionController.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { api } from '@loopback/core'; import { def } from './TransactionController.api'; import { TransactionRepository } from '../repositories/transaction'; diff --git a/packages/example-microservices/services/transaction/index.ts b/packages/example-microservices/services/transaction/index.ts index f36e5155dd76..0b748f73b5f6 100644 --- a/packages/example-microservices/services/transaction/index.ts +++ b/packages/example-microservices/services/transaction/index.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { Application } from '@loopback/core'; import { TransactionController } from './controllers/TransactionController'; diff --git a/packages/example-microservices/services/transaction/repositories/transaction/index.ts b/packages/example-microservices/services/transaction/repositories/transaction/index.ts index 616996fdca14..85f351219452 100644 --- a/packages/example-microservices/services/transaction/repositories/transaction/index.ts +++ b/packages/example-microservices/services/transaction/repositories/transaction/index.ts @@ -1,3 +1,8 @@ +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + import { juggler, DataSourceConstructor } from '@loopback/repository'; const modelDefinition = require('./models/transaction/model-definition.json'); From 849a301890b43f7785836d6fb618530ac2af127f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 30 Jan 2018 14:43:02 +0100 Subject: [PATCH 3/8] fixup! integrate with monorepo --- lerna.json | 5 +- package.json | 2 +- packages/example-microservices/README.md | 30 ++--- packages/example-microservices/bin/build | 9 -- packages/example-microservices/bin/install.js | 43 +++++++ packages/example-microservices/bin/test.js | 23 +--- packages/example-microservices/package.json | 14 +- .../account-without-juggler/package.json | 7 +- .../account/controllers/AccountController.ts | 18 +-- .../services/account/package.json | 8 +- .../account/repositories/account/index.ts | 22 ++-- .../services/account/test/unit/test.data.json | 4 +- .../services/account/test/unit/test.ts | 66 +++++----- .../services/customer/package.json | 7 +- .../services/facade/package.json | 7 +- .../controllers/todo-controller.api.ts | 120 +++++++++--------- .../controllers/todo-controller.ts | 31 ++--- .../services/todo-legacy/package.json | 8 +- .../services/transaction/package.json | 7 +- 19 files changed, 250 insertions(+), 181 deletions(-) delete mode 100755 packages/example-microservices/bin/build create mode 100755 packages/example-microservices/bin/install.js diff --git a/lerna.json b/lerna.json index 05dedc9f7343..5a15c8b0e848 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,9 @@ { "lerna": "2.5.1", - "packages": ["packages/*"], + "packages": [ + "packages/*", + "packages/example-microservices/services/*" + ], "command": { "publish": { "conventionalCommits": true diff --git a/package.json b/package.json index ce2f2becba6e..c412ccfffbc8 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "build:full": "npm run clean:lerna && npm run bootstrap && npm run build && npm run mocha && npm run lint", "pretest": "npm run clean && npm run build:current", "test": "node packages/build/bin/run-nyc npm run mocha", - "mocha": "node packages/build/bin/run-mocha \"packages/*/DIST/test/**/*.js\" \"packages/cli/test/*.js\"", + "mocha": "node packages/build/bin/run-mocha \"packages/*/DIST/test/**/*.js\" \"packages/cli/test/*.js\" && node packages/example-microservices/bin/test", "posttest": "npm run lint" }, "config": { diff --git a/packages/example-microservices/README.md b/packages/example-microservices/README.md index 8692019bf907..39a5a06f1ecf 100644 --- a/packages/example-microservices/README.md +++ b/packages/example-microservices/README.md @@ -11,15 +11,21 @@ How to build scalable microservices using LoopBack.next. Make sure you have the following installed: -- [Node.js](https://nodejs.org/en/download/) >= 7.0.0 -- [TypeScript](https://www.typescriptlang.org/index.html#download-links) >= 2.0.0 `npm i -g typescript` -- [TypeScript Node](https://github.com/TypeStrong/ts-node#installation) >= 3.0.0 `npm i -g ts-node` +- [Node.js](https://nodejs.org/en/download/) at v6.x or greater -```shell -# install loopback-next-example -git clone https://github.com/strongloop/loopback-next-example -cd loopback-next-example -npm run build +1. Install the new loopback CLI toolkit. +``` +npm i -g @loopback/cli +``` + +2. Download the "microservices" example. +``` +lb4 example microservices +``` + +3. Switch to the directory and install dependencies. +``` +cd loopback-example-microservices && npm install ``` ## Basic use @@ -41,14 +47,6 @@ npm stop > Helper scripts for the above commands are in [`/bin`](https://github.com/strongloop/loopback-next-example/tree/master/bin) directory. -# Team - -Ritchie Martori|Simon Ho|Siddhi Pai|Mahesh Patsute|Deepak Rajamohan -:-:|:-:|:-:|:-:|:-: -[](http://github.com/ritch)|[](http://github.com/superkhau)|[](http://github.com/siddhipai)|[](http://github.com/mpatsute)|[](http://github.com/deepakrkris) - -[See all contributors](https://github.com/strongloop/loopback-next-example/graphs/contributors) - # Contributing - [Guidelines](https://github.com/strongloop/loopback-next/wiki/Contributing) diff --git a/packages/example-microservices/bin/build b/packages/example-microservices/bin/build deleted file mode 100755 index db11cf2512ef..000000000000 --- a/packages/example-microservices/bin/build +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -cd services/account -npm i -cd ../customer -npm i -cd ../transaction -npm i -cd ../facade -npm i diff --git a/packages/example-microservices/bin/install.js b/packages/example-microservices/bin/install.js new file mode 100755 index 000000000000..ac4054174f88 --- /dev/null +++ b/packages/example-microservices/bin/install.js @@ -0,0 +1,43 @@ +#!/usr/bin/env node +// Copyright IBM Corp. 2018. All Rights Reserved. +// Node module: @loopback/example-microservices +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const spawn = require('child_process').spawn; + +const servicesRoot = path.resolve(__dirname, '..', 'services'); +const services = fs.readdirSync(servicesRoot); + +let p = Promise.resolve(); +for (const s of services) { + p = p.then(() => { + return execNpmInstall(`services/${s}`); + }); +} + +p.catch(err => { + console.error(err); + process.exit(1); +}); + +function execNpmInstall(cwd) { + console.log(`\n=== Running "npm install" in ${cwd} ===\n`); + return new Promise((resolve, reject) => { + const child = spawn('npm', ['install'], { + cwd: cwd, + stdio: 'inherit', + }); + child.once('error', err => reject(err)); + child.once('exit', (code, signal) => { + if (code || signal) + reject(`npm install failed: exit code ${code} signal ${signal}`); + else + resolve(); + }); + }); +} diff --git a/packages/example-microservices/bin/test.js b/packages/example-microservices/bin/test.js index 1aad2c421e9e..af07c3ebbd8c 100644 --- a/packages/example-microservices/bin/test.js +++ b/packages/example-microservices/bin/test.js @@ -1,3 +1,4 @@ +#!/usr/bin/env node // Copyright IBM Corp. 2018. All Rights Reserved. // Node module: @loopback/example-microservices // This file is licensed under the MIT License. @@ -14,7 +15,7 @@ let path = require('path'); let cmd = path.resolve(__dirname, '..', 'node_modules', '.bin', '_mocha'); let args = ['--compilers', 'ts:ts-node/register,tsx:ts-node/register']; -let services = path.resolve('services'); +let services = path.resolve(__dirname, '..', 'services'); return fs.readdirAsync(services).then(folders => { return Promise.each(folders, f => { let dir = path.resolve(services, f); @@ -25,19 +26,7 @@ return fs.readdirAsync(services).then(folders => { return new Promise((resolve, reject) => { console.log('RUN TESTS - %s:', f); let testArgs = args.push(path.resolve(dir, 'test/**/*test.ts')); - // Install dependencies - exec('npm i', { - cwd: dir - }); - let test = spawn(cmd, args); - test.stdout.on('data', out => { - console.log(out.toString()); - }); - - test.stderr.on('data', out => { - console.error(out.toString()); - }); - + let test = spawn(cmd, args, {stdio: 'inherit'}); test.on('close', code => { if (code) { return reject(code); @@ -53,10 +42,12 @@ return fs.readdirAsync(services).then(folders => { } }) .catch(code => { - console.error('TESTS FAILED - %s, exit code %s', f, code); - return process.exit(code); + return Promise.reject(`TESTS FAILED - ${f}, exit code ${code}`); }); }).then(() => { console.log('TESTS COMPLETE'); }); +}).catch(err => { + console.log(err); + process.exit(1); }); diff --git a/packages/example-microservices/package.json b/packages/example-microservices/package.json index e4f1b98d08f9..1749fb948403 100644 --- a/packages/example-microservices/package.json +++ b/packages/example-microservices/package.json @@ -2,16 +2,18 @@ "name": "@loopback/example-microservices", "version": "4.0.0-alpha.0", "description": "How to use LoopBack.next and some recommended best practices.", + "private": true, "main": "facade/index.js", "scripts": { - "build": "bin/build", - "restart": "npm run stop && npm run build && npm run start", + "postinstall": "node bin/install.js", + "restart": "npm run stop && npm run start", "start": "bin/start", "stop": "bin/stop", - "test": "node bin/test.js" + "test": "npm run mocha", + "mocha": "node bin/test.js" }, "engines": { - "node": ">=8" + "node": ">=6" }, "keywords": [ "loopback-next", @@ -29,8 +31,10 @@ "homepage": "https://github.com/strongloop/loopback-next-example#readme", "dependencies": { "bluebird": "^3.5.0", - "mocha": "^4.0.0", "ts-node": "^3.1.0", "typescript": "^2.4.1" + }, + "devDependencies": { + "mocha": "^4.0.0" } } diff --git a/packages/example-microservices/services/account-without-juggler/package.json b/packages/example-microservices/services/account-without-juggler/package.json index 1801affe1214..dcf99a876dc1 100644 --- a/packages/example-microservices/services/account-without-juggler/package.json +++ b/packages/example-microservices/services/account-without-juggler/package.json @@ -1,8 +1,13 @@ { - "name": "account", + "name": "@loopback-example-microservices/account-without-juggler", "version": "1.0.0", "description": "The Account microservice.", + "private": true, "main": "index.ts", + "repository": { + "type": "git", + "url": "https://github.com/strongloop/loopback-next.git" + }, "dependencies": { "mysql": "^2.13.0", "mysql-promise": "^4.1.0", diff --git a/packages/example-microservices/services/account/controllers/AccountController.ts b/packages/example-microservices/services/account/controllers/AccountController.ts index 2f9edbf22fe0..4990f6f40d04 100644 --- a/packages/example-microservices/services/account/controllers/AccountController.ts +++ b/packages/example-microservices/services/account/controllers/AccountController.ts @@ -3,10 +3,12 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { Filter, Where } from '@loopback/repository'; -import { api } from '@loopback/core'; -import { def } from './AccountController.api'; -import { AccountRepository } from '../repositories/account'; +import {Filter, Where} from '@loopback/repository'; +import {api} from '@loopback/rest'; +import {def} from './AccountController.api'; +import {AccountRepository} from '../repositories/account'; + +// tslint:disable:no-any @api(def) export class AccountController { @@ -16,19 +18,19 @@ export class AccountController { this.repository = new AccountRepository(); } - async getAccount(filter) { + async getAccount(filter: string) { return await this.repository.find(JSON.parse(filter)); } - async createAccount(accountInstance) { + async createAccount(accountInstance: any) { return await this.repository.create(accountInstance); } - async updateAccount(where, data) { + async updateAccount(where: string, data: any) { return await this.repository.update(JSON.parse(where), data); } - async deleteAccount(where) { + async deleteAccount(where: string) { return await this.repository.deleteAccount(JSON.parse(where)); } } diff --git a/packages/example-microservices/services/account/package.json b/packages/example-microservices/services/account/package.json index 1baf06330012..662324444ede 100644 --- a/packages/example-microservices/services/account/package.json +++ b/packages/example-microservices/services/account/package.json @@ -1,11 +1,17 @@ { - "name": "account", + "name": "@loopback-example-microservices/account", "version": "1.0.0", "description": "The Account microservice.", + "private": true, "main": "index.ts", + "repository": { + "type": "git", + "url": "https://github.com/strongloop/loopback-next.git" + }, "dependencies": { "@loopback/core": "^4.0.0-alpha.10", "@loopback/repository": "^4.0.0-alpha.5", + "@loopback/rest": "^4.0.0-alpha.22", "loopback-connector-mysql": "^4.2.0", "loopback-datasource-juggler": "^3.4.1", "ts-node": "^3.1.0", diff --git a/packages/example-microservices/services/account/repositories/account/index.ts b/packages/example-microservices/services/account/repositories/account/index.ts index 49689e62e3f2..1d80fffe60b6 100644 --- a/packages/example-microservices/services/account/repositories/account/index.ts +++ b/packages/example-microservices/services/account/repositories/account/index.ts @@ -3,34 +3,36 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { juggler, DataSourceConstructor } from '@loopback/repository'; +import {juggler, DataSourceConstructor} from '@loopback/repository'; const modelDefinition = require('./models/account/model-definition.json'); +// tslint:disable:no-any + export class AccountRepository { - model; + model: any; - constructor(file?:string) { + constructor(file?: string) { const ds: juggler.DataSource = new DataSourceConstructor('local-fs', { connector: 'memory', - file: file || './repositories/account/datasources/local-fs/data.json' + file: file || './repositories/account/datasources/local-fs/data.json', }); this.model = ds.createModel('Account', modelDefinition.properties, {}); } - async find(filter): Promise { + async find(filter: any): Promise { return await this.model.find(filter); } - async create(accountInstance): Promise { + async create(accountInstance: any): Promise { return await this.model.create(accountInstance); } - - async update(where, data): Promise { + + async update(where: any, data: any): Promise { return await this.model.updateAll(where, data, {}); } - - async deleteAccount(where): Promise { + + async deleteAccount(where: any): Promise { return await this.model.destroyAll(where); } } diff --git a/packages/example-microservices/services/account/test/unit/test.data.json b/packages/example-microservices/services/account/test/unit/test.data.json index 42e6f55d2753..983883439ebd 100644 --- a/packages/example-microservices/services/account/test/unit/test.data.json +++ b/packages/example-microservices/services/account/test/unit/test.data.json @@ -1,6 +1,6 @@ { "ids": { - "Account": 17 + "Account": 27 }, "models": { "Account": { @@ -10,4 +10,4 @@ "CHK99999999": "{\"id\":\"CHK99999999\",\"customerNumber\":\"0002444422\",\"balance\":100.89,\"branch\":\"Foster City\",\"type\":\"Checking\",\"avgBalance\":100.93,\"minimumBalance\":10}" } } -} +} \ No newline at end of file diff --git a/packages/example-microservices/services/account/test/unit/test.ts b/packages/example-microservices/services/account/test/unit/test.ts index e7089a21ee41..bc2f3f9c495a 100644 --- a/packages/example-microservices/services/account/test/unit/test.ts +++ b/packages/example-microservices/services/account/test/unit/test.ts @@ -5,12 +5,12 @@ // test/unit/test.js import 'mocha'; -import { AccountController } from "../../controllers/AccountController"; -import { expect } from "@loopback/testlab"; -import { AccountRepository } from "../../repositories/account"; +import {AccountController} from '../../controllers/AccountController'; +import {expect} from '@loopback/testlab'; +import {AccountRepository} from '../../repositories/account'; import * as path from 'path'; -let accCtrl; +let accCtrl: AccountController; const testAcc = { id: 'test1', @@ -19,38 +19,38 @@ const testAcc = { branch: 'Toronto', type: 'Chequing', avgBalance: 500, - minimumBalance: 0 + minimumBalance: 0, }; const brokenAcc = { customerNumber: '123456', balance: 1000, branch: 'Broke City', - type: 'Chequing' + type: 'Chequing', }; -describe("AccountController Unit Test Suite", () => { +describe('AccountController Unit Test Suite', () => { before(createAccountController); describe('AccountController.getAccount("{}")', () => { - it("returns an array of all accounts", async () => { - const result = await accCtrl.getAccount("{}"); + it('returns an array of all accounts', async () => { + const result = await accCtrl.getAccount('{}'); expect(result).to.not.be.empty(); expect(result).have.lengthOf(4); expect(result[0].id).to.equalOneOf([ - "CHK52321122", - "CHK54520000", - "CHK52321199", - "CHK99999999" + 'CHK52321122', + 'CHK54520000', + 'CHK52321199', + 'CHK99999999', ]); }); }); describe('AccountController.getAccount("")', () => { - it("rejects promise for invalid args", async () => { + it('rejects promise for invalid args', async () => { let flag = true; try { - await accCtrl.getAccount(""); + await accCtrl.getAccount(''); } catch (err) { flag = false; } @@ -59,21 +59,21 @@ describe("AccountController Unit Test Suite", () => { }); describe('AccountController.getAccount("{"where":{"id":"test1"}}")', () => { - it("searches and returns an empty array", async () => { + it('searches and returns an empty array', async () => { const result = await accCtrl.getAccount('{"where":{"id":"test1"}}'); expect(result).to.be.empty(); }); }); - describe("AccountController.createAccount(testAcc)", () => { - it("should create an account", async () => { + describe('AccountController.createAccount(testAcc)', () => { + it('should create an account', async () => { const result = await accCtrl.createAccount(testAcc); expect(JSON.stringify(result)).to.equal(JSON.stringify(testAcc)); }); }); describe('AccountController.getAccount("{"where":{"id":"test1"}}")', () => { - it("searches and returns newly created account", async () => { + it('searches and returns newly created account', async () => { const result = await accCtrl.getAccount('{"where":{"id":"test1"}}'); expect(result).to.not.be.empty(); expect(result).have.lengthOf(1); @@ -81,8 +81,8 @@ describe("AccountController Unit Test Suite", () => { }); }); - describe("AccountController.createAccount(brokenAcc)", () => { - it("fails to create with an Invalid Account instance.", async () => { + describe('AccountController.createAccount(brokenAcc)', () => { + it('fails to create with an Invalid Account instance.', async () => { let works = true; try { await accCtrl.createAccount(brokenAcc); @@ -94,16 +94,16 @@ describe("AccountController Unit Test Suite", () => { }); describe('AccountController.updateAccount("{"id":"test1"}", {"balance":2000})', () => { - it("updates an Account instance", async () => { + it('updates an Account instance', async () => { const result = await accCtrl.updateAccount('{"id":"test1"}', { - balance: 2000 + balance: 2000, }); expect(result.count).to.be.equal(1); }); }); describe('AccountController.getAccount("{"where":{"id":"test1"}}")', () => { - it("returns account with updated balance", async () => { + it('returns account with updated balance', async () => { const result = await accCtrl.getAccount('{"where":{"id":"test1"}}'); expect(result).to.not.be.empty(); expect(result).have.lengthOf(1); @@ -113,29 +113,29 @@ describe("AccountController Unit Test Suite", () => { }); describe('AccountController.deleteAccount("{"id":"test1"}")', () => { - it("deletes the Account instance", async () => { + it('deletes the Account instance', async () => { const result = await accCtrl.deleteAccount('{"id":"test1"}'); expect(result.count).to.be.equal(1); }); }); describe('AccountController.getAccount("{"where":{"id":"test1"}}")', () => { - it("searches and returns an empty array", async () => { + it('searches and returns an empty array', async () => { const result = await accCtrl.getAccount('{"where":{"id":"test1"}}'); expect(result).to.be.empty(); }); }); describe('AccountController.getAccount("{}")', () => { - it("returns an array of all accounts", async () => { - const result = await accCtrl.getAccount("{}"); + it('returns an array of all accounts', async () => { + const result = await accCtrl.getAccount('{}'); expect(result).to.not.be.empty(); expect(result).have.lengthOf(4); expect(result[0].id).to.equalOneOf([ - "CHK52321122", - "CHK54520000", - "CHK52321199", - "CHK99999999" + 'CHK52321122', + 'CHK54520000', + 'CHK52321199', + 'CHK99999999', ]); }); }); @@ -145,6 +145,6 @@ function createAccountController() { accCtrl = new AccountController(); accCtrl.repository = new AccountRepository( - path.resolve(__dirname, "test.data.json") + path.resolve(__dirname, 'test.data.json'), ); } diff --git a/packages/example-microservices/services/customer/package.json b/packages/example-microservices/services/customer/package.json index 93ef3dddc302..d27f1a61bc72 100644 --- a/packages/example-microservices/services/customer/package.json +++ b/packages/example-microservices/services/customer/package.json @@ -1,7 +1,12 @@ { - "name": "customer", + "name": "@loopback-example-microservices/customer", "version": "1.0.0", "description": "The Customer microservice.", + "private": true, + "repository": { + "type": "git", + "url": "https://github.com/strongloop/loopback-next.git" + }, "main": "index.ts", "dependencies": { "@loopback/core": "^4.0.0-alpha.10", diff --git a/packages/example-microservices/services/facade/package.json b/packages/example-microservices/services/facade/package.json index b78b3a22c3c4..0b82b4518c05 100644 --- a/packages/example-microservices/services/facade/package.json +++ b/packages/example-microservices/services/facade/package.json @@ -1,8 +1,13 @@ { - "name": "facade", + "name": "@loopback-example-microservices/facade", "version": "1.0.0", "description": "The Facade microservice.", + "private": true, "main": "index.ts", + "repository": { + "type": "git", + "url": "https://github.com/strongloop/loopback-next.git" + }, "dependencies": { "loopback-connector-swagger": "^3.2.0", "loopback-datasource-juggler": "^3.4.1", diff --git a/packages/example-microservices/services/todo-legacy/controllers/todo-controller.api.ts b/packages/example-microservices/services/todo-legacy/controllers/todo-controller.api.ts index e48f0c005ce8..b90f71518cc0 100644 --- a/packages/example-microservices/services/todo-legacy/controllers/todo-controller.api.ts +++ b/packages/example-microservices/services/todo-legacy/controllers/todo-controller.api.ts @@ -7,19 +7,21 @@ export const def = { basePath: '/', definitions: { todo: { - id: { - type: 'number', - description: 'The ID number of the Todo entry.' + properties: { + id: { + type: 'number', + description: 'The ID number of the Todo entry.', + }, + title: { + type: 'string', + description: 'The title of the Todo entry.', + }, + body: { + type: 'string', + description: 'The body of the Todo entry.', + }, }, - title: { - type: 'string', - description: 'The title of the Todo entry.' - }, - body: { - type: 'string', - description: 'The body of the Todo entry.' - } - } + }, }, paths: { '/todo': { @@ -32,7 +34,7 @@ export const def = { description: 'The title of the todo entry.', required: false, type: 'string', - format: 'JSON' + format: 'JSON', }, { name: 'filter', @@ -40,16 +42,16 @@ export const def = { description: 'The title of the todo entry.', required: false, type: 'object', - format: 'JSON' - } + format: 'JSON', + }, ], responses: { 200: { schema: { - $ref: '#/definitions/todo' - } - } - } + $ref: '#/definitions/todo', + }, + }, + }, }, post: { 'x-operation-name': 'create', @@ -59,16 +61,16 @@ export const def = { in: 'body', description: 'The Todo model instance.', required: true, - format: 'JSON' - } + format: 'JSON', + }, ], responses: { 201: { schema: { - $ref: '#/definitions/todo' - } - } - } + $ref: '#/definitions/todo', + }, + }, + }, }, delete: { 'x-operation-name': 'delete', @@ -80,15 +82,15 @@ export const def = { 'The criteria used to narrow down the number of customers returned.', required: false, type: 'string', - format: 'JSON' - } + format: 'JSON', + }, ], responses: { 204: { - description: 'The resource has been deleted.' - } - } - } + description: 'The resource has been deleted.', + }, + }, + }, }, '/todo/{id}': { get: { @@ -101,16 +103,16 @@ export const def = { 'The criteria used to narrow down the number of customers returned.', required: false, type: 'string', - format: 'JSON' - } + format: 'JSON', + }, ], responses: { 200: { schema: { - $ref: '#/definitions/todo' - } - } - } + $ref: '#/definitions/todo', + }, + }, + }, }, put: { 'x-operation-name': 'replace', @@ -121,23 +123,23 @@ export const def = { description: 'The todo ID.', required: true, type: 'string', - format: 'JSON' + format: 'JSON', }, { name: 'data', in: 'body', description: 'The Todo model instance.', required: true, - format: 'JSON' - } + format: 'JSON', + }, ], responses: { 201: { schema: { - $ref: '#/definitions/todo' - } - } - } + $ref: '#/definitions/todo', + }, + }, + }, }, patch: { 'x-operation-name': 'update', @@ -148,23 +150,23 @@ export const def = { description: 'The todo ID.', required: true, type: 'string', - format: 'JSON' + format: 'JSON', }, { name: 'data', in: 'body', description: 'The Todo model instance.', required: true, - format: 'JSON' - } + format: 'JSON', + }, ], responses: { 200: { schema: { - $ref: '#/definitions/todo' - } - } - } + $ref: '#/definitions/todo', + }, + }, + }, }, delete: { 'x-operation-name': 'deleteById', @@ -175,15 +177,15 @@ export const def = { description: 'The todo ID.', required: false, type: 'string', - format: 'JSON' - } + format: 'JSON', + }, ], responses: { 204: { - description: 'The resource has been deleted.' - } - } - } - } - } + description: 'The resource has been deleted.', + }, + }, + }, + }, + }, }; diff --git a/packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts b/packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts index edc35c8e2714..0695bc5396c6 100644 --- a/packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts +++ b/packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts @@ -3,11 +3,12 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { api, Application, inject } from '@loopback/core'; -import { def } from './todo-controller.api'; -import { Todo } from '../models/todo'; +import {Application, inject} from '@loopback/core'; +import {api} from '@loopback/rest'; +import {def} from './todo-controller.api'; +import {Todo} from '../models/todo'; import * as util from 'util'; -import { EntityCrudRepository, repository } from '@loopback/repository'; +import {EntityCrudRepository, repository} from '@loopback/repository'; @api(def) export class TodoController { @@ -15,14 +16,14 @@ export class TodoController { repository: EntityCrudRepository; constructor() {} - + async get(title?: string): Promise { - let filter = title ? { where: { title: title } } : {}; + let filter = title ? {where: {title: title}} : {}; return await this.repository.find(filter); } async getById(id: number): Promise { - return await this.repository.find({ where: { id: id } }); + return await this.repository.find({where: {id: id}}); } async create(body: Object) { @@ -37,13 +38,13 @@ export class TodoController { success = await this.repository.updateById(id, todo); // FIXME(kev): Unhandled error is thrown if you attempt to return // the boolean value that the repository.update method returns. - return Promise.resolve({ count: success ? 1 : 0 }); + return Promise.resolve({count: success ? 1 : 0}); } else if (body) { let result = await this.repository.updateAll(new Todo(body)); - return Promise.resolve({ count: result }); + return Promise.resolve({count: result}); } else { return Promise.reject( - new Error('Cowardly refusing to update all todos!') + new Error('Cowardly refusing to update all todos!'), ); } } @@ -52,29 +53,29 @@ export class TodoController { let success = await this.repository.replaceById(id, new Todo(body)); // FIXME(kev): Unhandled error is thrown if you attempt to return // the boolean value that the repository.replaceById method returns. - return Promise.resolve({ count: success ? 1 : 0 }); + return Promise.resolve({count: success ? 1 : 0}); } async delete(title: string): Promise { if (!title) { return Promise.reject(new Error('You must provide a filter query!')); } - let filter = { where: { title: title } }; + let filter = {where: {title: title}}; let result = await this.repository.deleteAll(filter); - return Promise.resolve({ count: result }); + return Promise.resolve({count: result}); } async deleteById(id: number): Promise { let success = await this.repository.deleteById(id); // FIXME(kev): Unhandled error is thrown if you attempt to return // the boolean value that the repository.replaceById method returns. - return Promise.resolve({ count: success ? 1 : 0 }); + return Promise.resolve({count: success ? 1 : 0}); } } /** * Helper class to define the return type of operations that return * affected item counts. - * + * * @class AffectedItems */ class AffectedItems { diff --git a/packages/example-microservices/services/todo-legacy/package.json b/packages/example-microservices/services/todo-legacy/package.json index 698a47c51c30..d6f37d95bb1c 100644 --- a/packages/example-microservices/services/todo-legacy/package.json +++ b/packages/example-microservices/services/todo-legacy/package.json @@ -1,8 +1,13 @@ { - "name": "todo", + "name": "@loopback-example-microservices/todo", "version": "1.0.0", "description": "Example Todo list application", + "private": true, "main": "index", + "repository": { + "type": "git", + "url": "https://github.com/strongloop/loopback-next.git" + }, "scripts": { "start": "ts-node index.ts", "test": "mocha --compilers ts:ts-node/register,tsx:ts-node/register test/**/*.test.ts" @@ -21,6 +26,7 @@ "dependencies": { "@loopback/core": "^4.0.0-alpha.13", "@loopback/repository": "^4.0.0-alpha.5", + "@loopback/rest": "^4.0.0-alpha.22", "@types/lodash": "^4.14.67", "loopback-connector-mysql": "^4.2.0", "ts-node": "^3.0.4", diff --git a/packages/example-microservices/services/transaction/package.json b/packages/example-microservices/services/transaction/package.json index 4369fc7ef8c1..6e947ded5265 100644 --- a/packages/example-microservices/services/transaction/package.json +++ b/packages/example-microservices/services/transaction/package.json @@ -1,8 +1,13 @@ { - "name": "transaction", + "name": "@loopback-example-microservices/transaction", "version": "1.0.0", "description": "The Transaction microservice.", + "private": true, "main": "index.ts", + "repository": { + "type": "git", + "url": "https://github.com/strongloop/loopback-next.git" + }, "dependencies": { "loopback-datasource-juggler": "^3.4.1", "@loopback/repository": "^4.0.0-alpha.5", From b3193377ccdf80a57cdbc0db91e8ef01835e3b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 30 Jan 2018 14:45:28 +0100 Subject: [PATCH 4/8] fixup! apply prettier formatting --- .../controllers/AccountController.api.ts | 88 +++++++++---------- .../controllers/AccountController.ts | 12 +-- .../services/account-without-juggler/index.ts | 8 +- .../account/datasources/mysqlconn.ts | 40 +++++---- .../account/datasources/mysqlds.ts | 5 +- .../repositories/account/index.ts | 7 +- .../repositories/account/models/Account.ts | 7 +- .../test/unit/account-controller.ts | 24 +++-- .../controllers/AccountController.api.ts | 53 +++++------ .../services/account/index.ts | 6 +- .../services/account/test/unit/test.data.json | 2 +- .../controllers/CustomerController.api.ts | 28 +++--- .../controllers/CustomerController.ts | 6 +- .../services/customer/index.ts | 6 +- .../customer/repositories/customer/index.ts | 6 +- .../AccountManagementController.api.ts | 23 ++--- .../AccountManagementController.ts | 10 +-- .../services/facade/index.ts | 6 +- .../facade/repositories/account/index.ts | 6 +- .../facade/repositories/customer/index.ts | 8 +- .../facade/repositories/transaction/index.ts | 8 +- .../services/todo-legacy/application.ts | 24 ++--- .../services/todo-legacy/datasources.ts | 20 ++--- .../services/todo-legacy/index.ts | 5 +- .../services/todo-legacy/models/todo.ts | 14 +-- .../test/controller/todo-controller.test.ts | 35 ++++---- .../controllers/TransactionController.api.ts | 20 +++-- .../controllers/TransactionController.ts | 6 +- .../services/transaction/index.ts | 8 +- .../repositories/transaction/index.ts | 6 +- 30 files changed, 257 insertions(+), 240 deletions(-) diff --git a/packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts index 2bb11d8b9701..960281819097 100644 --- a/packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts +++ b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts @@ -16,18 +16,18 @@ export const def = { description: 'The criteria used to narrow down the number of accounts returned.', required: true, - type: 'object' - } + type: 'object', + }, ], responses: { 200: { schema: { type: 'array', - $ref: '#/definitions/Account' - } - } - } - } + $ref: '#/definitions/Account', + }, + }, + }, + }, }, '/accounts/create': { post: { @@ -38,17 +38,17 @@ export const def = { in: 'body', description: 'The account instance to create.', required: true, - type: 'object' - } + type: 'object', + }, ], responses: { 200: { schema: { - $ref: '#/definitions/Account' - } - } - } - } + $ref: '#/definitions/Account', + }, + }, + }, + }, }, '/accounts/update': { post: { @@ -59,15 +59,15 @@ export const def = { in: 'query', description: 'The id of the model instance to be updated.', required: true, - type: 'string' + type: 'string', }, { name: 'data', in: 'body', description: 'An object of model property name/value pairs.', required: true, - type: 'object' - } + type: 'object', + }, ], responses: { 200: { @@ -77,13 +77,13 @@ export const def = { properties: { count: { type: 'number', - description: 'The number of records updated.' - } - } - } - } - } - } + description: 'The number of records updated.', + }, + }, + }, + }, + }, + }, }, '/accounts/delete': { delete: { @@ -94,8 +94,8 @@ export const def = { in: 'query', description: 'The ID for the model instance to be deleted.', required: true, - type: 'object' - } + type: 'object', + }, ], responses: { 200: { @@ -105,45 +105,45 @@ export const def = { properties: { count: { type: 'number', - description: 'The number of records deleted.' - } - } - } - } - } - } - } + description: 'The number of records deleted.', + }, + }, + }, + }, + }, + }, + }, }, definitions: { Account: { id: { type: 'string', - description: 'The ID for the account instance.' + description: 'The ID for the account instance.', }, customerNumber: { type: 'string', - description: 'The customer ID for the account instance.' + description: 'The customer ID for the account instance.', }, balance: { type: 'number', - description: 'The current balance for the account instance.' + description: 'The current balance for the account instance.', }, branch: { type: 'string', - description: 'The branch location for the account instance.' + description: 'The branch location for the account instance.', }, type: { type: 'string', - description: 'The type of banking account.' + description: 'The type of banking account.', }, avgBalance: { type: 'number', - description: 'The average balance for the account instance.' + description: 'The average balance for the account instance.', }, minimumBalance: { type: 'number', - description: 'The minimum balance for the account instance.' - } - } - } + description: 'The minimum balance for the account instance.', + }, + }, + }, }; diff --git a/packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts index cabfce82159c..5aaf088d3e40 100644 --- a/packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts +++ b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts @@ -3,15 +3,15 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { api } from '@loopback/core'; -import { def } from './AccountController.api'; -import { AccountRepository } from '../repositories/account'; -import { inject } from '@loopback/context'; -import { Account } from '../repositories/account/models/Account'; +import {api} from '@loopback/core'; +import {def} from './AccountController.api'; +import {AccountRepository} from '../repositories/account'; +import {inject} from '@loopback/context'; +import {Account} from '../repositories/account/models/Account'; @api(def) export class AccountController { - @inject('repositories.account') private repository: AccountRepository + @inject('repositories.account') private repository: AccountRepository; constructor() {} //fixme figure out how to use Filter interface diff --git a/packages/example-microservices/services/account-without-juggler/index.ts b/packages/example-microservices/services/account-without-juggler/index.ts index 96c027a26125..f84ca8fd313a 100644 --- a/packages/example-microservices/services/account-without-juggler/index.ts +++ b/packages/example-microservices/services/account-without-juggler/index.ts @@ -3,9 +3,9 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { Application } from '@loopback/core'; -import { AccountController } from './controllers/AccountController'; -import { AccountRepository } from './repositories/account'; +import {Application} from '@loopback/core'; +import {AccountController} from './controllers/AccountController'; +import {AccountRepository} from './repositories/account'; class AccountMicroservice extends Application { private _startTime: Date; @@ -28,7 +28,7 @@ class AccountMicroservice extends Application { const port: Number = await this.get('http.port'); return { - appName: "account-without-juggler", + appName: 'account-without-juggler', uptime: Date.now() - this._startTime.getTime(), url: 'http://127.0.0.1:' + port, }; diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts index 4a6be5e38fdf..d349dc9f4ff0 100644 --- a/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts @@ -3,7 +3,9 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -const debug = require('debug')('loopback:repositories:account:datasources:connections:mysql'); +const debug = require('debug')( + 'loopback:repositories:account:datasources:connections:mysql', +); const mysql = require('mysql'); const db = require('mysql-promise')(); import { @@ -15,12 +17,12 @@ import { Filter, ObjectType, Options, - Where + Where, } from '@loopback/repository'; export class MySqlConn implements CrudConnector { //fixme make connection strongly typed - private connection: any + private connection: any; constructor(config: Object) { db.configure(config, mysql); @@ -31,10 +33,10 @@ export class MySqlConn implements CrudConnector { connect(): Promise { return this.connection.connect(); } - disconnect(): Promise { + disconnect(): Promise { return this.connection.end(); } - ping(): Promise { + ping(): Promise { return this.connection.ping(); } @@ -42,7 +44,7 @@ export class MySqlConn implements CrudConnector { modelClass: Class, data: EntityData, where: Where, - options: Options + options: Options, ): Promise { throw new Error('Not implemented yet.'); } @@ -50,7 +52,7 @@ export class MySqlConn implements CrudConnector { create( modelClass: Class, entity: EntityData, - options: Options + options: Options, ): Promise { let self = this; let placeHolders = []; @@ -77,7 +79,7 @@ export class MySqlConn implements CrudConnector { save( modelClass: Class, entity: EntityData, - options: Options + options: Options, ): Promise { throw new Error('Not implemented yet.'); } @@ -85,7 +87,7 @@ export class MySqlConn implements CrudConnector { find( modelClass: Class, filter: Filter, - options: Options + options: Options, ): Promise { let self = this; let findQuery = 'SELECT * FROM ?? '; @@ -106,7 +108,7 @@ export class MySqlConn implements CrudConnector { findById( modelClass: Class, id: any, - options: Options + options: Options, ): Promise { throw new Error('Not implemented yet.'); } @@ -114,7 +116,7 @@ export class MySqlConn implements CrudConnector { update( modelClass: Class, entity: EntityData, - options: Options + options: Options, ): Promise { throw new Error('Not implemented yet.'); } @@ -122,7 +124,7 @@ export class MySqlConn implements CrudConnector { delete( modelClass: Class, entity: EntityData, - options: Options + options: Options, ): Promise { throw new Error('Not implemented yet.'); } @@ -130,7 +132,7 @@ export class MySqlConn implements CrudConnector { createAll( modelClass: Class, entities: EntityData[], - options: Options + options: Options, ): Promise { throw new Error('Not implemented yet.'); } @@ -139,7 +141,7 @@ export class MySqlConn implements CrudConnector { modelClass: Class, id: any, data: EntityData, - options: Options + options: Options, ): Promise { let self = this; let updateQuery = 'UPDATE ?? SET '; @@ -162,7 +164,7 @@ export class MySqlConn implements CrudConnector { modelClass: Class, id: any, data: EntityData, - options: Options + options: Options, ): Promise { throw new Error('Not implemented yet.'); } @@ -170,7 +172,7 @@ export class MySqlConn implements CrudConnector { deleteAll( modelClass: Class, where: Where, - options: Options + options: Options, ): Promise { throw new Error('Not implemented yet.'); } @@ -178,7 +180,7 @@ export class MySqlConn implements CrudConnector { deleteById( modelClass: Class, id: any, - options: Options + options: Options, ): Promise { let self = this; let deleteQuery = 'DELETE FROM ?? '; @@ -195,7 +197,7 @@ export class MySqlConn implements CrudConnector { count( modelClass: Class, where: Where, - options: Options + options: Options, ): Promise { throw new Error('Not implemented yet.'); } @@ -203,7 +205,7 @@ export class MySqlConn implements CrudConnector { exists( modelClass: Class, id: any, - options: Options + options: Options, ): Promise { throw new Error('Not implemented yet.'); } diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlds.ts b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlds.ts index 3d27a2f73b22..66c21ddd1029 100644 --- a/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlds.ts +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlds.ts @@ -3,8 +3,8 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { MySqlConn } from './mysqlconn'; -import { DataSource } from '@loopback/repository'; +import {MySqlConn} from './mysqlconn'; +import {DataSource} from '@loopback/repository'; const mysqlCreds = require('./mysql.json'); export class MySqlDs implements DataSource { @@ -17,4 +17,3 @@ export class MySqlDs implements DataSource { this.connector = new MySqlConn(this.settings); } } - diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/index.ts b/packages/example-microservices/services/account-without-juggler/repositories/account/index.ts index 2f4384e0e319..71c98c30e6be 100644 --- a/packages/example-microservices/services/account-without-juggler/repositories/account/index.ts +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/index.ts @@ -3,10 +3,9 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { CrudRepositoryImpl } from '@loopback/repository'; -import { MySqlDs } from './datasources/mysqlds'; -import { Account } from './models/Account'; - +import {CrudRepositoryImpl} from '@loopback/repository'; +import {MySqlDs} from './datasources/mysqlds'; +import {Account} from './models/Account'; export class AccountRepository extends CrudRepositoryImpl { constructor() { diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts b/packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts index 3e72b9155385..760acb9d0eab 100644 --- a/packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts @@ -7,12 +7,15 @@ import { Entity, model, ModelDefinition, - PropertyDefinition + PropertyDefinition, } from '@loopback/repository'; @model(require('./account/model-definition')) export class Account extends Entity { - static definition = new ModelDefinition('Account', require('./account/model-definition').properties); + static definition = new ModelDefinition( + 'Account', + require('./account/model-definition').properties, + ); static modelName = 'Account'; id: string; diff --git a/packages/example-microservices/services/account-without-juggler/test/unit/account-controller.ts b/packages/example-microservices/services/account-without-juggler/test/unit/account-controller.ts index 89f72327bf18..ff865dcd4a95 100644 --- a/packages/example-microservices/services/account-without-juggler/test/unit/account-controller.ts +++ b/packages/example-microservices/services/account-without-juggler/test/unit/account-controller.ts @@ -4,9 +4,9 @@ // License text available at https://opensource.org/licenses/MIT import 'mocha'; -import { AccountController } from '../../controllers/AccountController'; -import { expect } from '@loopback/testlab'; -import { AccountRepository } from '../../repositories/account'; +import {AccountController} from '../../controllers/AccountController'; +import {expect} from '@loopback/testlab'; +import {AccountRepository} from '../../repositories/account'; let testController: any; @@ -17,14 +17,14 @@ const testAcc = { branch: 'Toronto', type: 'Chequing', avgBalance: 500, - minimumBalance: 0 + minimumBalance: 0, }; const brokenAcc = { customerNumber: '123456', balance: 1000, branch: 'Broke City', - type: 'Chequing' + type: 'Chequing', }; describe('AccountController Unit Test Suite', () => { @@ -33,7 +33,9 @@ describe('AccountController Unit Test Suite', () => { it('creates an account instance', async () => { const result = await testController.createAccount(testAcc); expect(result).to.deepEqual(testAcc); - const getResult = await testController.getAccount('{"where":{"id":"test1"}}'); + const getResult = await testController.getAccount( + '{"where":{"id":"test1"}}', + ); expect(getResult).to.not.be.empty(); expect(getResult).have.lengthOf(1); expect(getResult[0]).to.deepEqual(testAcc); @@ -55,10 +57,12 @@ describe('AccountController Unit Test Suite', () => { it('updates an account instance', async () => { const result = await testController.updateAccount('{"id":"test1"}}', { - balance: 2000 + balance: 2000, }); expect(result.count).to.be.equal(1); - const getResult = await testController.getAccount('{"where":{"id":"test1"}}'); + const getResult = await testController.getAccount( + '{"where":{"id":"test1"}}', + ); expect(getResult).to.not.be.empty(); expect(getResult).have.lengthOf(1); expect(getResult[0].id).to.be.equal(testAcc.id); @@ -68,7 +72,9 @@ describe('AccountController Unit Test Suite', () => { it('deletes an account instance', async () => { const result = await testController.deleteAccount('{"id":"test1"}}'); expect(result.count).to.be.equal(1); - const getResult = await testController.getAccount('{"where":{"id":"test1"}}'); + const getResult = await testController.getAccount( + '{"where":{"id":"test1"}}', + ); expect(getResult).to.be.empty(); }); }); diff --git a/packages/example-microservices/services/account/controllers/AccountController.api.ts b/packages/example-microservices/services/account/controllers/AccountController.api.ts index 79f7b9495827..ca696c383a40 100644 --- a/packages/example-microservices/services/account/controllers/AccountController.api.ts +++ b/packages/example-microservices/services/account/controllers/AccountController.api.ts @@ -13,16 +13,17 @@ export const def = { { name: 'filter', in: 'query', - description: 'The criteria used to narrow down the number of accounts returned.', + description: + 'The criteria used to narrow down the number of accounts returned.', required: false, - type: 'object' - } + type: 'object', + }, ], responses: { 200: { schema: { type: 'array', - items: '#/definitions/Account' + items: '#/definitions/Account', }, }, }, @@ -37,13 +38,13 @@ export const def = { in: 'body', description: 'The account instance to create.', required: true, - type: 'object' + type: 'object', }, ], responses: { 200: { schema: { - accountInstance: "#/definitions/Account" + accountInstance: '#/definitions/Account', }, }, }, @@ -56,17 +57,18 @@ export const def = { { name: 'where', in: 'query', - description: 'The criteria used to narrow down the number of accounts returned.', + description: + 'The criteria used to narrow down the number of accounts returned.', required: false, - type: 'object' + type: 'object', }, - { + { name: 'data', in: 'body', description: 'An object of model property name/value pairs', required: true, - type: 'object' - } + type: 'object', + }, ], responses: { 200: { @@ -76,13 +78,13 @@ export const def = { properties: { count: { type: 'number', - description: 'number of records updated' - } - } + description: 'number of records updated', + }, + }, }, }, }, - } + }, }, '/accounts/delete': { delete: { @@ -91,10 +93,11 @@ export const def = { { name: 'where', in: 'query', - description: 'The criteria used to narrow down which account instances to delete.', + description: + 'The criteria used to narrow down which account instances to delete.', required: true, - type:'object' - } + type: 'object', + }, ], responses: { 200: { @@ -104,16 +107,16 @@ export const def = { properties: { count: { type: 'number', - description: 'number of records deleted' - } - } + description: 'number of records deleted', + }, + }, }, }, }, - } - } + }, + }, }, definitions: { - Account: require('../repositories/account/models/account/model-definition.json') - } + Account: require('../repositories/account/models/account/model-definition.json'), + }, }; diff --git a/packages/example-microservices/services/account/index.ts b/packages/example-microservices/services/account/index.ts index 75140d20ba32..0a19c955979e 100644 --- a/packages/example-microservices/services/account/index.ts +++ b/packages/example-microservices/services/account/index.ts @@ -3,8 +3,8 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { Application } from '@loopback/core'; -import { AccountController } from './controllers/AccountController'; +import {Application} from '@loopback/core'; +import {AccountController} from './controllers/AccountController'; class AccountMicroservice extends Application { private _startTime: Date; @@ -25,7 +25,7 @@ class AccountMicroservice extends Application { const port: Number = await this.get('http.port'); return { - appName: "account", + appName: 'account', uptime: Date.now() - this._startTime.getTime(), url: 'http://127.0.0.1:' + port, }; diff --git a/packages/example-microservices/services/account/test/unit/test.data.json b/packages/example-microservices/services/account/test/unit/test.data.json index 983883439ebd..7d922bb6a32f 100644 --- a/packages/example-microservices/services/account/test/unit/test.data.json +++ b/packages/example-microservices/services/account/test/unit/test.data.json @@ -1,6 +1,6 @@ { "ids": { - "Account": 27 + "Account": 30 }, "models": { "Account": { diff --git a/packages/example-microservices/services/customer/controllers/CustomerController.api.ts b/packages/example-microservices/services/customer/controllers/CustomerController.api.ts index 9c561071a0b4..33333bd7d5c4 100644 --- a/packages/example-microservices/services/customer/controllers/CustomerController.api.ts +++ b/packages/example-microservices/services/customer/controllers/CustomerController.api.ts @@ -16,16 +16,17 @@ export const def = { description: 'The customer id.', required: true, type: 'string', - format: 'JSON' + format: 'JSON', }, { name: 'filter', in: 'query', - description: 'The criteria used to narrow down the number of customers returned.', + description: + 'The criteria used to narrow down the number of customers returned.', required: false, type: 'string', - format: 'JSON' - } + format: 'JSON', + }, ], responses: { 200: { @@ -36,39 +37,40 @@ export const def = { }, firstName: { type: 'string', - description: 'The customer\'s first name.', + description: "The customer's first name.", }, lastName: { type: 'string', - description: 'The customer\'s last name.', + description: "The customer's last name.", }, ssn: { type: 'string', - description: 'The customer\'s social security number.', + description: "The customer's social security number.", }, customerSince: { type: 'datetime', - description: 'The customer\'s registration date.' + description: "The customer's registration date.", }, street: { type: 'string', - description: 'The street name of the customer\'s address.', + description: "The street name of the customer's address.", }, state: { type: 'string', - description: 'The state of the customer\'s address.', + description: "The state of the customer's address.", }, city: { type: 'string', - description: 'The city of the customer\'s address.', + description: "The city of the customer's address.", }, zip: { type: 'string', - description: 'The zip code of the customer\'s address.', + description: "The zip code of the customer's address.", }, lastUpdated: { type: 'string', - description: 'The last time the customer\'s information was updated.', + description: + "The last time the customer's information was updated.", }, }, }, diff --git a/packages/example-microservices/services/customer/controllers/CustomerController.ts b/packages/example-microservices/services/customer/controllers/CustomerController.ts index 89772457115b..4a359e4cf688 100644 --- a/packages/example-microservices/services/customer/controllers/CustomerController.ts +++ b/packages/example-microservices/services/customer/controllers/CustomerController.ts @@ -3,9 +3,9 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { api } from '@loopback/core'; -import { def } from './CustomerController.api'; -import { CustomerRepository } from '../repositories/customer'; +import {api} from '@loopback/core'; +import {def} from './CustomerController.api'; +import {CustomerRepository} from '../repositories/customer'; @api(def) export class CustomerController { diff --git a/packages/example-microservices/services/customer/index.ts b/packages/example-microservices/services/customer/index.ts index 99ea0e404914..40d5dd5384fe 100644 --- a/packages/example-microservices/services/customer/index.ts +++ b/packages/example-microservices/services/customer/index.ts @@ -3,8 +3,8 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { Application } from '@loopback/core'; -import { CustomerController } from './controllers/CustomerController'; +import {Application} from '@loopback/core'; +import {CustomerController} from './controllers/CustomerController'; class CustomerApplication extends Application { private _startTime: Date; @@ -25,7 +25,7 @@ class CustomerApplication extends Application { const port: Number = await this.get('http.port'); return { - appName: "customer", + appName: 'customer', uptime: Date.now() - this._startTime.getTime(), url: 'http://127.0.0.1:' + port, }; diff --git a/packages/example-microservices/services/customer/repositories/customer/index.ts b/packages/example-microservices/services/customer/repositories/customer/index.ts index 63091ec18d56..4670f5cc20af 100644 --- a/packages/example-microservices/services/customer/repositories/customer/index.ts +++ b/packages/example-microservices/services/customer/repositories/customer/index.ts @@ -3,16 +3,16 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { juggler, DataSourceConstructor } from '@loopback/repository'; +import {juggler, DataSourceConstructor} from '@loopback/repository'; const modelDefinition = require('./models/customer/model-definition.json'); -export class CustomerRepository  { +export class CustomerRepository { model; constructor() { const ds: juggler.DataSource = new DataSourceConstructor('local-fs', { connector: 'memory', - file: './repositories/customer/datasources/local-fs/data.json' + file: './repositories/customer/datasources/local-fs/data.json', }); this.model = ds.createModel('Customer', modelDefinition); } diff --git a/packages/example-microservices/services/facade/controllers/AccountManagementController.api.ts b/packages/example-microservices/services/facade/controllers/AccountManagementController.api.ts index 6a7ffde92229..4eb594e94a54 100644 --- a/packages/example-microservices/services/facade/controllers/AccountManagementController.api.ts +++ b/packages/example-microservices/services/facade/controllers/AccountManagementController.api.ts @@ -13,10 +13,11 @@ export const def = { { name: 'accountNumber', in: 'query', - description: 'The account number to use when retrieving data from the underlying microservices.', + description: + 'The account number to use when retrieving data from the underlying microservices.', required: true, type: 'string', - } + }, ], responses: { 200: { @@ -47,8 +48,8 @@ export const def = { avgBalance: { type: 'number', description: 'average balance', - } - } + }, + }, }, customer: { type: 'array', @@ -96,10 +97,10 @@ export const def = { type: 'string', description: 'lastUpdated date of address of customer', }, - } - } - ] - } + }, + }, + ], + }, }, }, }, @@ -115,8 +116,8 @@ export const def = { description: 'The account instance.', required: true, type: 'object', - format: 'JSON' - } + format: 'JSON', + }, ], responses: { 200: { @@ -153,6 +154,6 @@ export const def = { }, }, }, - } + }, }, }; diff --git a/packages/example-microservices/services/facade/controllers/AccountManagementController.ts b/packages/example-microservices/services/facade/controllers/AccountManagementController.ts index 9234f448e4c0..be331a2e42f5 100644 --- a/packages/example-microservices/services/facade/controllers/AccountManagementController.ts +++ b/packages/example-microservices/services/facade/controllers/AccountManagementController.ts @@ -3,11 +3,11 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { api } from '@loopback/core'; -import { def } from './AccountManagementController.api'; -import { AccountRepository } from '../repositories/account'; -import { CustomerRepository } from '../repositories/customer'; -import { TransactionRepository } from '../repositories/transaction'; +import {api} from '@loopback/core'; +import {def} from './AccountManagementController.api'; +import {AccountRepository} from '../repositories/account'; +import {CustomerRepository} from '../repositories/customer'; +import {TransactionRepository} from '../repositories/transaction'; import bluebird = require('bluebird'); @api(def) diff --git a/packages/example-microservices/services/facade/index.ts b/packages/example-microservices/services/facade/index.ts index 6fea05c99414..c01583395157 100644 --- a/packages/example-microservices/services/facade/index.ts +++ b/packages/example-microservices/services/facade/index.ts @@ -3,8 +3,8 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { Application } from '@loopback/core'; -import { AccountController } from './controllers/AccountManagementController'; +import {Application} from '@loopback/core'; +import {AccountController} from './controllers/AccountManagementController'; class FacadeMicroservice extends Application { private _startTime: Date; @@ -23,7 +23,7 @@ class FacadeMicroservice extends Application { const port: Number = await this.get('http.port'); return { - appName: "facade", + appName: 'facade', uptime: Date.now() - this._startTime.getTime(), url: 'http://127.0.0.1:' + port, }; diff --git a/packages/example-microservices/services/facade/repositories/account/index.ts b/packages/example-microservices/services/facade/repositories/account/index.ts index ae5e5c505fef..6faa48769bcc 100644 --- a/packages/example-microservices/services/facade/repositories/account/index.ts +++ b/packages/example-microservices/services/facade/repositories/account/index.ts @@ -3,14 +3,14 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { juggler, DataSourceConstructor } from '@loopback/repository'; +import {juggler, DataSourceConstructor} from '@loopback/repository'; // mixin of data source into service is not yet available, swagger.json needs to // be loaded synchronously (ie. can't instantiate in the class constructor) const ds = new DataSourceConstructor('AccountService', { connector: 'swagger', - spec: 'repositories/account/swagger.json' + spec: 'repositories/account/swagger.json', }); export class AccountRepository { @@ -22,7 +22,7 @@ export class AccountRepository { async find(accountNumber) { const response = await this.model.findById({id: accountNumber}); - const accounts = response && response.obj || []; + const accounts = (response && response.obj) || []; return accounts.length ? accounts[0] : {}; } diff --git a/packages/example-microservices/services/facade/repositories/customer/index.ts b/packages/example-microservices/services/facade/repositories/customer/index.ts index 17d43069077c..a9ff676a3c56 100644 --- a/packages/example-microservices/services/facade/repositories/customer/index.ts +++ b/packages/example-microservices/services/facade/repositories/customer/index.ts @@ -3,7 +3,7 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { juggler, DataSourceConstructor } from '@loopback/repository'; +import {juggler, DataSourceConstructor} from '@loopback/repository'; // mixin of data source into service is not yet available, swagger.json needs to // be loaded synchronously (ie. can't instantiate in the class constructor) @@ -11,7 +11,7 @@ import { juggler, DataSourceConstructor } from '@loopback/repository'; var SwaggerClient = require('swagger-client'); const ds = new DataSourceConstructor('CustomerService', { connector: 'swagger', - spec: 'repositories/customer/swagger.json' + spec: 'repositories/customer/swagger.json', }); export class CustomerRepository { @@ -22,7 +22,7 @@ export class CustomerRepository { } async find(customerNumber) { - const response = await this.model.findById({ id: customerNumber }); - return response && response.obj || []; + const response = await this.model.findById({id: customerNumber}); + return (response && response.obj) || []; } } diff --git a/packages/example-microservices/services/facade/repositories/transaction/index.ts b/packages/example-microservices/services/facade/repositories/transaction/index.ts index 3ce75c9da8c1..0d261bf8f019 100644 --- a/packages/example-microservices/services/facade/repositories/transaction/index.ts +++ b/packages/example-microservices/services/facade/repositories/transaction/index.ts @@ -3,7 +3,7 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { juggler, DataSourceConstructor } from '@loopback/repository'; +import {juggler, DataSourceConstructor} from '@loopback/repository'; // mixin of data source into service is not yet available, swagger.json needs to // be loaded synchronously (ie. can't instantiate in the class constructor) @@ -11,7 +11,7 @@ import { juggler, DataSourceConstructor } from '@loopback/repository'; var SwaggerClient = require('swagger-client'); const ds = new DataSourceConstructor('TransactionService', { connector: 'swagger', - spec: 'repositories/transaction/swagger.json' + spec: 'repositories/transaction/swagger.json', }); export class TransactionRepository { @@ -22,7 +22,7 @@ export class TransactionRepository { } async find(accountNumber) { - const response = await this.model.findById({ id: accountNumber }); - return response && response.obj || []; + const response = await this.model.findById({id: accountNumber}); + return (response && response.obj) || []; } } diff --git a/packages/example-microservices/services/todo-legacy/application.ts b/packages/example-microservices/services/todo-legacy/application.ts index 289d7c7e4d2c..06feb9be882f 100644 --- a/packages/example-microservices/services/todo-legacy/application.ts +++ b/packages/example-microservices/services/todo-legacy/application.ts @@ -3,14 +3,14 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { Application } from '@loopback/core'; -import { TodoController } from './controllers/todo-controller'; +import {Application} from '@loopback/core'; +import {TodoController} from './controllers/todo-controller'; import { juggler, DataSourceConstructor, - DefaultCrudRepository + DefaultCrudRepository, } from '@loopback/repository'; -import { datasources } from './datasources'; +import {datasources} from './datasources'; export class TodoApplication extends Application { private _startTime: Date; @@ -30,7 +30,7 @@ export class TodoApplication extends Application { app.bind('servers.https.enabled').to(true); } - async start() : Promise { + async start(): Promise { this._startTime = new Date(); return super.start(); } @@ -38,10 +38,14 @@ export class TodoApplication extends Application { async info() { const port: Number = await this.get('http.port'); - return JSON.stringify({ - appName: "todo-legecy", - uptime: Date.now() - this._startTime.getTime(), - url: 'http://127.0.0.1:' + port, - }, null, 2); + return JSON.stringify( + { + appName: 'todo-legecy', + uptime: Date.now() - this._startTime.getTime(), + url: 'http://127.0.0.1:' + port, + }, + null, + 2, + ); } } diff --git a/packages/example-microservices/services/todo-legacy/datasources.ts b/packages/example-microservices/services/todo-legacy/datasources.ts index fb3b89b00dc9..2bdc42f7efb6 100644 --- a/packages/example-microservices/services/todo-legacy/datasources.ts +++ b/packages/example-microservices/services/todo-legacy/datasources.ts @@ -12,23 +12,23 @@ export const datasources: DataSourceConfig = { database: 'testdb', password: 'pass', user: 'root', - } - }; + }, +}; export interface DataSourceConfig { - [datasource: string]: DataSourceDefinition + [datasource: string]: DataSourceDefinition; } /** * The parameters required to define a MySQL datasource. - * + * * @export * @interface DataSourceDefinition */ export interface DataSourceDefinition { - /** + /** * The name of the connection (for programmatic reference). - * + * * @type {string} * @memberof DataSourceDefinition */ @@ -36,14 +36,14 @@ export interface DataSourceDefinition { /** * The identifying name of the legacy connector module used to interact with * the datasource. - * (ex. "mysql", "mongodb", "db2", etc...) - * + * (ex. "mysql", "mongodb", "db2", etc...) + * * @type {string} * @memberof DataSourceDefinition */ connector: string; /** - * + * * The accessible machine name, domain address or IP address of the * datasource. * @type {string} @@ -52,7 +52,7 @@ export interface DataSourceDefinition { host: string; /** * The port number on which the datasource is listening for connections. - * + * * @type {number} * @memberof DataSourceDefinition */ diff --git a/packages/example-microservices/services/todo-legacy/index.ts b/packages/example-microservices/services/todo-legacy/index.ts index 999d5c206b0b..4e903e87b54e 100644 --- a/packages/example-microservices/services/todo-legacy/index.ts +++ b/packages/example-microservices/services/todo-legacy/index.ts @@ -3,9 +3,8 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { Application } from '@loopback/core'; -import { TodoApplication } from './application'; - +import {Application} from '@loopback/core'; +import {TodoApplication} from './application'; async function main(): Promise { const app = new TodoApplication(); diff --git a/packages/example-microservices/services/todo-legacy/models/todo.ts b/packages/example-microservices/services/todo-legacy/models/todo.ts index 23d563c97686..5000396fd2e0 100644 --- a/packages/example-microservices/services/todo-legacy/models/todo.ts +++ b/packages/example-microservices/services/todo-legacy/models/todo.ts @@ -3,26 +3,26 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { Entity, model, property } from '@loopback/repository'; +import {Entity, model, property} from '@loopback/repository'; @model() export class Todo extends Entity { @property({ - type: "number", + type: 'number', id: true, - description: "The ID number of the Todo entry.", + description: 'The ID number of the Todo entry.', }) id: number; @property({ - type: "string", - description: "The title of the todo.", + type: 'string', + description: 'The title of the todo.', }) title: string; @property({ - type: "string", - description: "The main body of the todo.", + type: 'string', + description: 'The main body of the todo.', }) body: string; diff --git a/packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts b/packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts index fc45c1bbd863..8f8efec7730a 100644 --- a/packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts +++ b/packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts @@ -5,14 +5,14 @@ import 'mocha'; import * as _ from 'lodash'; -import { expect, sinon } from '@loopback/testlab'; +import {expect, sinon} from '@loopback/testlab'; import { DefaultCrudRepository, DataSourceConstructor, ModelBaseConstructor, } from '@loopback/repository'; -import { TodoController } from '../../controllers/todo-controller'; -import { Todo } from '../../models/todo'; +import {TodoController} from '../../controllers/todo-controller'; +import {Todo} from '../../models/todo'; import * as util from 'util'; @@ -23,12 +23,9 @@ describe('TodoController', () => { // building the stubs and fakes by hand! let datasource = new DataSourceConstructor({ name: 'ds', - connector: 'memory' + connector: 'memory', }); - let repository = new DefaultCrudRepository( - Todo, - datasource - ); + let repository = new DefaultCrudRepository(Todo, datasource); controller.repository = repository; describe('getTodo', () => { @@ -55,10 +52,10 @@ describe('TodoController', () => { stub.getCall(0).args, [ { - where: { title: 'test2' } - } + where: {title: 'test2'}, + }, ], - 'controller created correct filter object' + 'controller created correct filter object', ); }); }); @@ -72,7 +69,7 @@ describe('TodoController', () => { let stub = sandbox.stub(repository, 'create'); let result = await controller.create({ title: 'foo', - body: 'bar' + body: 'bar', }); expect.ok(stub.called, 'create was called'); }); @@ -89,7 +86,7 @@ describe('TodoController', () => { Object.assign(replacement, { id: 1, title: 'foo', - body: 'bar' + body: 'bar', }); let result = await controller.replace(1, replacement); expect.ok(stub.called, 'replace was called'); @@ -105,9 +102,9 @@ describe('TodoController', () => { let stub = sandbox.stub(controller.repository, 'updateById'); let replacement = { id: 1, - title: 'foo' + title: 'foo', }; - let expected = _.merge({ id: 1 }, replacement); + let expected = _.merge({id: 1}, replacement); let result = await controller.update(1, replacement); expect.ok(stub.called, 'update was called'); }); @@ -138,11 +135,11 @@ describe('TodoController', () => { [ { where: { - title: 'test2' - } - } + title: 'test2', + }, + }, ], - 'controller created correct filter object' + 'controller created correct filter object', ); }); }); diff --git a/packages/example-microservices/services/transaction/controllers/TransactionController.api.ts b/packages/example-microservices/services/transaction/controllers/TransactionController.api.ts index d5cd885a3abf..3e560c898fae 100644 --- a/packages/example-microservices/services/transaction/controllers/TransactionController.api.ts +++ b/packages/example-microservices/services/transaction/controllers/TransactionController.api.ts @@ -16,16 +16,17 @@ export const def = { description: 'The transaction id', required: true, type: 'string', - format: 'JSON' + format: 'JSON', }, { name: 'filter', in: 'query', - description: 'The criteria used to narrow down the number of transactions returned.', + description: + 'The criteria used to narrow down the number of transactions returned.', required: false, type: 'string', - format: 'JSON' - } + format: 'JSON', + }, ], responses: { 200: { @@ -36,20 +37,21 @@ export const def = { }, dateTime: { type: 'date', - description: 'The date and time of the transaction.' + description: 'The date and time of the transaction.', }, accountNo: { type: 'string', - description: 'The associated account number.' + description: 'The associated account number.', }, amount: { type: 'number', - description: 'The amount being consider in the transaction.' + description: 'The amount being consider in the transaction.', }, transactionType: { type: 'string', - description: 'The type of transaction. Can be "credit" or "debit".' - } + description: + 'The type of transaction. Can be "credit" or "debit".', + }, }, }, }, diff --git a/packages/example-microservices/services/transaction/controllers/TransactionController.ts b/packages/example-microservices/services/transaction/controllers/TransactionController.ts index ec2ec78d2e26..529e68d16eb7 100644 --- a/packages/example-microservices/services/transaction/controllers/TransactionController.ts +++ b/packages/example-microservices/services/transaction/controllers/TransactionController.ts @@ -3,9 +3,9 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { api } from '@loopback/core'; -import { def } from './TransactionController.api'; -import { TransactionRepository } from '../repositories/transaction'; +import {api} from '@loopback/core'; +import {def} from './TransactionController.api'; +import {TransactionRepository} from '../repositories/transaction'; @api(def) export class TransactionController { diff --git a/packages/example-microservices/services/transaction/index.ts b/packages/example-microservices/services/transaction/index.ts index 0b748f73b5f6..568ce21c0cce 100644 --- a/packages/example-microservices/services/transaction/index.ts +++ b/packages/example-microservices/services/transaction/index.ts @@ -3,8 +3,8 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { Application } from '@loopback/core'; -import { TransactionController } from './controllers/TransactionController'; +import {Application} from '@loopback/core'; +import {TransactionController} from './controllers/TransactionController'; class TransactionApplication extends Application { private _startTime: Date; @@ -25,7 +25,7 @@ class TransactionApplication extends Application { const port: Number = await this.get('http.port'); return { - appName: "transaction", + appName: 'transaction', uptime: Date.now() - this._startTime.getTime(), url: 'http://127.0.0.1:' + port, }; @@ -41,4 +41,4 @@ async function main(): Promise { main().catch(err => { console.log('Cannot start the app.', err); process.exit(1); -}); \ No newline at end of file +}); diff --git a/packages/example-microservices/services/transaction/repositories/transaction/index.ts b/packages/example-microservices/services/transaction/repositories/transaction/index.ts index 85f351219452..2937c40049df 100644 --- a/packages/example-microservices/services/transaction/repositories/transaction/index.ts +++ b/packages/example-microservices/services/transaction/repositories/transaction/index.ts @@ -3,7 +3,7 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import { juggler, DataSourceConstructor } from '@loopback/repository'; +import {juggler, DataSourceConstructor} from '@loopback/repository'; const modelDefinition = require('./models/transaction/model-definition.json'); export class TransactionRepository { @@ -12,12 +12,12 @@ export class TransactionRepository { constructor() { const ds = new DataSourceConstructor('local-fs', { connector: 'memory', - file: './repositories/transaction/datasources/local-fs/data.json' + file: './repositories/transaction/datasources/local-fs/data.json', }); this.model = ds.createModel('Transaction', modelDefinition); } async find(id): Promise { - return await this.model.find({ where: { accountNo: id } }); + return await this.model.find({where: {accountNo: id}}); } } From 0e4a921104d81637c6028a157a5200fa4f66dccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 30 Jan 2018 15:14:45 +0100 Subject: [PATCH 5/8] fixup! address more linter errors (mostly tslint) --- packages/example-microservices/bin/test.js | 85 ++++++++++--------- .../controllers/AccountController.api.ts | 56 ++++++------ .../controllers/AccountController.ts | 9 +- .../account-without-juggler/package.json | 7 +- .../account/datasources/mysqlconn.ts | 33 +++---- .../repositories/account/models/Account.ts | 8 +- ...ntroller.ts => account-controller.test.ts} | 19 ++--- .../services/account/test/unit/test.data.json | 2 +- .../controllers/CustomerController.ts | 4 +- .../services/customer/package.json | 1 + .../customer/repositories/customer/index.ts | 2 + .../AccountManagementController.ts | 4 +- .../services/facade/package.json | 7 +- .../facade/repositories/account/index.ts | 2 + .../facade/repositories/customer/index.ts | 2 +- .../facade/repositories/transaction/index.ts | 2 +- .../services/todo-legacy/application.ts | 4 +- .../controllers/todo-controller.ts | 14 +-- .../services/todo-legacy/datasources.ts | 2 + .../test/controller/todo-controller.test.ts | 54 ++++++------ .../controllers/TransactionController.ts | 4 +- .../services/transaction/package.json | 5 +- .../repositories/transaction/index.ts | 2 + packages/repository/src/model.ts | 2 +- 24 files changed, 177 insertions(+), 153 deletions(-) rename packages/example-microservices/services/account-without-juggler/test/unit/{account-controller.ts => account-controller.test.ts} (79%) diff --git a/packages/example-microservices/bin/test.js b/packages/example-microservices/bin/test.js index af07c3ebbd8c..a786e0e101b7 100644 --- a/packages/example-microservices/bin/test.js +++ b/packages/example-microservices/bin/test.js @@ -6,48 +6,51 @@ 'use strict'; -let Promise = require('bluebird'); -let exec = require('child_process').execSync; -let spawn = require('child_process').spawn; -let fs = Promise.promisifyAll(require('fs')); -let path = require('path'); +const Promise = require('bluebird'); +const exec = require('child_process').execSync; +const spawn = require('child_process').spawn; +const fs = Promise.promisifyAll(require('fs')); +const path = require('path'); -let cmd = path.resolve(__dirname, '..', 'node_modules', '.bin', '_mocha'); -let args = ['--compilers', 'ts:ts-node/register,tsx:ts-node/register']; +const cmd = path.resolve(__dirname, '..', 'node_modules', '.bin', '_mocha'); +const args = ['--compilers', 'ts:ts-node/register,tsx:ts-node/register']; -let services = path.resolve(__dirname, '..', 'services'); -return fs.readdirAsync(services).then(folders => { - return Promise.each(folders, f => { - let dir = path.resolve(services, f); - return fs - .readdirAsync(dir) - .then(subfolders => { - if (subfolders.indexOf('test') > -1) { - return new Promise((resolve, reject) => { - console.log('RUN TESTS - %s:', f); - let testArgs = args.push(path.resolve(dir, 'test/**/*test.ts')); - let test = spawn(cmd, args, {stdio: 'inherit'}); - test.on('close', code => { - if (code) { - return reject(code); - } else { - console.log('TEST SUCCESS - %s', f); - return resolve(); - } +const services = path.resolve(__dirname, '..', 'services'); +return fs + .readdirAsync(services) + .then(folders => { + return Promise.each(folders, f => { + const dir = path.resolve(services, f); + return fs + .readdirAsync(dir) + .then(subfolders => { + if (subfolders.indexOf('test') > -1) { + return new Promise((resolve, reject) => { + console.log('RUN TESTS - %s:', f); + const testArgs = args.push(path.resolve(dir, 'test/**/*test.ts')); + const test = spawn(cmd, args, {stdio: 'inherit'}); + test.on('close', code => { + if (code) { + return reject(code); + } else { + console.log('TEST SUCCESS - %s', f); + return resolve(); + } + }); }); - }); - } else { - console.log('No "test" folder was found in %s', f); - return Promise.resolve(); - } - }) - .catch(code => { - return Promise.reject(`TESTS FAILED - ${f}, exit code ${code}`); - }); - }).then(() => { - console.log('TESTS COMPLETE'); + } else { + console.log('No "test" folder was found in %s', f); + return Promise.resolve(); + } + }) + .catch(code => { + return Promise.reject(`TESTS FAILED - ${f}, exit code ${code}`); + }); + }).then(() => { + console.log('TESTS COMPLETE'); + }); + }) + .catch(err => { + console.log(err); + process.exit(1); }); -}).catch(err => { - console.log(err); - process.exit(1); -}); diff --git a/packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts index 960281819097..212bd8fc5449 100644 --- a/packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts +++ b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.api.ts @@ -116,33 +116,35 @@ export const def = { }, definitions: { Account: { - id: { - type: 'string', - description: 'The ID for the account instance.', - }, - customerNumber: { - type: 'string', - description: 'The customer ID for the account instance.', - }, - balance: { - type: 'number', - description: 'The current balance for the account instance.', - }, - branch: { - type: 'string', - description: 'The branch location for the account instance.', - }, - type: { - type: 'string', - description: 'The type of banking account.', - }, - avgBalance: { - type: 'number', - description: 'The average balance for the account instance.', - }, - minimumBalance: { - type: 'number', - description: 'The minimum balance for the account instance.', + properties: { + id: { + type: 'string', + description: 'The ID for the account instance.', + }, + customerNumber: { + type: 'string', + description: 'The customer ID for the account instance.', + }, + balance: { + type: 'number', + description: 'The current balance for the account instance.', + }, + branch: { + type: 'string', + description: 'The branch location for the account instance.', + }, + type: { + type: 'string', + description: 'The type of banking account.', + }, + avgBalance: { + type: 'number', + description: 'The average balance for the account instance.', + }, + minimumBalance: { + type: 'number', + description: 'The minimum balance for the account instance.', + }, }, }, }, diff --git a/packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts index 5aaf088d3e40..8c3e8a29016d 100644 --- a/packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts +++ b/packages/example-microservices/services/account-without-juggler/controllers/AccountController.ts @@ -3,16 +3,17 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import {api} from '@loopback/core'; +import {api} from '@loopback/rest'; import {def} from './AccountController.api'; import {AccountRepository} from '../repositories/account'; -import {inject} from '@loopback/context'; +import {inject} from '@loopback/core'; import {Account} from '../repositories/account/models/Account'; @api(def) export class AccountController { - @inject('repositories.account') private repository: AccountRepository; - constructor() {} + constructor( + @inject('repositories.account') private repository: AccountRepository, + ) {} //fixme figure out how to use Filter interface //fixme filter is string even though swagger spec diff --git a/packages/example-microservices/services/account-without-juggler/package.json b/packages/example-microservices/services/account-without-juggler/package.json index dcf99a876dc1..9d02897473ae 100644 --- a/packages/example-microservices/services/account-without-juggler/package.json +++ b/packages/example-microservices/services/account-without-juggler/package.json @@ -9,11 +9,12 @@ "url": "https://github.com/strongloop/loopback-next.git" }, "dependencies": { - "mysql": "^2.13.0", - "mysql-promise": "^4.1.0", "@loopback/core": "^4.0.0-alpha.10", "@loopback/repository": "^4.0.0-alpha.5", - "loopback-datasource-juggler": "^3.4.1" + "@loopback/rest": "^4.0.0-alpha.22", + "loopback-datasource-juggler": "^3.4.1", + "mysql": "^2.13.0", + "mysql-promise": "^4.1.0" }, "devDependencies": { "@loopback/testlab": "^4.0.0-alpha.6", diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts index d349dc9f4ff0..f6d1732e96ef 100644 --- a/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/datasources/mysqlconn.ts @@ -3,6 +3,8 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT +// tslint:disable:no-any + const debug = require('debug')( 'loopback:repositories:account:datasources:connections:mysql', ); @@ -54,17 +56,18 @@ export class MySqlConn implements CrudConnector { entity: EntityData, options: Options, ): Promise { - let self = this; - let placeHolders = []; - for (var prop in modelClass.definition.properties) { + const self = this; + const placeHolders = []; + for (const prop in modelClass.definition.properties) { placeHolders.push('?'); } - let createQuery = 'INSERT INTO ?? VALUES (' + placeHolders.join(',') + ')'; - var vals = [modelClass.modelName]; - for (var prop in entity) { + const createQuery = + 'INSERT INTO ?? VALUES (' + placeHolders.join(',') + ')'; + const vals = [modelClass.modelName]; + for (const prop in entity) { vals.push(entity[prop]); } - let sqlStmt = mysql.format(createQuery, vals); + const sqlStmt = mysql.format(createQuery, vals); debug('Insert ', sqlStmt); return self.connection.query(sqlStmt).spread(function(result: any) { @@ -89,12 +92,12 @@ export class MySqlConn implements CrudConnector { filter: Filter, options: Options, ): Promise { - let self = this; + const self = this; let findQuery = 'SELECT * FROM ?? '; findQuery = mysql.format(findQuery, [modelClass.modelName]); if (filter.where) { let whereClause = '?? = ?'; - for (var key in filter.where) { + for (const key in filter.where) { whereClause = mysql.format(whereClause, [key, filter.where[key]]); } findQuery += ' WHERE ' + whereClause; @@ -143,15 +146,15 @@ export class MySqlConn implements CrudConnector { data: EntityData, options: Options, ): Promise { - let self = this; + const self = this; let updateQuery = 'UPDATE ?? SET '; updateQuery = mysql.format(updateQuery, [modelClass.modelName]); - let updateClause = []; - for (var prop in data) { + const updateClause = []; + for (const prop in data) { updateClause.push(mysql.format('??=?', [prop, data[prop]])); } updateQuery += updateClause.join(','); - let whereClause = mysql.format(' WHERE ??=?', ['id', id]); + const whereClause = mysql.format(' WHERE ??=?', ['id', id]); updateQuery += whereClause; debug('updateById ', updateQuery); @@ -182,10 +185,10 @@ export class MySqlConn implements CrudConnector { id: any, options: Options, ): Promise { - let self = this; + const self = this; let deleteQuery = 'DELETE FROM ?? '; deleteQuery = mysql.format(deleteQuery, modelClass.modelName); - let whereClause = mysql.format(' WHERE ??=?', ['id', id]); + const whereClause = mysql.format(' WHERE ??=?', ['id', id]); deleteQuery += whereClause; debug('deleteById ', deleteQuery); diff --git a/packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts b/packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts index 760acb9d0eab..78c42bd9ed6b 100644 --- a/packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts +++ b/packages/example-microservices/services/account-without-juggler/repositories/account/models/Account.ts @@ -10,12 +10,10 @@ import { PropertyDefinition, } from '@loopback/repository'; -@model(require('./account/model-definition')) +const definition = require('./account/model-definition'); +@model(definition) export class Account extends Entity { - static definition = new ModelDefinition( - 'Account', - require('./account/model-definition').properties, - ); + static definition = new ModelDefinition(definition); static modelName = 'Account'; id: string; diff --git a/packages/example-microservices/services/account-without-juggler/test/unit/account-controller.ts b/packages/example-microservices/services/account-without-juggler/test/unit/account-controller.test.ts similarity index 79% rename from packages/example-microservices/services/account-without-juggler/test/unit/account-controller.ts rename to packages/example-microservices/services/account-without-juggler/test/unit/account-controller.test.ts index ff865dcd4a95..aa98a580731a 100644 --- a/packages/example-microservices/services/account-without-juggler/test/unit/account-controller.ts +++ b/packages/example-microservices/services/account-without-juggler/test/unit/account-controller.test.ts @@ -8,7 +8,7 @@ import {AccountController} from '../../controllers/AccountController'; import {expect} from '@loopback/testlab'; import {AccountRepository} from '../../repositories/account'; -let testController: any; +let testController: AccountController; const testAcc = { id: 'test1', @@ -27,7 +27,9 @@ const brokenAcc = { type: 'Chequing', }; -describe('AccountController Unit Test Suite', () => { +// NOTE(bajtos) These tests require a MySQL database running on localhost +// Our CI setup is not ready for that yet, so let's skip these tests for now. +describe.skip('AccountController Unit Test Suite', () => { before(createAccountController); it('creates an account instance', async () => { @@ -56,10 +58,8 @@ describe('AccountController Unit Test Suite', () => { }); it('updates an account instance', async () => { - const result = await testController.updateAccount('{"id":"test1"}}', { - balance: 2000, - }); - expect(result.count).to.be.equal(1); + const result = await testController.updateById('test1', {balance: 2000}); + expect(result).to.equal(true); const getResult = await testController.getAccount( '{"where":{"id":"test1"}}', ); @@ -70,8 +70,8 @@ describe('AccountController Unit Test Suite', () => { }); it('deletes an account instance', async () => { - const result = await testController.deleteAccount('{"id":"test1"}}'); - expect(result.count).to.be.equal(1); + const result = await testController.deleteById('test1'); + expect(result).to.equal(true); const getResult = await testController.getAccount( '{"where":{"id":"test1"}}', ); @@ -80,6 +80,5 @@ describe('AccountController Unit Test Suite', () => { }); function createAccountController() { - testController = new AccountController(); - testController.repository = new AccountRepository(); + testController = new AccountController(new AccountRepository()); } diff --git a/packages/example-microservices/services/account/test/unit/test.data.json b/packages/example-microservices/services/account/test/unit/test.data.json index 7d922bb6a32f..e63da31c27ab 100644 --- a/packages/example-microservices/services/account/test/unit/test.data.json +++ b/packages/example-microservices/services/account/test/unit/test.data.json @@ -1,6 +1,6 @@ { "ids": { - "Account": 30 + "Account": 34 }, "models": { "Account": { diff --git a/packages/example-microservices/services/customer/controllers/CustomerController.ts b/packages/example-microservices/services/customer/controllers/CustomerController.ts index 4a359e4cf688..1e75462853ed 100644 --- a/packages/example-microservices/services/customer/controllers/CustomerController.ts +++ b/packages/example-microservices/services/customer/controllers/CustomerController.ts @@ -3,10 +3,12 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import {api} from '@loopback/core'; +import {api} from '@loopback/rest'; import {def} from './CustomerController.api'; import {CustomerRepository} from '../repositories/customer'; +// tslint:disable:no-any + @api(def) export class CustomerController { repository: CustomerRepository; diff --git a/packages/example-microservices/services/customer/package.json b/packages/example-microservices/services/customer/package.json index d27f1a61bc72..6ae35f8f030e 100644 --- a/packages/example-microservices/services/customer/package.json +++ b/packages/example-microservices/services/customer/package.json @@ -11,6 +11,7 @@ "dependencies": { "@loopback/core": "^4.0.0-alpha.10", "@loopback/repository": "^4.0.0-alpha.5", + "@loopback/rest": "^4.0.0-alpha.22", "loopback-datasource-juggler": "^3.4.1" }, "devDependencies": { diff --git a/packages/example-microservices/services/customer/repositories/customer/index.ts b/packages/example-microservices/services/customer/repositories/customer/index.ts index 4670f5cc20af..2985a78d581d 100644 --- a/packages/example-microservices/services/customer/repositories/customer/index.ts +++ b/packages/example-microservices/services/customer/repositories/customer/index.ts @@ -6,6 +6,8 @@ import {juggler, DataSourceConstructor} from '@loopback/repository'; const modelDefinition = require('./models/customer/model-definition.json'); +// tslint:disable:no-any + export class CustomerRepository { model; diff --git a/packages/example-microservices/services/facade/controllers/AccountManagementController.ts b/packages/example-microservices/services/facade/controllers/AccountManagementController.ts index be331a2e42f5..21690fa51708 100644 --- a/packages/example-microservices/services/facade/controllers/AccountManagementController.ts +++ b/packages/example-microservices/services/facade/controllers/AccountManagementController.ts @@ -3,13 +3,15 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import {api} from '@loopback/core'; +import {api} from '@loopback/rest'; import {def} from './AccountManagementController.api'; import {AccountRepository} from '../repositories/account'; import {CustomerRepository} from '../repositories/customer'; import {TransactionRepository} from '../repositories/transaction'; import bluebird = require('bluebird'); +// tslint:disable:no-any + @api(def) export class AccountController { accountRepository: AccountRepository; diff --git a/packages/example-microservices/services/facade/package.json b/packages/example-microservices/services/facade/package.json index 0b82b4518c05..9a2a4b642aa5 100644 --- a/packages/example-microservices/services/facade/package.json +++ b/packages/example-microservices/services/facade/package.json @@ -9,10 +9,11 @@ "url": "https://github.com/strongloop/loopback-next.git" }, "dependencies": { - "loopback-connector-swagger": "^3.2.0", - "loopback-datasource-juggler": "^3.4.1", "@loopback/core": "^4.0.0-alpha.10", - "@loopback/repository": "^4.0.0-alpha.5" + "@loopback/repository": "^4.0.0-alpha.5", + "@loopback/rest": "^4.0.0-alpha.22", + "loopback-connector-swagger": "^3.2.0", + "loopback-datasource-juggler": "^3.4.1" }, "devDependencies": { "@types/node": "^7.0.12" diff --git a/packages/example-microservices/services/facade/repositories/account/index.ts b/packages/example-microservices/services/facade/repositories/account/index.ts index 6faa48769bcc..c466f93974f7 100644 --- a/packages/example-microservices/services/facade/repositories/account/index.ts +++ b/packages/example-microservices/services/facade/repositories/account/index.ts @@ -5,6 +5,8 @@ import {juggler, DataSourceConstructor} from '@loopback/repository'; +// tslint:disable:no-any + // mixin of data source into service is not yet available, swagger.json needs to // be loaded synchronously (ie. can't instantiate in the class constructor) diff --git a/packages/example-microservices/services/facade/repositories/customer/index.ts b/packages/example-microservices/services/facade/repositories/customer/index.ts index a9ff676a3c56..995423aa4c84 100644 --- a/packages/example-microservices/services/facade/repositories/customer/index.ts +++ b/packages/example-microservices/services/facade/repositories/customer/index.ts @@ -8,7 +8,7 @@ import {juggler, DataSourceConstructor} from '@loopback/repository'; // mixin of data source into service is not yet available, swagger.json needs to // be loaded synchronously (ie. can't instantiate in the class constructor) -var SwaggerClient = require('swagger-client'); +const SwaggerClient = require('swagger-client'); const ds = new DataSourceConstructor('CustomerService', { connector: 'swagger', spec: 'repositories/customer/swagger.json', diff --git a/packages/example-microservices/services/facade/repositories/transaction/index.ts b/packages/example-microservices/services/facade/repositories/transaction/index.ts index 0d261bf8f019..a3f3ab617c00 100644 --- a/packages/example-microservices/services/facade/repositories/transaction/index.ts +++ b/packages/example-microservices/services/facade/repositories/transaction/index.ts @@ -8,7 +8,7 @@ import {juggler, DataSourceConstructor} from '@loopback/repository'; // mixin of data source into service is not yet available, swagger.json needs to // be loaded synchronously (ie. can't instantiate in the class constructor) -var SwaggerClient = require('swagger-client'); +const SwaggerClient = require('swagger-client'); const ds = new DataSourceConstructor('TransactionService', { connector: 'swagger', spec: 'repositories/transaction/swagger.json', diff --git a/packages/example-microservices/services/todo-legacy/application.ts b/packages/example-microservices/services/todo-legacy/application.ts index 06feb9be882f..4dd1a1a9d707 100644 --- a/packages/example-microservices/services/todo-legacy/application.ts +++ b/packages/example-microservices/services/todo-legacy/application.ts @@ -18,11 +18,11 @@ export class TodoApplication extends Application { constructor() { super(); const app = this; - let ds = datasources['ds']; + const ds = datasources['ds']; // Controller bindings app.controller(TodoController); - let datasource = new DataSourceConstructor('ds', ds); + const datasource = new DataSourceConstructor('ds', ds); app.bind('datasources.ds').to(datasource); // Server protocol bindings diff --git a/packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts b/packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts index 0695bc5396c6..7bedc8821d1f 100644 --- a/packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts +++ b/packages/example-microservices/services/todo-legacy/controllers/todo-controller.ts @@ -18,7 +18,7 @@ export class TodoController { constructor() {} async get(title?: string): Promise { - let filter = title ? {where: {title: title}} : {}; + const filter = title ? {where: {title: title}} : {}; return await this.repository.find(filter); } @@ -33,14 +33,14 @@ export class TodoController { async update(id: number, body: Object): Promise { let success: boolean; if (id) { - let todo = new Todo(body); + const todo = new Todo(body); todo.id = id; success = await this.repository.updateById(id, todo); // FIXME(kev): Unhandled error is thrown if you attempt to return // the boolean value that the repository.update method returns. return Promise.resolve({count: success ? 1 : 0}); } else if (body) { - let result = await this.repository.updateAll(new Todo(body)); + const result = await this.repository.updateAll(new Todo(body)); return Promise.resolve({count: result}); } else { return Promise.reject( @@ -50,7 +50,7 @@ export class TodoController { } async replace(id: number, body: Todo): Promise { - let success = await this.repository.replaceById(id, new Todo(body)); + const success = await this.repository.replaceById(id, new Todo(body)); // FIXME(kev): Unhandled error is thrown if you attempt to return // the boolean value that the repository.replaceById method returns. return Promise.resolve({count: success ? 1 : 0}); @@ -60,13 +60,13 @@ export class TodoController { if (!title) { return Promise.reject(new Error('You must provide a filter query!')); } - let filter = {where: {title: title}}; - let result = await this.repository.deleteAll(filter); + const filter = {where: {title: title}}; + const result = await this.repository.deleteAll(filter); return Promise.resolve({count: result}); } async deleteById(id: number): Promise { - let success = await this.repository.deleteById(id); + const success = await this.repository.deleteById(id); // FIXME(kev): Unhandled error is thrown if you attempt to return // the boolean value that the repository.replaceById method returns. return Promise.resolve({count: success ? 1 : 0}); diff --git a/packages/example-microservices/services/todo-legacy/datasources.ts b/packages/example-microservices/services/todo-legacy/datasources.ts index 2bdc42f7efb6..fb8aa103f43f 100644 --- a/packages/example-microservices/services/todo-legacy/datasources.ts +++ b/packages/example-microservices/services/todo-legacy/datasources.ts @@ -3,6 +3,8 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT +// tslint:disable:no-any + export const datasources: DataSourceConfig = { ds: { name: 'ds', diff --git a/packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts b/packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts index 8f8efec7730a..b5753cd60cec 100644 --- a/packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts +++ b/packages/example-microservices/services/todo-legacy/test/controller/todo-controller.test.ts @@ -17,36 +17,36 @@ import {Todo} from '../../models/todo'; import * as util from 'util'; describe('TodoController', () => { - let controller = new TodoController(); + const controller = new TodoController(); // NOTE: Creating the datasource and model definition with // the real functions, and then stubbing them is easier than // building the stubs and fakes by hand! - let datasource = new DataSourceConstructor({ + const datasource = new DataSourceConstructor({ name: 'ds', connector: 'memory', }); - let repository = new DefaultCrudRepository(Todo, datasource); + const repository = new DefaultCrudRepository(Todo, datasource); controller.repository = repository; describe('getTodo', () => { - let sandbox = sinon.sandbox.create(); + const sandbox = sinon.sandbox.create(); beforeEach(() => { sandbox.restore(); }); it('returns all todos when called without ID', async () => { - let stub = sandbox.stub(repository, 'find'); - let result = await controller.get(); + const stub = sandbox.stub(repository, 'find'); + const result = await controller.get(); expect.ok(stub.called, 'find was called'); expect.deepEqual(stub.getCall(0).args, [{}], 'args were correct'); }); it('returns the correct todo by ID', async () => { - let stub = sandbox.stub(repository, 'find'); - let result = await controller.getById(1); + const stub = sandbox.stub(repository, 'find'); + const result = await controller.getById(1); expect.ok(stub.called, 'find was called'); }); it('can filter by title', async () => { - let stub = sandbox.stub(repository, 'find'); - let result = await controller.get('test2'); + const stub = sandbox.stub(repository, 'find'); + const result = await controller.get('test2'); expect.ok(stub.called, 'find was called'); expect.deepEqual( stub.getCall(0).args, @@ -61,13 +61,13 @@ describe('TodoController', () => { }); describe('createTodo', () => { - let sandbox = sinon.sandbox.create(); + const sandbox = sinon.sandbox.create(); beforeEach(() => { sandbox.restore(); }); it('calls create on the repository', async () => { - let stub = sandbox.stub(repository, 'create'); - let result = await controller.create({ + const stub = sandbox.stub(repository, 'create'); + const result = await controller.create({ title: 'foo', body: 'bar', }); @@ -76,36 +76,36 @@ describe('TodoController', () => { }); describe('replaceTodo', () => { - let sandbox = sinon.sandbox.create(); + const sandbox = sinon.sandbox.create(); beforeEach(() => { sandbox.restore(); }); it('returns an affected item count of 1 on success', async () => { - let stub = sandbox.stub(controller.repository, 'replaceById'); - let replacement = new Todo(); + const stub = sandbox.stub(controller.repository, 'replaceById'); + const replacement = new Todo(); Object.assign(replacement, { id: 1, title: 'foo', body: 'bar', }); - let result = await controller.replace(1, replacement); + const result = await controller.replace(1, replacement); expect.ok(stub.called, 'replace was called'); }); }); describe('updateTodo', () => { - let sandbox = sinon.sandbox.create(); + const sandbox = sinon.sandbox.create(); beforeEach(() => { sandbox.restore(); }); it('returns the updated version of the object', async () => { - let stub = sandbox.stub(controller.repository, 'updateById'); - let replacement = { + const stub = sandbox.stub(controller.repository, 'updateById'); + const replacement = { id: 1, title: 'foo', }; - let expected = _.merge({id: 1}, replacement); - let result = await controller.update(1, replacement); + const expected = _.merge({id: 1}, replacement); + const result = await controller.update(1, replacement); expect.ok(stub.called, 'update was called'); }); // There's no unhappy path tests here for missing ID, because @@ -113,13 +113,13 @@ describe('TodoController', () => { }); describe('deleteTodo', () => { - let sandbox = sinon.sandbox.create(); + const sandbox = sinon.sandbox.create(); beforeEach(() => { sandbox.restore(); }); it('works on one item', async () => { - let stub = sandbox.stub(controller.repository, 'deleteById'); - let result = await controller.deleteById(1); + const stub = sandbox.stub(controller.repository, 'deleteById'); + const result = await controller.deleteById(1); expect.ok(stub.called, 'delete was called'); // The null filter is automatically replaced with an empty object in // controller layer! @@ -127,8 +127,8 @@ describe('TodoController', () => { }); it('can filter by title', async () => { - let stub = sandbox.stub(controller.repository, 'deleteAll'); - let result = await controller.delete('test2'); + const stub = sandbox.stub(controller.repository, 'deleteAll'); + const result = await controller.delete('test2'); expect.ok(stub.called, 'result exists'); expect.deepEqual( stub.getCall(0).args, diff --git a/packages/example-microservices/services/transaction/controllers/TransactionController.ts b/packages/example-microservices/services/transaction/controllers/TransactionController.ts index 529e68d16eb7..47eb8273f16e 100644 --- a/packages/example-microservices/services/transaction/controllers/TransactionController.ts +++ b/packages/example-microservices/services/transaction/controllers/TransactionController.ts @@ -3,10 +3,12 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import {api} from '@loopback/core'; +import {api} from '@loopback/rest'; import {def} from './TransactionController.api'; import {TransactionRepository} from '../repositories/transaction'; +// tslint:disable:no-any + @api(def) export class TransactionController { repository: TransactionRepository; diff --git a/packages/example-microservices/services/transaction/package.json b/packages/example-microservices/services/transaction/package.json index 6e947ded5265..804c3085edd6 100644 --- a/packages/example-microservices/services/transaction/package.json +++ b/packages/example-microservices/services/transaction/package.json @@ -9,9 +9,10 @@ "url": "https://github.com/strongloop/loopback-next.git" }, "dependencies": { - "loopback-datasource-juggler": "^3.4.1", + "@loopback/core": "^4.0.0-alpha.10", "@loopback/repository": "^4.0.0-alpha.5", - "@loopback/core": "^4.0.0-alpha.10" + "@loopback/rest": "^4.0.0-alpha.22", + "loopback-datasource-juggler": "^3.4.1" }, "devDependencies": { "@types/node": "^7.0.12" diff --git a/packages/example-microservices/services/transaction/repositories/transaction/index.ts b/packages/example-microservices/services/transaction/repositories/transaction/index.ts index 2937c40049df..05d2ab02d69a 100644 --- a/packages/example-microservices/services/transaction/repositories/transaction/index.ts +++ b/packages/example-microservices/services/transaction/repositories/transaction/index.ts @@ -6,6 +6,8 @@ import {juggler, DataSourceConstructor} from '@loopback/repository'; const modelDefinition = require('./models/transaction/model-definition.json'); +// tslint:disable:no-any + export class TransactionRepository { model; diff --git a/packages/repository/src/model.ts b/packages/repository/src/model.ts index 5fe1fa97fbf0..9014ce36e730 100644 --- a/packages/repository/src/model.ts +++ b/packages/repository/src/model.ts @@ -158,7 +158,7 @@ export abstract class Model { /** * Convert to a plain object as DTO */ - toObject(options?: Options): Object { + toObject(options?: Options): AnyObject { let obj: AnyObject; if (options && options.ignoreUnknownProperties === false) { obj = {}; From 7f408e1c6b385d3fef8391b0e2ebdac06a4754f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 30 Jan 2018 15:22:58 +0100 Subject: [PATCH 6/8] fixup! add CODEOWNERS --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index e8fb812a9df1..0dae87a13ef7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -12,6 +12,7 @@ packages/core/* @bajtos @raymondfeng @kjdelisle packages/example-getting-started/* @bajtos @kjdelisle packages/example-hello-world/* @b-admike packages/example-log-extension/* @virkt25 +packages/example-microservices/* @raymondfeng @virkt25 @kjdelisle packages/example-rpc-server/* @kjdelisle packages/metadata/* @raymondfeng packages/openapi-spec/* @bajtos @jannyHou From 3eeb6d3cfe91f2734e41e7fa7c724d5f9c0fe26e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 30 Jan 2018 15:24:35 +0100 Subject: [PATCH 7/8] fixup! add `lb4 example` entry, fix descriptions --- packages/cli/generators/example/index.js | 1 + packages/example-microservices/package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cli/generators/example/index.js b/packages/cli/generators/example/index.js index bf96bd6c1c67..31d8663b240c 100644 --- a/packages/cli/generators/example/index.js +++ b/packages/cli/generators/example/index.js @@ -16,6 +16,7 @@ const EXAMPLES = { 'An application and tutorial on how to build with LoopBack 4.', 'hello-world': 'A simple hello-world Application using LoopBack 4', 'log-extension': 'An example extension project for LoopBack 4', + 'microservices': 'How to build scalable microservices using LoopBack', 'rpc-server': 'A basic RPC server using a made-up protocol.', }; Object.freeze(EXAMPLES); diff --git a/packages/example-microservices/package.json b/packages/example-microservices/package.json index 1749fb948403..44cb6bb25c5e 100644 --- a/packages/example-microservices/package.json +++ b/packages/example-microservices/package.json @@ -1,7 +1,7 @@ { "name": "@loopback/example-microservices", "version": "4.0.0-alpha.0", - "description": "How to use LoopBack.next and some recommended best practices.", + "description": "How to build scalable microservices using LoopBack", "private": true, "main": "facade/index.js", "scripts": { From c500aaf8bb36d543669cbcc2c9881a8537d70e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 30 Jan 2018 16:03:05 +0100 Subject: [PATCH 8/8] fixup! fix build on windows, skip per-service npm install when running via Lerna --- packages/example-microservices/bin/install.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/example-microservices/bin/install.js b/packages/example-microservices/bin/install.js index ac4054174f88..707292b09e71 100755 --- a/packages/example-microservices/bin/install.js +++ b/packages/example-microservices/bin/install.js @@ -13,6 +13,13 @@ const spawn = require('child_process').spawn; const servicesRoot = path.resolve(__dirname, '..', 'services'); const services = fs.readdirSync(servicesRoot); +if (process.env.LERNA_ROOT_PATH) { + console.log( + '**Lerna was detected, skipping "npm install" in individual services**' + ); + process.exit(0); +} + let p = Promise.resolve(); for (const s of services) { p = p.then(() => { @@ -31,6 +38,9 @@ function execNpmInstall(cwd) { const child = spawn('npm', ['install'], { cwd: cwd, stdio: 'inherit', + // On Windows, `npm` is not an executable filea + // we have to execute it via shell + shell: true, }); child.once('error', err => reject(err)); child.once('exit', (code, signal) => {