From c21fb608c4a83cb84281fcbc72fa596b5dd3d20a Mon Sep 17 00:00:00 2001 From: Hage Yaapa Date: Mon, 30 Jul 2018 18:18:08 +0530 Subject: [PATCH] chore: todo example on IBM Cloud add todo example on IBM Cloud --- examples/todo/.cfignore | 4 + examples/todo/DEPLOYING.md | 170 ++++++++++++++++++ examples/todo/manifest.yml | 10 ++ examples/todo/package.json | 2 +- .../todo/src/datasources/db.datasource.json | 8 +- .../todo/src/datasources/db.datasource.ts | 43 +++++ examples/todo/src/index.ts | 8 + 7 files changed, 241 insertions(+), 4 deletions(-) create mode 100644 examples/todo/.cfignore create mode 100644 examples/todo/DEPLOYING.md create mode 100644 examples/todo/manifest.yml diff --git a/examples/todo/.cfignore b/examples/todo/.cfignore new file mode 100644 index 000000000000..137db8f965e0 --- /dev/null +++ b/examples/todo/.cfignore @@ -0,0 +1,4 @@ +.git/ +node_modules/ +vcap-local.js +.vscode/ diff --git a/examples/todo/DEPLOYING.md b/examples/todo/DEPLOYING.md new file mode 100644 index 000000000000..baebf529a868 --- /dev/null +++ b/examples/todo/DEPLOYING.md @@ -0,0 +1,170 @@ +# How to deploy the app locally and on IBM Cloud + +This is the guide for deploying the Todo app locally and on IBM Cloud. + +## Database setup + +The app, when running locally, connects to a local instance of Cloudant; when +deployed to IBM Cloud, it connects to a provisioned Cloudant instance. + +### Local + +Follow the steps: + +1. [Install Docker](https://www.docker.com/). +2. [Download Cloudant image](https://hub.docker.com/r/ibmcom/cloudant-developer/). +3. Ensure port `8080` is free and start Cloudant: + +``` +docker run \ + --detach \ + --volume cloudant:/srv \ + --name cloudant-developer \ + --publish 8080:80 \ + --hostname cloudant.dev \ + ibmcom/cloudant-developer +``` + +4. Log on to the local [Cloudant Dashboard](http://localhost:8080/dashboard.html) using `admin:pass` as the crendentials. +5. Create a database named `todo`. +6. Add a new document: + +``` +{ + "title": "I AM A LOCAL", + "desc": "I call 127.0.0.1 my home", + "loopback__model__name": "Todo" +} +``` + +7. Set the `url` property of `db.datasource.json` to `http://admin:pass@localhost:8080`, which is where we can access the locally running Cloudant instance. + +### IBM Cloud + +Follow the steps: + +1. Log on to IBM Cloud. +2. Create a [Node.js SDK app](https://console.bluemix.net/catalog/starters/sdk-for-nodejs). +3. Provision a [Cloudant service](https://console.bluemix.net/catalog/services/cloudant). Give this a name, which is the same as the name of the datasource configured in `db.datasource.json`. This is to indicate that this service is the corresponding datasource on IBM Cloud. +4. Log on to the IBM Cloud Cloudant Dashboard. +5. Create a database named `todo`. +6. Add a new document: + +``` +{ + "title": "I AM ON IBM CLOUD", + "desc": "Yes, I am!", + "loopback__model__name": "Todo" +} +``` + +7. Add a new field in `db.datasource.json` named `ibmCloud`, and set it to `true`. This is to indicate that there is a corresponding provisioned service on IBM Cloud, which should be used instead of the locally configured datasource in the `url` property, when the app is runnong on IBM Cloud. +8. Connect the Cloudant service to the Node.js SDK app created in step 2. + +## Deployment + +### Local + +1. Add `loopback-cloudant-connector` to dependecies in `package.json`. +2. Install all dependencies: + +``` +npm i +``` + +3. Build the app: + +``` +npm run build +``` + +This is a must, if switched from a another branch. + +4. Ensure Cloudant is running locally. +5. Start the app: + +``` +npm start +``` + +6. Load `http://localhost:3000/todos` to see: + +``` +[{"id":null,"title":"I AM A LOCAL","desc":"I call 127.0.0.1 my home"}] +``` + +### IBM Cloud + +1. + +1. Add a `.cfignore` file with the following content: + +``` +.git/ +node_modules/ +vcap-local.js +.vscode/ +``` + +This is to avoid uploading the listed files and directories to IBM Cloud. + +2. Add a `manifest.yml` file with the details of the app: + +``` +--- +applications: +- instances: 1 + timeout: 256 + name: LB4-0001 + buildpack: sdk-for-nodejs + command: npm start + memory: 128M + domain: eu-gb.mybluemix.net + host: lb4-0001 +``` + +`LB4-0001` is the name of the app that was created from the example. + +3. Remove the `prestart` script from `package.json`, since we don't want to build + the app on Cloud. + +4) Build the app locally: + +``` +npm run build +``` + +This is a must, if switched from a another branch. + +5. Push the app to IBM Cloud: + +``` +cf push lb4-0001 +``` + +6. Load https://lb4-0001.eu-gb.mybluemix.net/todos to see: + +``` +[{"id":null,"title":"I AM ON IBM CLOUD","desc":"Yes, I am!"}] +``` + +## Pain points + +Some pain points from the overall experience. + +### IBM Cloud + +1. Service-connecting service (the service which binds Node.js app to Cloudant) + was down for some time. +2. Could not see the created app for some time because of UI bug(s). +3. The button for downloading the scaffolded app is not there anymore. No easy + way to get the code that was generated. This will greatly help in understanding + the gnerated IBM Cloud artifacts and the specifics for Node.js apps. I used the + sample app I created with Nana Amfo a month or so ago. + +### LoopBack + +1. Model configuration change is drastic. +2. Repository creation and linking to a model is manual. There should be a + provision to do these from the `lb` commands. +3. There no API Explorer to play around with the models. diff --git a/examples/todo/manifest.yml b/examples/todo/manifest.yml new file mode 100644 index 000000000000..cc08adcaed5a --- /dev/null +++ b/examples/todo/manifest.yml @@ -0,0 +1,10 @@ +--- +applications: +- instances: 1 + timeout: 256 + name: LB4-0001 + buildpack: sdk-for-nodejs + command: npm start + memory: 128M + domain: eu-gb.mybluemix.net + host: lb4-0001 diff --git a/examples/todo/package.json b/examples/todo/package.json index d43a7f9b1fb1..df8462ce4a55 100644 --- a/examples/todo/package.json +++ b/examples/todo/package.json @@ -25,7 +25,6 @@ "test": "lb-mocha \"DIST/test/*/**/*.js\"", "test:dev": "lb-mocha --allow-console-logs DIST/test/**/*.js && npm run posttest", "verify": "npm pack && tar xf loopback-todo*.tgz && tree package && npm run clean", - "prestart": "npm run build", "start": "node ." }, "repository": { @@ -46,6 +45,7 @@ "@loopback/repository": "^0.14.2", "@loopback/rest": "^0.19.2", "@loopback/service-proxy": "^0.6.2", + "loopback-connector-cloudant": "^1.3.0", "loopback-connector-rest": "^3.1.1" }, "devDependencies": { diff --git a/examples/todo/src/datasources/db.datasource.json b/examples/todo/src/datasources/db.datasource.json index a68f220be986..9cf9a97283d5 100644 --- a/examples/todo/src/datasources/db.datasource.json +++ b/examples/todo/src/datasources/db.datasource.json @@ -1,6 +1,8 @@ { "name": "db", - "connector": "memory", - "localStorage": "", - "file": "./data/db.json" + "connector": "cloudant", + "url": "http://admin:pass@localhost:8080", + "database": "todo", + "modelIndex": "", + "ibmCloud": true } diff --git a/examples/todo/src/datasources/db.datasource.ts b/examples/todo/src/datasources/db.datasource.ts index 2e5d07a46140..87971e4f89df 100644 --- a/examples/todo/src/datasources/db.datasource.ts +++ b/examples/todo/src/datasources/db.datasource.ts @@ -7,6 +7,31 @@ import {inject} from '@loopback/core'; import {juggler, DataSource} from '@loopback/repository'; const config = require('./db.datasource.json'); +type VcapService = { + name: string; + credentials: { + // tslint:disable-next-line:no-any + [property: string]: any; + }; +}; + +if (shouldConfigureForIbmCloud()) { + const services = JSON.parse(process.env.VCAP_SERVICES!); + Object.keys(services).forEach(key => { + const serviceGroup = services[key]; + + // tslint:disable-next-line:no-any + serviceGroup.forEach((service: VcapService) => { + if (service.name === config.name) { + config.url = service.credentials.url; + } + }); + }); +} + +// cf does not show .log() reliably, so have to use .warn() +console.warn(config); + export class DbDataSource extends juggler.DataSource { static dataSourceName = 'db'; @@ -14,6 +39,24 @@ export class DbDataSource extends juggler.DataSource { @inject('datasources.config.db', {optional: true}) dsConfig: DataSource = config, ) { + dsConfig = Object.assign({}, dsConfig, { + // A workaround for the current design flaw where inside our monorepo, + // packages/service-proxy/node_modules/loopback-datasource-juggler cannot + // see/load the connector from examples/soap/node_modules/loopback-connector-soap + // as explained in todo example + connector: require('loopback-connector-cloudant'), + }); super(dsConfig); } } + +function shouldConfigureForIbmCloud() { + if ( + process.env.HOME === '/home/vcap/app' && // relatively reliable way to detect the app is running on IBM Cloud + config.ibmCloud && // have to explicitly config datasource to mark it as an IBM Cloud service with a matching name locally + process.env.VCAP_SERVICES // service should have been provisioned and bound to the app + ) { + return true; + } + return false; +} diff --git a/examples/todo/src/index.ts b/examples/todo/src/index.ts index cbc8c8c974e6..3d309ddbcd0c 100644 --- a/examples/todo/src/index.ts +++ b/examples/todo/src/index.ts @@ -7,7 +7,15 @@ import {TodoListApplication} from './application'; import {ApplicationConfig} from '@loopback/core'; export async function main(options?: ApplicationConfig) { + // IBM Cloud app port is set on process.env.PORT + if (!options) { + options = {rest: {port: process.env.PORT}}; + } else { + if (!options.rest) options.rest = {}; + options.rest.port = process.env.PORT || options.rest.port; + } const app = new TodoListApplication(options); + await app.boot(); await app.start();