Skip to content

Commit

Permalink
chore: todo example on IBM Cloud
Browse files Browse the repository at this point in the history
add todo example on IBM Cloud
  • Loading branch information
Hage Yaapa committed Aug 14, 2018
1 parent be81131 commit c21fb60
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 4 deletions.
4 changes: 4 additions & 0 deletions examples/todo/.cfignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.git/
node_modules/
vcap-local.js
.vscode/
170 changes: 170 additions & 0 deletions examples/todo/DEPLOYING.md
Original file line number Diff line number Diff line change
@@ -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.
10 changes: 10 additions & 0 deletions examples/todo/manifest.yml
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion examples/todo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand All @@ -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": {
Expand Down
8 changes: 5 additions & 3 deletions examples/todo/src/datasources/db.datasource.json
Original file line number Diff line number Diff line change
@@ -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
}
43 changes: 43 additions & 0 deletions examples/todo/src/datasources/db.datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,56 @@ 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';

constructor(
@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;
}
8 changes: 8 additions & 0 deletions examples/todo/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down

0 comments on commit c21fb60

Please sign in to comment.