diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..1f5462b --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,49 @@ +{ + "rules": { + "indent": [ + 2, + 4 + ], + "no-console" : [ + 0 + ], + "quotes": [ + 2, + "single", + "avoid-escape" + ], + "linebreak-style": [ + 2, + "unix" + ], + "semi": [ + 2, + "always" + ], + "curly": 2, + "eqeqeq": 2, + "no-extend-native": 2, + "max-depth": [ + 2, + 4 + ], + "no-new": 2, + "strict": [ + 2, + "global" + ], + "no-trailing-spaces": 2, + "space-in-parens": 2, + "space-infix-ops": 2, + "keyword-spacing": 2, + "spaced-comment": 2, + "camelcase": 2 + }, + "env": { + "node": true, + "es6": true + }, + "extends": "eslint:recommended" +} + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5148e52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history diff --git a/.pipeline/config.yml b/.pipeline/config.yml new file mode 100644 index 0000000..8aab12c --- /dev/null +++ b/.pipeline/config.yml @@ -0,0 +1,11 @@ + general: +steps: + mtaBuild: + buildTarget: 'CF' + cloudFoundryDeploy: + deployTool: 'mtaDeployPlugin' + deployType: 'standard' + cloudFoundry: + org: 'P1942815479trial_trial' + space: 'dev' + credentialsId: 'Github'' diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..56e144e --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,13 @@ +@Library('piper-lib-os') _ +node() { + stage('prepare') { + checkout scm + setupCommonPipelineEnvironment script:this + } + stage('build') { + mtaBuild script: this +} + stage('deploy') { + cloudFoundryDeploy script: this +} +} diff --git a/README.md b/README.md index 3c5448f..dcd1523 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,59 @@ -## Description -This is a "Hello World" application for the SAP Cloud Platform Cloud Foundry Environment that spans over several chapters (branches). You will learn:   -- How to develop a simple RESTful API in Node.js -- How to persist data in a PostgreSQL DB -- How to perform authentication and authorization -- How to develop a UI served by the API +# Chapter 1: Basic Implementation of a Node.js Web Service + +## Learning Goal +Having finished this chapter, you should be able to run a small Node.js Web service on your local machine and you'll be able to deploy it to SAP Cloud Platform Cloud Foundry Environment. + ## Prerequisites -To make this sample application work for you, please make sure you have: -- A basic knowledge of Node.js -- An account on SAP Cloud Platform, for example, a [trial account](https://account.hanatrial.ondemand.com/) you may [sign up](https://account.hanatrial.ondemand.com/register) for. For more information about the Sign up process, see this [blog](https://blogs.sap.com/2017/05/16/sap-cloud-platform-trial-now-includes-cloud-foundry/). -## Download and Installation -Please clone this project locally. Then check out the branches one by one in sequence, and follow the instructions from the respective README document. -## Support -This project is 'as-is'. We do not provide support and will not make changes. You are welcome to make changes to improve the project but we are not available for questions or support of any kind. -## License -Copyright (c) 2017 SAP SE or an SAP affiliate company. All rights reserved. -This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the [LICENSE](LICENSE) file. +- You [signed up](https://account.hanatrial.ondemand.com/register) for a [trial account](https://account.hanatrial.ondemand.com/) in SAP Cloud Platform, or you have a productive account. +- Once you got your account, you performed the steps described in [Getting Started with Cloud Foundry](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/b8ee7894fe0b4df5b78f61dd1ac178ee.html). +- You have basic knowledge about [Node.js: Development](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/3a7a0bece0d044eca59495965d8a0237.html), for example, [Create a Node.js Application](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/772b45ce6c46492b908d4c985add932a.html) and [SAP NPM Registry](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/fe672690385a4541b45622a9088f4503.html). Please make sure that you bookmark these pages: Later [SAP Node.js Packages](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/92f1bce8f72946c180d198e21f74a68c.html) and [Secure Node.js Applications](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/3a8e4372f8e74d05b4ed03a484865e08.html) are also relevant. +- For more information about working with NPM registry, see this [blog](https://blogs.sap.com/2017/05/16/sap-npm-registry-launched-making-the-lives-of-node.js-developers-easier/). + + +## Step 1: Configure NPM on your machine to ensure all subsequent NPM calls work. +Execute the following command: +``` +npm config set @sap:registry https://npm.sap.com +``` + +## Step 2: Run the service locally. +- Clone this repo to your machine.   +- In the folder you cloned into, execute the `npm install` command. +- To start the server, execute the `nodejs server.js` command. +- To get all users or the details of one user, browse `http://:8088/users` or `http://:8088/users/2`. +- To add another entry to the list of users using the POST operation use, for example, the `Postman` extension of Chrome. (PUT and DELETE are not yet implemented). To test these operations, import the file `SAP-CP-CF_Hello_World.postman_collection.json` from this repository into `Postman`.   + + +## Step 3: Push to Cloud and run the service. +To log on, access your endpoint with the following command: +``` +cf api https://api.cf.eu10.hana.ondemand.com +``` +or: +``` +cf api https://api.cf.us10.hana.ondemand.com +``` +(depending upon the landscape your account was created in) +To log on use the following command: +``` +cf login +``` +If you have access to more than 1 org or space, execute the following command: +``` +cf target -o ORG -s SPACE +``` +To deploy the application to SAP Cloud Platform Cloud Foundry Environment, execute the following command: +``` +cf push --random-route +``` +For more information on these commands, see [Getting Started with Cloud Foundry](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/b8ee7894fe0b4df5b78f61dd1ac178ee.html) and [Deploy an Application](http://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html). + + +Check the output of this command, and write down the URL created for the application. +As a result you should be able to browse `https:///users`. +If you want to use the `Postman` collection above, please adjust the URL for the requests in the Cloud folder to the allocated ``. + + + diff --git a/SAP-CP-CF_Hello_World.postman_collection.json b/SAP-CP-CF_Hello_World.postman_collection.json new file mode 100644 index 0000000..e46e026 --- /dev/null +++ b/SAP-CP-CF_Hello_World.postman_collection.json @@ -0,0 +1,97 @@ +{ + "variables": [], + "info": { + "name": "SAP-CP-CF_Hello_World", + "_postman_id": "0f295c39-5626-6ceb-6174-5946f395683f", + "description": "", + "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json" + }, + "item": [ + { + "name": "localhost", + "description": "", + "item": [ + { + "name": "GET_Users", + "request": { + "url": "http://localhost:8088/users", + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + } + ], + "body": {}, + "description": "" + }, + "response": [] + }, + { + "name": "Create_POST_User", + "request": { + "url": "http://localhost:8088/users", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Donald\",\n \"color\": \"blue\",\n \"material\": \"cotton\"\n}\n" + }, + "description": "" + }, + "response": [] + } + ] + }, + { + "name": "Cloud", + "description": "", + "item": [ + { + "name": "GET_Users", + "request": { + "url": "https:///users", + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + } + ], + "body": {}, + "description": "" + }, + "response": [] + }, + { + "name": "Create_POST_User", + "request": { + "url": "https:///users", + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "description": "" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Donald\",\n \"color\": \"blue\",\n \"material\": \"cotton\"\n}\n" + }, + "description": "" + }, + "response": [] + } + ] + } + ] +} diff --git a/manifest.yml b/manifest.yml new file mode 100644 index 0000000..4976c93 --- /dev/null +++ b/manifest.yml @@ -0,0 +1,6 @@ +--- +applications: +- name: sapcpcfhw + path: . + buildpack: nodejs_buildpack + memory: 128M diff --git a/mta.yaml b/mta.yaml new file mode 100644 index 0000000..8dbf174 --- /dev/null +++ b/mta.yaml @@ -0,0 +1,10 @@ +_schema-version: 2.1.0 +ID: com.sap.piper.node.hello.world +version: 1.0.0 +description: A Hello World sample application +provider: SAP Sample generator + +modules: + - name: piper.node.hello.world + type: nodejs + path: . diff --git a/package.json b/package.json new file mode 100644 index 0000000..f8809f9 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "SAP-CP-CF-HelloWorld", + "description": "Sample SAP-CP-CF Hello World Restful Service", + "version": "0.0.1", + "private": true, + "dependencies": { + "express": "4.15.3", + "body-parser": "1.17.2" + }, + "engines": { + "node": "6.x.x" + }, + "scripts": { + "start": "node server.js" + } +} \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 0000000..8bc9a4e --- /dev/null +++ b/server.js @@ -0,0 +1,57 @@ +'use strict'; +const express = require('express'); +const bodyParser = require('body-parser'); + +function log(logTxt) { + console.log(logTxt); +} + +const app = express(); +const url = '/users'; +const users = require('./users.json'); + +app.use(bodyParser.json()); + +app.get(url, function (req, res) { + log('Get All Request'); + res.status(200).json(users); +}); + +app.get(url + '/:id', function (req, res) { + // First read existing users. + const userId = parseInt(req.params.id); + log('Get User Request for id ' + userId); + + const foundUser = users.find(function(user) { + return user.id === userId; + }); + if (!foundUser) { + res.status(404).end(); + return; + } + + res.status(200).json(foundUser); +}); + +app.post(url, function (req, res) { + + var newUser = req.body; + + newUser.id = users.length; + users.push(newUser); + + log('User added ' + newUser + ' with id: ' + newUser.id); + + res.status(201).json(newUser); +}); + +const PORT = process.env.PORT || 8088; + +var server = app.listen(PORT, function () { + + const host = server.address().address; + const port = server.address().port; + + log('Example app listening at http://' + host + ':' + port); + +}); diff --git a/users.json b/users.json new file mode 100644 index 0000000..b7cd514 --- /dev/null +++ b/users.json @@ -0,0 +1,20 @@ +[ + { + "name": "SpongeBob", + "color": "yellow", + "material": "sponge", + "id": 0 + }, + { + "name": "Shaun", + "color": "white", + "material": "wool", + "id": 1 + }, + { + "name": "Kermitt", + "color": "green", + "material": "polyester", + "id": 2 + } +] \ No newline at end of file