Adding books microservice for GE 8.6
This commit is contained in:
parent
1deac4dc9b
commit
f3b01722a2
17 changed files with 4273 additions and 0 deletions
34
books/.eslintrc
Normal file
34
books/.eslintrc
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"extends": "airbnb-base",
|
||||
"parserOptions": {
|
||||
"sourceType": "script",
|
||||
},
|
||||
"rules": {
|
||||
"strict": 0,
|
||||
"no-param-reassign": [
|
||||
"error",
|
||||
{ "props": false }
|
||||
],
|
||||
"func-names": 0,
|
||||
"comma-dangle": 0,
|
||||
"prefer-template": 0,
|
||||
"camelcase": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"consistent-return": 0,
|
||||
"no-bitwise": ["error", { "allow": ["~"] }]
|
||||
},
|
||||
"globals": {
|
||||
"describe": true,
|
||||
"it": true,
|
||||
"after": true,
|
||||
"afterEach": true,
|
||||
"before": true,
|
||||
"beforeEach": true,
|
||||
"should": true,
|
||||
"reqServer": true
|
||||
},
|
||||
"env": {
|
||||
"node": true,
|
||||
"mocha": true
|
||||
}
|
||||
}
|
||||
115
books/.gitignore
vendored
Normal file
115
books/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
29
books/data/authors.js
Normal file
29
books/data/authors.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
const authors = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'James Joyce',
|
||||
dob: 1882
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'F Scott Fitzgerald',
|
||||
dob: 1896
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Aldous Huxley',
|
||||
dob: 1894
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Vladimir Nabokov',
|
||||
dob: 1899
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'William Faulkner',
|
||||
dob: 1897
|
||||
}
|
||||
];
|
||||
|
||||
module.exports.books = authors;
|
||||
39
books/data/books.js
Normal file
39
books/data/books.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
const books = [
|
||||
{
|
||||
ranking: 1,
|
||||
name: 'ULYSSES',
|
||||
Author: 'James Joyce',
|
||||
country: 'Ireland',
|
||||
yearPublished: '1922'
|
||||
},
|
||||
{
|
||||
ranking: 2,
|
||||
name: 'THE GREAT GATSBY',
|
||||
Author: 'F. Scott Fitzgerald',
|
||||
country: 'USA',
|
||||
yearPublished: '1925'
|
||||
},
|
||||
{
|
||||
ranking: 3,
|
||||
name: 'A PORTRAIT OF THE ARTIST AS A YOUNG MAN',
|
||||
Author: 'James Joyce',
|
||||
country: 'Ireland',
|
||||
yearPublished: '1916'
|
||||
},
|
||||
{
|
||||
ranking: 4,
|
||||
name: 'BRAVE NEW WORLD',
|
||||
Author: 'Aldous Huxley',
|
||||
country: 'UK',
|
||||
yearPublished: '1932'
|
||||
},
|
||||
{
|
||||
ranking: 5,
|
||||
name: 'THE SOUND AND THE FURY',
|
||||
Author: 'William Faulkner',
|
||||
country: 'USA',
|
||||
yearPublished: '1929'
|
||||
}
|
||||
];
|
||||
|
||||
module.exports.books = books;
|
||||
90
books/exec/www
Executable file
90
books/exec/www
Executable file
|
|
@ -0,0 +1,90 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var app = require('../server');
|
||||
var debug = require('debug')('movies:server');
|
||||
var http = require('http');
|
||||
|
||||
/**
|
||||
* Get port from environment and store in Express.
|
||||
*/
|
||||
|
||||
var port = normalizePort(process.env.PORT || '8080');
|
||||
app.set('port', port);
|
||||
|
||||
/**
|
||||
* Create HTTP server.
|
||||
*/
|
||||
|
||||
var server = http.createServer(app);
|
||||
|
||||
/**
|
||||
* Listen on provided port, on all network interfaces.
|
||||
*/
|
||||
|
||||
server.listen(port);
|
||||
server.on('error', onError);
|
||||
server.on('listening', onListening);
|
||||
|
||||
/**
|
||||
* Normalize a port into a number, string, or false.
|
||||
*/
|
||||
|
||||
function normalizePort(val) {
|
||||
var port = parseInt(val, 10);
|
||||
|
||||
if (isNaN(port)) {
|
||||
// named pipe
|
||||
return val;
|
||||
}
|
||||
|
||||
if (port >= 0) {
|
||||
// port number
|
||||
return port;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener for HTTP server "error" event.
|
||||
*/
|
||||
|
||||
function onError(error) {
|
||||
if (error.syscall !== 'listen') {
|
||||
throw error;
|
||||
}
|
||||
|
||||
var bind = typeof port === 'string'
|
||||
? 'Pipe ' + port
|
||||
: 'Port ' + port;
|
||||
|
||||
// handle specific listen errors with friendly messages
|
||||
switch (error.code) {
|
||||
case 'EACCES':
|
||||
console.error(bind + ' requires elevated privileges');
|
||||
process.exit(1);
|
||||
break;
|
||||
case 'EADDRINUSE':
|
||||
console.error(bind + ' is already in use');
|
||||
process.exit(1);
|
||||
break;
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener for HTTP server "listening" event.
|
||||
*/
|
||||
|
||||
function onListening() {
|
||||
var addr = server.address();
|
||||
var bind = typeof addr === 'string'
|
||||
? 'pipe ' + addr
|
||||
: 'port ' + addr.port;
|
||||
debug('Listening on ' + bind);
|
||||
}
|
||||
152
books/jenkins/Jenkinsfile
vendored
Normal file
152
books/jenkins/Jenkinsfile
vendored
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
pipeline {
|
||||
options {
|
||||
// set a timeout of 30 minutes for this pipeline
|
||||
timeout(time: 30, unit: 'MINUTES')
|
||||
}
|
||||
agent {
|
||||
node {
|
||||
label 'nodejs'
|
||||
}
|
||||
}
|
||||
|
||||
environment {
|
||||
DEV_PROJECT = "youruser-books-dev"
|
||||
STAGE_PROJECT = "youruser-books-stage"
|
||||
APP_GIT_URL = "https://github.com/youruser/DO288-apps"
|
||||
NEXUS_SERVER = "http://nexus-common.apps.cluster.domain.example.com/repository/nodejs"
|
||||
|
||||
// DO NOT CHANGE THE GLOBAL VARS BELOW THIS LINE
|
||||
APP_NAME = "books"
|
||||
}
|
||||
|
||||
|
||||
stages {
|
||||
|
||||
stage('NPM Install') {
|
||||
steps {
|
||||
echo '### Installing NPM dependencies ###'
|
||||
sh '''
|
||||
npm config set registry ${NEXUS_SERVER}
|
||||
cd books
|
||||
npm install
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Run Unit Tests') {
|
||||
steps {
|
||||
echo '### Running unit tests ###'
|
||||
sh 'cd books; npm test'
|
||||
junit 'books/reports/server/mocha/test-results.xml'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Run Linting Tools') {
|
||||
steps {
|
||||
echo '### Running eslint on code ###'
|
||||
sh 'cd books; npm run lint'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Launch new app in DEV env') {
|
||||
steps {
|
||||
echo '### Cleaning existing resources in DEV env ###'
|
||||
sh '''
|
||||
oc project ${DEV_PROJECT}
|
||||
oc delete all -l app=${APP_NAME}
|
||||
sleep 5
|
||||
'''
|
||||
|
||||
echo '### Creating a new app in DEV env ###'
|
||||
sh '''
|
||||
oc project ${DEV_PROJECT}
|
||||
oc new-app --name books nodejs:8~${APP_GIT_URL} --build-env npm_config_registry=${NEXUS_SERVER} --context-dir ${APP_NAME}
|
||||
oc expose svc/${APP_NAME}
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Wait for S2I build to complete') {
|
||||
steps {
|
||||
script {
|
||||
openshift.withCluster() {
|
||||
openshift.withProject( "${DEV_PROJECT}" ) {
|
||||
def bc = openshift.selector("bc", "${APP_NAME}")
|
||||
bc.logs('-f')
|
||||
def builds = bc.related('builds')
|
||||
builds.untilEach(1) {
|
||||
return (it.object().status.phase == "Complete")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Wait for deployment in DEV env') {
|
||||
steps {
|
||||
script {
|
||||
openshift.withCluster() {
|
||||
openshift.withProject( "${DEV_PROJECT}" ) {
|
||||
def deployment = openshift.selector("dc", "${APP_NAME}").rollout()
|
||||
openshift.selector("dc", "${APP_NAME}").related('pods').untilEach(1) {
|
||||
return (it.object().status.phase == "Running")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Promote to Staging Env') {
|
||||
steps {
|
||||
timeout(time: 60, unit: 'MINUTES') {
|
||||
input message: "Promote to Staging?"
|
||||
}
|
||||
script {
|
||||
openshift.withCluster() {
|
||||
openshift.tag("${DEV_PROJECT}/books:latest", "${STAGE_PROJECT}/books:stage")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Deploy to Staging Env') {
|
||||
steps {
|
||||
echo '### Cleaning existing resources in Staging ###'
|
||||
sh '''
|
||||
oc project ${STAGE_PROJECT}
|
||||
oc delete all -l app=${APP_NAME}
|
||||
sleep 5
|
||||
'''
|
||||
|
||||
echo '### Creating a new app in Staging ###'
|
||||
sh '''
|
||||
oc project ${STAGE_PROJECT}
|
||||
oc new-app --name books -i books:stage
|
||||
oc expose svc/${APP_NAME}
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
stage('Wait for deployment in Staging') {
|
||||
steps {
|
||||
sh "oc get route ${APP_NAME} -n ${STAGE_PROJECT} -o jsonpath='{ .spec.host }' --loglevel=4 > routehost"
|
||||
|
||||
script {
|
||||
routeHost = readFile('routehost').trim()
|
||||
|
||||
openshift.withCluster() {
|
||||
openshift.withProject( "${STAGE_PROJECT}" ) {
|
||||
def deployment = openshift.selector("dc", "${APP_NAME}").rollout()
|
||||
openshift.selector("dc", "${APP_NAME}").related('pods').untilEach(1) {
|
||||
return (it.object().status.phase == "Running")
|
||||
}
|
||||
}
|
||||
echo "Deployment to Staging env is complete. Access the app at the URL http://${routeHost}."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3637
books/package-lock.json
generated
Normal file
3637
books/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
30
books/package.json
Normal file
30
books/package.json
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"name": "books",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node ./exec/www",
|
||||
"test": "export MOCHA_FILE='reports/server/mocha/test-results.xml' && IP=0.0.0.0 PORT=3030 node_modules/.bin/nyc --reporter=text node_modules/.bin/mocha tests/*_test.js -R mocha-junit-reporter",
|
||||
"lint": "node_modules/.bin/eslint . --ext .js"
|
||||
},
|
||||
"dependencies": {
|
||||
"cookie-parser": "~1.4.4",
|
||||
"debug": "~2.6.9",
|
||||
"express": "~4.16.1",
|
||||
"express-prettify": "^0.1.1",
|
||||
"hbs": "~4.0.4",
|
||||
"http-errors": "~1.6.3",
|
||||
"morgan": "~1.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"chai-http": "^4.3.0",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-airbnb-base": "^13.1.0",
|
||||
"eslint-plugin-import": "^2.18.0",
|
||||
"mocha": "^6.1.4",
|
||||
"mocha-junit-reporter": "^1.23.0",
|
||||
"nyc": "^14.1.1",
|
||||
"request": "^2.88.0"
|
||||
}
|
||||
}
|
||||
8
books/public/stylesheets/style.css
Normal file
8
books/public/stylesheets/style.css
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
body {
|
||||
padding: 50px;
|
||||
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #00B7FF;
|
||||
}
|
||||
13
books/routes/authors.js
Normal file
13
books/routes/authors.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
const express = require('express');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const authors = require('../data/authors');
|
||||
|
||||
|
||||
/* GET users listing. */
|
||||
router.get('/', (req, res) => {
|
||||
res.json(authors);
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
12
books/routes/books.js
Normal file
12
books/routes/books.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
const express = require('express');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const books = require('../data/books');
|
||||
|
||||
/* GET users listing. */
|
||||
router.get('/', (req, res) => {
|
||||
res.json(books);
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
10
books/routes/index.js
Normal file
10
books/routes/index.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
const express = require('express');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
/* GET home page. */
|
||||
router.get('/', (req, res) => {
|
||||
res.render('index', { title: 'The Book List' });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
45
books/server.js
Normal file
45
books/server.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
const createError = require('http-errors');
|
||||
const express = require('express');
|
||||
const pretty = require('express-prettify');
|
||||
const path = require('path');
|
||||
const cookieParser = require('cookie-parser');
|
||||
const logger = require('morgan');
|
||||
|
||||
const indexRouter = require('./routes/index');
|
||||
const booksRouter = require('./routes/books');
|
||||
const authorsRouter = require('./routes/authors');
|
||||
|
||||
const app = express();
|
||||
|
||||
// view engine setup
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
app.set('view engine', 'hbs');
|
||||
|
||||
app.use(logger('dev'));
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: false }));
|
||||
app.use(cookieParser());
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
app.use(pretty({ query: 'pretty' }));
|
||||
|
||||
app.use('/', indexRouter);
|
||||
app.use('/books', booksRouter);
|
||||
app.use('/authors', authorsRouter);
|
||||
|
||||
// catch 404 and forward to error handler
|
||||
app.use((req, res, next) => {
|
||||
next(createError(404));
|
||||
});
|
||||
|
||||
// error handler
|
||||
app.use((err, req, res) => {
|
||||
// set locals, only providing error in development
|
||||
res.locals.message = err.message;
|
||||
res.locals.error = req.app.get('env') === 'development' ? err : {};
|
||||
|
||||
// render the error page
|
||||
res.status(err.status || 500);
|
||||
res.render('error');
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
41
books/tests/app_test.js
Normal file
41
books/tests/app_test.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
const chai = require('chai');
|
||||
const chaiHTTP = require('chai-http');
|
||||
const server = require('../server');
|
||||
|
||||
const { expect } = chai;
|
||||
|
||||
chai.use(chaiHTTP);
|
||||
|
||||
reqServer = process.env.HTTP_TEST_SERVER || server;
|
||||
|
||||
describe('Books App routes test', () => {
|
||||
it('GET to / should return 200', (done) => {
|
||||
chai.request(reqServer)
|
||||
.get('/')
|
||||
.end((err, res) => {
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.text).to.include('Welcome');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET to /books should return 200', (done) => {
|
||||
chai.request(reqServer)
|
||||
.get('/books')
|
||||
.end((err, res) => {
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.text).to.include('ULYSSES');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('GET to /authors should return 200', (done) => {
|
||||
chai.request(reqServer)
|
||||
.get('/authors')
|
||||
.end((err, res) => {
|
||||
expect(res).to.have.status(200);
|
||||
expect(res.text).to.include('James Joyce');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
3
books/views/error.hbs
Normal file
3
books/views/error.hbs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<h1>{{message}}</h1>
|
||||
<h2>{{error.status}}</h2>
|
||||
<pre>{{error.stack}}</pre>
|
||||
5
books/views/index.hbs
Normal file
5
books/views/index.hbs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<h1>{{title}}</h1>
|
||||
<p>Welcome to {{title}}</p>
|
||||
|
||||
<p>The List of books is <a href="/books">here</a>.</p>
|
||||
<p>The List of authors is <a href="/authors">here</a>.</p>
|
||||
10
books/views/layout.hbs
Normal file
10
books/views/layout.hbs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{title}}</title>
|
||||
<link rel='stylesheet' href='/stylesheets/style.css' />
|
||||
</head>
|
||||
<body>
|
||||
{{{body}}}
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Reference in a new issue