Add programmable error rate to catalogue and user
This commit is contained in:
@@ -11,10 +11,11 @@
|
||||
"dependencies": {
|
||||
"body-parser": "^1.18.1",
|
||||
"express": "^4.15.4",
|
||||
"mongodb": "^2.2.33",
|
||||
"mongodb": "2.2.33",
|
||||
"pino": "^5.10.8",
|
||||
"express-pino-logger": "^4.0.0",
|
||||
"pino-pretty": "^2.5.0",
|
||||
"@instana/collector": "^1.28.0"
|
||||
"@instana/collector": "^1.28.0",
|
||||
"mathjs": "^6.6.4"
|
||||
}
|
||||
}
|
||||
|
@@ -26,7 +26,80 @@ const expLogger = expPino({
|
||||
|
||||
// MongoDB
|
||||
var productsCollection;
|
||||
var mongoConnected = false;
|
||||
|
||||
const port = process.env.CATALOGUE_SERVER_PORT || '8080';
|
||||
|
||||
// set up Mongo
|
||||
function mongoConnectionPromise() {
|
||||
return new Promise(function() {
|
||||
var mongoURL;
|
||||
|
||||
if (process.env.VCAP_SERVICES) {
|
||||
const bindingName = 'catalogue_database';
|
||||
|
||||
connectionDetails = null;
|
||||
|
||||
console.log('Env var \'VCAP_SERVICES\' found, scanning for \'catalogue_database\' service binding');
|
||||
|
||||
for (let [key, value] of Object.entries(JSON.parse(process.env.VCAP_SERVICES))) {
|
||||
try {
|
||||
connectionDetails = value.find(function(binding) {
|
||||
return bindingName == binding.binding_name && binding.credentials;
|
||||
}).credentials;
|
||||
|
||||
if (!connectionDetails) {
|
||||
throw new Error(`Cannot find a service binding with name '${bindingName}'`);
|
||||
}
|
||||
|
||||
const username = connectionDetails.username;
|
||||
const password = connectionDetails.password;
|
||||
|
||||
const normalizedAuthDetails = `${encodeURIComponent(username)}:${encodeURIComponent(password)}`;
|
||||
|
||||
mongoURL = connectionDetails.uri.replace(`${username}:${password}`, normalizedAuthDetails);
|
||||
} catch (err) {
|
||||
console.log('Cannot process key \'' + key + '\' of \'VCAP_SERVICES\'', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
} else if (process.env.MONGO_URL) {
|
||||
mongoURL = process.env.MONGO_URL;
|
||||
|
||||
console.log('MongoDB URI found in \'MONGO_URL\': ' + mongoURL);
|
||||
} else {
|
||||
mongoURL = 'mongodb://mongodb:27017/catalogue';
|
||||
|
||||
console.log('Using default MongoDB URI');
|
||||
}
|
||||
|
||||
if (!mongoURL) {
|
||||
throw new Error('MongoDB connection data missing');
|
||||
}
|
||||
|
||||
return mongoClient.connect(mongoURL);
|
||||
})
|
||||
.then(function(db) {
|
||||
productsCollection = db.collection('products');
|
||||
|
||||
db.on('close', function() {
|
||||
logger.error('Disconnected from the MongoDB database, reconnecting...', e);
|
||||
|
||||
mongoConnect();
|
||||
})
|
||||
})
|
||||
.then(function() {
|
||||
logger.info('MongoDB connected');
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.log(`Cannot connect to MongoDB: ${err}`);
|
||||
});
|
||||
};
|
||||
|
||||
async function mongoConnect() {
|
||||
await mongoConnectionPromise();
|
||||
}
|
||||
|
||||
mongoConnect();
|
||||
|
||||
const app = express();
|
||||
|
||||
@@ -51,163 +124,64 @@ app.get('/health', (req, res) => {
|
||||
|
||||
// all products
|
||||
app.get('/products', (req, res) => {
|
||||
if(mongoConnected) {
|
||||
productsCollection.find({}).toArray().then((products) => {
|
||||
res.json(products);
|
||||
}).catch((e) => {
|
||||
req.log.error('ERROR', e);
|
||||
res.status(500).send(e);
|
||||
});
|
||||
} else {
|
||||
req.log.error('database not available');
|
||||
res.status(500).send('database not avaiable');
|
||||
}
|
||||
productsCollection.find({}).toArray().then((products) => {
|
||||
res.json(products);
|
||||
}).catch((e) => {
|
||||
req.log.error('ERROR', e);
|
||||
res.status(500).send(e);
|
||||
});
|
||||
});
|
||||
|
||||
// product by SKU
|
||||
app.get('/product/:sku', (req, res) => {
|
||||
if(mongoConnected) {
|
||||
productsCollection.findOne({sku: req.params.sku}).then((product) => {
|
||||
req.log.info('product', product);
|
||||
if(product) {
|
||||
res.json(product);
|
||||
} else {
|
||||
res.status(404).send('SKU not found');
|
||||
}
|
||||
}).catch((e) => {
|
||||
req.log.error('ERROR', e);
|
||||
res.status(500).send(e);
|
||||
});
|
||||
} else {
|
||||
req.log.error('database not available');
|
||||
res.status(500).send('database not available');
|
||||
}
|
||||
productsCollection.findOne({sku: req.params.sku}).then((product) => {
|
||||
req.log.info('product', product);
|
||||
if(product) {
|
||||
res.json(product);
|
||||
} else {
|
||||
res.status(404).send('SKU not found');
|
||||
}
|
||||
}).catch((e) => {
|
||||
req.log.error('ERROR', e);
|
||||
res.status(500).send(e);
|
||||
});
|
||||
});
|
||||
|
||||
// products in a category
|
||||
app.get('/products/:cat', (req, res) => {
|
||||
if(mongoConnected) {
|
||||
productsCollection.find({ categories: req.params.cat }).sort({ name: 1 }).toArray().then((products) => {
|
||||
if(products) {
|
||||
res.json(products);
|
||||
} else {
|
||||
res.status(404).send('No products for ' + req.params.cat);
|
||||
}
|
||||
}).catch((e) => {
|
||||
req.log.error('ERROR', e);
|
||||
res.status(500).send(e);
|
||||
});
|
||||
} else {
|
||||
req.log.error('database not available');
|
||||
res.status(500).send('database not avaiable');
|
||||
}
|
||||
productsCollection.find({ categories: req.params.cat }).sort({ name: 1 }).toArray().then((products) => {
|
||||
if(products) {
|
||||
res.json(products);
|
||||
} else {
|
||||
res.status(404).send('No products for ' + req.params.cat);
|
||||
}
|
||||
}).catch((e) => {
|
||||
req.log.error('ERROR', e);
|
||||
res.status(500).send(e);
|
||||
});
|
||||
});
|
||||
|
||||
// all categories
|
||||
app.get('/categories', (req, res) => {
|
||||
if(mongoConnected) {
|
||||
productsCollection.distinct('categories').then((categories) => {
|
||||
res.json(categories);
|
||||
}).catch((e) => {
|
||||
req.log.error('ERROR', e);
|
||||
res.status(500).send(e);
|
||||
});
|
||||
} else {
|
||||
req.log.error('database not available');
|
||||
res.status(500).send('database not available');
|
||||
}
|
||||
productsCollection.distinct('categories').then((categories) => {
|
||||
res.json(categories);
|
||||
}).catch((e) => {
|
||||
req.log.error('ERROR', e);
|
||||
res.status(500).send(e);
|
||||
});
|
||||
});
|
||||
|
||||
// search name and description
|
||||
app.get('/search/:text', (req, res) => {
|
||||
if(mongoConnected) {
|
||||
productsCollection.find({ '$text': { '$search': req.params.text }}).toArray().then((hits) => {
|
||||
res.json(hits);
|
||||
}).catch((e) => {
|
||||
req.log.error('ERROR', e);
|
||||
res.status(500).send(e);
|
||||
});
|
||||
} else {
|
||||
req.log.error('database not available');
|
||||
res.status(500).send('database not available');
|
||||
}
|
||||
productsCollection.find({ '$text': { '$search': req.params.text }}).toArray().then((hits) => {
|
||||
res.json(hits);
|
||||
}).catch((e) => {
|
||||
req.log.error('ERROR', e);
|
||||
res.status(500).send(e);
|
||||
});
|
||||
});
|
||||
|
||||
// set up Mongo
|
||||
function mongoConnect() {
|
||||
return new Promise((resolve, reject) => {
|
||||
var mongoURL;
|
||||
|
||||
if (process.env.VCAP_SERVICES) {
|
||||
connectionDetails = null;
|
||||
|
||||
console.log('Env var \'VCAP_SERVICES\' found, scanning for \'catalogue_database\' service binding');
|
||||
|
||||
for (let [key, value] of Object.entries(JSON.parse(process.env.VCAP_SERVICES))) {
|
||||
try {
|
||||
binding = value.find(function(binding) {
|
||||
return 'catalogue_database' == binding.binding_name && binding.credentials;
|
||||
});
|
||||
|
||||
if (!binding) {
|
||||
continue;
|
||||
}
|
||||
|
||||
connectionDetails = binding.credentials;
|
||||
|
||||
if (connectionDetails) {
|
||||
mongoURL = connectionDetails.uri;
|
||||
|
||||
console.log('MongoDB URI for \'catalogue_database\' service binding found in \'VCAP_SERVICES\'');
|
||||
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Cannot process key \'' + key + '\' of \'VCAP_SERVICES\'', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
} else if (process.env.MONGO_URL) {
|
||||
mongoURL = process.env.MONGO_URL;
|
||||
|
||||
console.log('MongoDB URI found in \'MONGO_URL\': ' + mongoURL);
|
||||
} else {
|
||||
mongoURL = 'mongodb://mongodb:27017/catalogue';
|
||||
|
||||
console.log('Using default MongoDB URI');
|
||||
}
|
||||
|
||||
if (!mongoURL) {
|
||||
throw new Error('MongoDB connection data missing');
|
||||
}
|
||||
|
||||
mongoClient.connect(mongoURL, (error, db) => {
|
||||
if(error) {
|
||||
reject(error);
|
||||
} else {
|
||||
productsCollection = db.collection('products');
|
||||
resolve('connected');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// mongodb connection retry loop
|
||||
function mongoLoop() {
|
||||
mongoConnect().then((r) => {
|
||||
mongoConnected = true;
|
||||
logger.info('MongoDB connected');
|
||||
}).catch((e) => {
|
||||
logger.error('An error occurred', e);
|
||||
setTimeout(mongoLoop, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
mongoLoop();
|
||||
|
||||
// fire it up!
|
||||
const port = process.env.CATALOGUE_SERVER_PORT || '8080';
|
||||
// Fire up
|
||||
app.listen(port, () => {
|
||||
logger.info('Started on port', port);
|
||||
});
|
||||
|
||||
|
63
user/package-lock.json
generated
Normal file
63
user/package-lock.json
generated
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"name": "user",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"complex.js": {
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.11.tgz",
|
||||
"integrity": "sha512-6IArJLApNtdg1P1dFtn3dnyzoZBEF0MwMnrfF1exSBRpZYoy4yieMkpZhQDC0uwctw48vii0CFVyHfpgZ/DfGw=="
|
||||
},
|
||||
"decimal.js": {
|
||||
"version": "10.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz",
|
||||
"integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw=="
|
||||
},
|
||||
"escape-latex": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz",
|
||||
"integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw=="
|
||||
},
|
||||
"fraction.js": {
|
||||
"version": "4.0.12",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.0.12.tgz",
|
||||
"integrity": "sha512-8Z1K0VTG4hzYY7kA/1sj4/r1/RWLBD3xwReT/RCrUCbzPszjNQCCsy3ktkU/eaEqX3MYa4pY37a52eiBlPMlhA=="
|
||||
},
|
||||
"javascript-natural-sort": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
|
||||
"integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k="
|
||||
},
|
||||
"mathjs": {
|
||||
"version": "6.6.4",
|
||||
"resolved": "https://registry.npmjs.org/mathjs/-/mathjs-6.6.4.tgz",
|
||||
"integrity": "sha512-fvmP89ujJbDAC8ths7FZh7PWdA71dfA5WJVAzJbQhSDCHK1aBk8WRf1XcTw51ERs+sKx9nYBGsRshqmb/oe8Ag==",
|
||||
"requires": {
|
||||
"complex.js": "^2.0.11",
|
||||
"decimal.js": "^10.2.0",
|
||||
"escape-latex": "^1.2.0",
|
||||
"fraction.js": "^4.0.12",
|
||||
"javascript-natural-sort": "^0.7.1",
|
||||
"seed-random": "^2.2.0",
|
||||
"tiny-emitter": "^2.1.0",
|
||||
"typed-function": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"seed-random": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/seed-random/-/seed-random-2.2.0.tgz",
|
||||
"integrity": "sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ="
|
||||
},
|
||||
"tiny-emitter": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
|
||||
},
|
||||
"typed-function": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/typed-function/-/typed-function-1.1.1.tgz",
|
||||
"integrity": "sha512-RbN7MaTQBZLJYzDENHPA0nUmWT0Ex80KHItprrgbTPufYhIlTePvCXZxyQK7wgn19FW5bnuaBIKcBb5mRWjB1Q=="
|
||||
}
|
||||
}
|
||||
}
|
@@ -9,13 +9,14 @@
|
||||
"author": "SteveW",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.18.1",
|
||||
"express": "^4.15.4",
|
||||
"mongodb": "^2.2.33",
|
||||
"redis": "^2.8.0",
|
||||
"pino": "^5.10.8",
|
||||
"express-pino-logger": "^4.0.0",
|
||||
"pino-pretty": "^2.5.0",
|
||||
"instana-nodejs-sensor": "^1.28.0"
|
||||
"body-parser": "^1.18.1",
|
||||
"express": "^4.15.4",
|
||||
"express-pino-logger": "^4.0.0",
|
||||
"instana-nodejs-sensor": "^1.28.0",
|
||||
"mathjs": "^6.6.4",
|
||||
"mongodb": "^2.2.33",
|
||||
"pino": "^5.10.8",
|
||||
"pino-pretty": "^2.5.0",
|
||||
"redis": "^2.8.0"
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,10 @@ const bodyParser = require('body-parser');
|
||||
const express = require('express');
|
||||
const pino = require('pino');
|
||||
const expPino = require('express-pino-logger');
|
||||
const { randomInt } = require('mathjs');
|
||||
|
||||
const errorRate = parseInt(process.env.ERROR_RATE) || 0
|
||||
|
||||
|
||||
// MongoDB
|
||||
var db;
|
||||
@@ -55,6 +59,11 @@ app.get('/health', (req, res) => {
|
||||
|
||||
// use REDIS INCR to track anonymous users
|
||||
app.get('/uniqueid', (req, res) => {
|
||||
if (errorRate > randomInt(1, 100)) {
|
||||
res.status(500).send('Cannot process the uniqueid request');
|
||||
return;
|
||||
}
|
||||
|
||||
req.log.error('Unique ID test');
|
||||
// get number from Redis
|
||||
redisClient.incr('anonymous-counter', (err, r) => {
|
||||
@@ -71,6 +80,11 @@ app.get('/uniqueid', (req, res) => {
|
||||
|
||||
// check user exists
|
||||
app.get('/check/:id', (req, res) => {
|
||||
if (errorRate > randomInt(1, 100)) {
|
||||
res.status(500).send('Cannot process the check user request');
|
||||
return;
|
||||
}
|
||||
|
||||
if(mongoConnected) {
|
||||
usersCollection.findOne({name: req.params.id}).then((user) => {
|
||||
if(user) {
|
||||
@@ -104,6 +118,11 @@ app.get('/users', (req, res) => {
|
||||
});
|
||||
|
||||
app.post('/login', (req, res) => {
|
||||
if (errorRate > randomInt(1, 100)) {
|
||||
res.status(500).send('Cannot process the login request');
|
||||
return;
|
||||
}
|
||||
|
||||
req.log.info('login', req.body);
|
||||
if(req.body.name === undefined || req.body.password === undefined) {
|
||||
req.log.warn('credentails not complete');
|
||||
@@ -134,6 +153,11 @@ app.post('/login', (req, res) => {
|
||||
|
||||
// TODO - validate email address format
|
||||
app.post('/register', (req, res) => {
|
||||
if (errorRate > randomInt(1, 100)) {
|
||||
res.status(500).send('Cannot process the registration request');
|
||||
return;
|
||||
}
|
||||
|
||||
req.log.info('register', req.body);
|
||||
if(req.body.name === undefined || req.body.password === undefined || req.body.email === undefined) {
|
||||
req.log.warn('insufficient data');
|
||||
@@ -169,6 +193,11 @@ app.post('/register', (req, res) => {
|
||||
});
|
||||
|
||||
app.post('/order/:id', (req, res) => {
|
||||
if (errorRate > randomInt(1, 100)) {
|
||||
res.status(500).send('Cannot process the order request');
|
||||
return;
|
||||
}
|
||||
|
||||
req.log.info('order', req.body);
|
||||
// only for registered users
|
||||
if(mongoConnected) {
|
||||
@@ -223,6 +252,11 @@ app.post('/order/:id', (req, res) => {
|
||||
});
|
||||
|
||||
app.get('/history/:id', (req, res) => {
|
||||
if (errorRate > randomInt(1, 100)) {
|
||||
res.status(500).send('Cannot process the history request');
|
||||
return;
|
||||
}
|
||||
|
||||
if(mongoConnected) {
|
||||
ordersCollection.findOne({
|
||||
name: req.params.id
|
||||
|
Reference in New Issue
Block a user