From 68f7b4da69f0793d5426471bb32bd3962fd37889 Mon Sep 17 00:00:00 2001 From: atul-github Date: Tue, 26 Feb 2019 11:35:21 +0530 Subject: [PATCH 01/36] Removing default BAseEntity WRITE protection. --- README.md | 16 ++++++++++++++ common/models/base-entity.json | 8 ------- lib/load.js | 21 ++++++++++++++++++ server/boot/authentication.js | 18 ++++++++++------ test/test.js | 39 ++++++++++++++++++++++++++++++---- 5 files changed, 84 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index ca425f9..3633d1d 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ + [start()](#start--) + [addContextField(name, property)](#addcontextfield-name--property-) + [removeForceId](#removeforceid) + + [setACLToBaseEntity](#setACLToBaseEntity) + [observers](#observers) * [Configurations](#configurations) * [Remote End point (RestAPI)](#remote-end-point--restapi-) @@ -481,6 +482,21 @@ If you want to disable this, you can use **disableForceIdForUserModels** setting **About ForceId** : In loopback 3, ForceId setting is done on model which is **true** by default. In this case, user/programmer cannot create a record on model by passing his/her own id. Id is always generated by loopback. To disable this setting, you can use removeForceId call. +### setACLToBaseEntity +This function should be called to set default ACL on BaseEntity Model. in oeCloud 2.x, BaseEntity doesn't have any ACL applied. Therefore, all operations on all models which are derived from BaseEntity are possible. To prevent that, programmer can call this method. + +```javaScript +var oecloud = require('oe-cloud'); +oecloud.setACLToBaseEntity({ + "accessType": "WRITE", + "principalType": "ROLE", + "principalId": "$unauthenticated", + "permission": "DENY" + }); +// oecloud.boot() and other code +``` +Remember that this has to be done before other models are loaded - meaning it should be done before you call boot(). + ### observers diff --git a/common/models/base-entity.json b/common/models/base-entity.json index b9a2dc5..5a93a75 100644 --- a/common/models/base-entity.json +++ b/common/models/base-entity.json @@ -16,14 +16,6 @@ }, "validations": [], "relations": {}, - "acls": [ - { - "accessType": "WRITE", - "principalType": "ROLE", - "principalId": "$unauthenticated", - "permission": "DENY" - } - ], "methods": {} } diff --git a/lib/load.js b/lib/load.js index cb4d0b8..f90af76 100644 --- a/lib/load.js +++ b/lib/load.js @@ -241,6 +241,8 @@ app.addContextField('roles', { }); +// Atul : This utility function is used to remove forceId settings from model +// This is important for User/Role etc models from loopback app.removeForceId = function (modelName) { var model = loopback.getModelByType(modelName); model.settings.forceId = false; @@ -257,6 +259,25 @@ app.removeForceId = function (modelName) { } }; + +// Atul : By Default unauthenticated users can do WRITE operations on BaseEntity. To keep backward compatibility, application can call this function to set ACL on BaseEntity +// Remember that, if programmer wants to call this function to set ACL, it has to be done before boot. +// "acls": +// { +// "accessType": "WRITE", +// "principalType": "ROLE", +// "principalId": "$unauthenticated", +// "permission": "DENY" +// } +app.setACLToBaseEntity = function (acl) { + if (!Array.isArray(acl)) { + app.addSettingsToBaseEntity({acls: [acl]}); + } else { + app.addSettingsToBaseEntity({acls: acl}); + } +}; + + app.registry.modelBuilder.registerCustomType('timestamp', 'date'); var emailPattern = '^(([^<>()[\\]\\\\.,;:\\s@\\"]+(\\.[^<>()[\\]\\\\.,;:\\s@\\"]+)*)|(\\".+\\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$'; app.registry.modelBuilder.registerCustomType('email', 'string', { pattern: emailPattern }); diff --git a/server/boot/authentication.js b/server/boot/authentication.js index 84908ef..68376de 100644 --- a/server/boot/authentication.js +++ b/server/boot/authentication.js @@ -4,12 +4,12 @@ * Bangalore, India. All Rights Reserved. * */ -// Atul : This script is used to enable authentication for the application -// Also 'enableAuthCookie' is introduced. if this option is set then cookie is created when user login using user.login() -// When user logs out, cookie is deleted. This is helpfule at least for node-red because node-red application does not -// send access Token as part of URL / or AuthSession header -// Atul : This script includes aboutMe() API. This API returned user information to caller including context + +// Author : Atul +// This script has many user/authentication related functionalities. const loopback = require('loopback'); + +// Atul : This script includes aboutMe() API. This API returned user information to caller including context function aboutMe() { var userModel = loopback.getModelByType('User'); userModel.aboutMe = function (options, cb) { @@ -92,6 +92,9 @@ module.exports = function enableAuthentication(server) { server.removeForceId('RoleMapping'); } + // Atul : Below code will add Roles of the logged in user to AccessToken. + // As accessToken fields are available in context ( and henc options ), programmer can use list of Roles user belongs to + // roles field is added to AccessToken on load.js file var accessTokenModel = loopback.getModelByType('AccessToken'); accessTokenModel.observe('before save', function (ctx, next) { if (!ctx.isNewInstance) { @@ -135,7 +138,10 @@ module.exports = function enableAuthentication(server) { }); }); - + // Atul : This script is used to enable authentication for the application + // Also 'enableAuthCookie' is introduced. if this option is set then cookie is created when user login using user.login() + // When user logs out, cookie is deleted. This is helpfule at least for node-red because node-red application does not + // send access Token as part of URL / or AuthSession header var enableAuthCookie = server.get('enableAuthCookie'); if (!enableAuthCookie) { return; diff --git a/test/test.js b/test/test.js index f6e463e..8db3a8b 100644 --- a/test/test.js +++ b/test/test.js @@ -16,7 +16,14 @@ require = function (a) { var oecloud = require('oe-cloud'); var loopback = require('loopback'); -const path = require('path'); +// Atul : Below code is commented - kept is as reference to test behavior when by default BaseEntity write operation is protected. +// when this code is uncommented, t-17 would fail. +// oecloud.setACLToBaseEntity({ +// "accessType": "WRITE", +// "principalType": "ROLE", +// "principalId": "$unauthenticated", +// "permission": "DENY" +// }); oecloud.observe('loaded', function (ctx, next) { if (!oecloud.options.baseEntitySources) { @@ -575,7 +582,6 @@ describe(chalk.blue('oeCloud Test Started'), function (done) { }); it('t13 - changing datasource of model', function (done) { - var DataSourceDefinition = loopback.findModel('DataSourceDefinition'); var ds = oecloud.datasources['oe-cloud-test-newdb']; var newCustomerModel = loopback.findModel('NewCustomer'); ds.attach(newCustomerModel); @@ -643,6 +649,31 @@ describe(chalk.blue('oeCloud Test Started'), function (done) { done(err); }); }); -}); - + it('t17 - Able to create record in customer without passing access token', function (done) { + var url = basePath + '/customers'; + api.set('Accept', 'application/json') + .post(url) + .send({name : "customer created without access token", age : 10}) + .end(function (err, response) { + console.log(response.error); + done(err); + }); + }); + it('t17 - Should not Able to create record in customer without passing valid access token', function (done) { + var acl = { accessType: 'WRITE', permission: 'DENY', principalId: '$unauthenticated', principalType: 'ROLE' }; + var baseEntity = loopback.findModel('Customer'); + baseEntity.settings.acls = []; + baseEntity.settings.acls.push(acl); + var url = basePath + '/customers'; + api.set('Accept', 'application/json') + .post(url) + .send({name : "Another customer created without access token", age : 10}) + .end(function (err, response) { + if(response.status != 401){ + return done(new Error("unauthorized access should not be allowed")); + } + done(); + }); + }); +}); From 3801dbbcb247714ebc57f526a3d7df27f42ae777 Mon Sep 17 00:00:00 2001 From: Ramesh Date: Wed, 27 Feb 2019 12:25:38 +0530 Subject: [PATCH 02/36] added perf jmx file --- test/performance/oe-cloud-test.jmx | 988 +++++++++++++++++++++++++++++ 1 file changed, 988 insertions(+) create mode 100644 test/performance/oe-cloud-test.jmx diff --git a/test/performance/oe-cloud-test.jmx b/test/performance/oe-cloud-test.jmx new file mode 100644 index 0000000..e3836e4 --- /dev/null +++ b/test/performance/oe-cloud-test.jmx @@ -0,0 +1,988 @@ + + + + + + false + true + + + + + + + + + + hostname + ${__P(host,10.73.97.32)} + = + + + port + ${__P(port,3000)} + = + + + model + CustomerPerfModel + = + + + users + ${__P(users,1)} + = + + + rampUp + ${__P(rampup,1)} + = + + + loopcount + ${__P(loopcount,5)} + = + + + relatedmodel + AccountPerfModel + = + + + + + + continue + + false + 1 + + 1 + 1 + 1511329880000 + 1511329880000 + false + + + + + + true + + + + false + { + "username":"admin", + "password":"admin", + "email": "admin@oecloud.com" +} + = + + + + ${hostname} + ${port} + http + + /api/Users/ + POST + true + false + true + false + + + + + + + + + Accept-Language + en-US,en;q=0.8,hi;q=0.6 + + + accept + application/json + + + content-type + application/json + + + + + + + username + + Assertion.response_data + false + 2 + + + + + false + true + true + false + + + + + true + + + + false + { + "username":"admin", + "password":"admin" +} + = + + + + ${hostname} + ${port} + http + + /api/Users/login + POST + true + false + true + false + + + + + + + + + Accept-Language + en-US,en;q=0.8,hi;q=0.6 + + + accept + application/json + + + content-type + application/json + + + + + + access_token + $.id + 0 + null + + + + false + + + String access = vars.get("access_token"); +props.put("access_token", access); + + + + + id + + Assertion.response_data + false + 2 + + + + + false + true + true + false + + + + + true + + + + false + { + "id":"${relatedmodel}", + "name":"${relatedmodel}", + "plural":"${relatedmodel}", + "properties":{ + "accountNo":{ + "type":"string", + "required":true + }, + "accountType":{ + "type":"string", + "in":["savings","salary","current"] + }, + "balance":{ + "type":"number", + "min":0, + "max":1000000 + }, + "openingDate":{ + "type":"date" + } + }, + "base":"BaseEntity" +} + = + + + + ${hostname} + ${port} + http + + /api/ModelDefinitions?access_token=${access_token} + POST + true + false + true + false + + + + + + + + + accept + application/json + + + content-type + application/json + + + + + + false + true + true + false + + + + + true + + + + false + { + "id":"${model}", + "name":"${model}", + "plural":"${model}", + "properties":{ + "name":{ + "type":"string", + "required":true, + "length":42 + }, + "city":{ + "type":"string", + "in":["Bangalore","Hyderabad","Pune"] + }, + "state":{ + "type":"string" + }, + "email":{ + "type":"email", + "unique":true, + "index":true + }, + "phone":{ + "type":"string", + "length":10 + }, + "zipcode":{ + "type":"number", + "min":100000, + "max":999999 + }, + "country":{ + "type":"string" + }, + "aadhar":{ + "type":"string", + "length":16 + }, + "pan":{ + "type":"string", + "length":10 + }, + "userId":{ + "type":"string" + }, + "creditCard":{ + "type":"string", + "length":20 + }, + "ppfAccountNo":{ + "type":"string" + }, + "internetBanking":{ + "type":"boolean" + }, + "checkBook":{ + "type":"boolean" + } + }, + "base":"BaseEntity", + "relations":{ + "account":{ + "model":"${relatedmodel}", + "type":"hasMany" + } + } +} + = + + + + ${hostname} + ${port} + http + + /api/ModelDefinitions?access_token=${access_token} + POST + true + false + true + false + + + + + + + + + accept + application/json + + + content-type + application/json + + + + + + false + true + true + false + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + + + + + + + + continue + + false + 1 + + ${users} + ${rampUp} + 1511359936000 + 1511359936000 + false + + + + + + + + hostname + localhost + = + + + port + 3000 + = + + + model + PerfModels + = + + + + + + true + ${loopcount} + + + + 1 + 1000000 + 1 + id + + false + + + + 100000 + 10000000 + 1 + zip + + false + + + + + + accept + application/json + + + content-type + application/json + + + authorization + ${__property(access_token)} + + + + + + true + + + + false + { + "id":"customer${id}", + "name":"customer${id}", + "city":"Bangalore", + "state":"Karnataka", + "email":"user${id}@oeCloud.com", + "phone":"9080706050", + "zipcode":${zip}, + "country":"IN", + "aadhar":"1234567812345678", + "pan":"BBWAS4567H", + "userId":"cust${id}", + "creditCard":"12345678123456781234", + "ppfAccountNo":"PPF123", + "internetBanking":true, + "checkBook":true, + "account":[{ + "accountNo":"account${id}", + "accountType":"salary", + "balance":23456, + "openingDate":"2018-03-26T10:50:51.161z" + }] +} + = + + + + ${hostname} + ${port} + http + + /api/${model} + POST + true + false + true + false + + + + + + + false + true + true + false + + + + + true + ${loopcount} + + + + 1 + 1000000 + 1 + id + + false + + + + + + accept + application/json + + + content-type + application/json + + + authorization + ${__property(access_token)} + + + + + + + + + ${hostname} + ${port} + http + + /api/${model}/customer${id} + GET + true + false + true + false + + + + + + + false + true + true + false + + + + + true + ${loopcount} + + + + 1 + 1000000 + 1 + id + + false + + + + true + + + + false + { + "name":"customer${id}", + "city":"Bangalore", + "state":"Karnataka", + "email":"user${id}@oeCloud.com", + "phone":"9080706050", + "country":"IN", + "aadhar":"1234567812345678", + "pan":"BBWAS4567H", + "userId":"customerId${id}", + "creditCard":"12345678123456781234", + "ppfAccountNo":"PPF123", + "internetBanking":true, + "checkBook":false +} + = + + + + ${hostname} + ${port} + http + + /api/${model}/customer${id} + PUT + true + false + true + false + + + + + + + + + accept + application/json + + + content-type + application/json + + + authorization + ${__property(access_token)} + + + + + + false + true + true + false + + + + + true + ${loopcount} + + + + 1 + 1000000 + 1 + id + + false + + + + + + + ${hostname} + ${port} + http + + /api/${model}/customer${id} + DELETE + true + false + true + false + + + + + + + + + accept + application/json + + + content-type + application/json + + + authorization + ${__property(access_token)} + + + + + + false + true + true + false + + + + + "count":1 + + + Assertion.response_data + false + 2 + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + false + false + true + false + true + false + false + false + true + 0 + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + + + + + + + + From 377000e73c8e8264f9ca8e5ab20e457cad44056d Mon Sep 17 00:00:00 2001 From: Ramesh Date: Fri, 1 Mar 2019 17:58:55 +0530 Subject: [PATCH 03/36] added test cases for aggregation groupby and having clauses --- Gruntfile.js | 2 +- package.json | 4 +- test/aggregation-fn-groupby-test.js | 938 ++++++++++++++++++++++++++++ test/aggregation-fn-having-test.js | 698 +++++++++++++++++++++ test/bootstrap.js | 44 ++ test/common/models/MarksList.json | 14 + test/model-config.json | 4 + test/test.js | 126 ++-- 8 files changed, 1749 insertions(+), 81 deletions(-) create mode 100644 test/aggregation-fn-groupby-test.js create mode 100644 test/aggregation-fn-having-test.js create mode 100644 test/bootstrap.js create mode 100644 test/common/models/MarksList.json diff --git a/Gruntfile.js b/Gruntfile.js index 0ef31f4..58ff49d 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -23,7 +23,7 @@ module.exports = function GruntConfig(grunt) { mochaOptions: ['--exit'] }, coverage: { - src: ['test/test.js'], + src: ['test/test.js', 'test/aggregation-fn-groupby-test.js', 'test/aggregation-fn-having-test.js'], options: { timeout: 60000, check: { diff --git a/package.json b/package.json index fcab7bd..5738df5 100644 --- a/package.json +++ b/package.json @@ -15,13 +15,13 @@ }, "dependencies": { "async": "2.6.1", - "lodash": "4.17.11", "compression": "1.7.3", "cookie-parser": "1.4.3", + "lodash": "4.17.11", "loopback": "3.22.3", - "loopback-datasource-juggler": "3.24.0", "loopback-boot": "2.27.1", "loopback-component-explorer": "5.4.0", + "loopback-datasource-juggler": "3.24.0", "mustache": "2.3.2", "oe-logger": "git+http://evgit/oecloud.io/oe-logger.git#master", "serve-favicon": "2.5.0", diff --git a/test/aggregation-fn-groupby-test.js b/test/aggregation-fn-groupby-test.js new file mode 100644 index 0000000..123e265 --- /dev/null +++ b/test/aggregation-fn-groupby-test.js @@ -0,0 +1,938 @@ +/** + * + * ©2016-2017 EdgeVerve Systems Limited (a fully owned Infosys subsidiary), + * Bangalore, India. All Rights Reserved. + * + */ + +var bootstrap = require('./bootstrap.js'); +var loopback = require('loopback'); +var chalk = require('chalk'); +var chai = require('chai'); +chai.use(require('chai-things')); +var expect = chai.expect; +var api = bootstrap.api; + +describe(chalk.blue('Aggregation Functions with group filter test'), function () { + this.timeout(10000); + + var studentsData = [ + { + "name": "Rickon", + "maths": 14, + "physics": 32, + "chemistry": 23, + "section": "Bravo", + "gender": "Male" + }, { + "name": "Sansa", + "maths": 15, + "physics": 16, + "chemistry": 51, + "section": "Bravo", + "gender": "Female" + }, { + "name": "Bran", + "maths": 14, + "physics": 72, + "chemistry": 96, + "section": "Charlie", + "gender": "Male" + }, { + "name": "Cersei", + "maths": 17, + "physics": 2, + "chemistry": 29, + "section": "Charlie", + "gender": "Female" + }, { + "name": "Arya", + "maths": 20, + "physics": 81, + "chemistry": 70, + "section": "Delta", + "gender": "Female" + }, { + "name": "Eddard", + "maths": 33, + "physics": 72, + "chemistry": 36, + "section": "Charlie", + "gender": "Male" + }, { + "name": "Tyrion", + "maths": 64, + "physics": 34, + "chemistry": 73, + "section": "Echo", + "gender": "Male" + }, { + "name": "Jaime", + "maths": 20, + "physics": 79, + "chemistry": 25, + "section": "Delta", + "gender": "Male" + }, { + "name": "Shae", + "maths": 47, + "physics": 36, + "chemistry": 1, + "section": "Echo", + "gender": "Female" + }, { + "name": "Ygritte", + "maths": 66, + "physics": 99, + "chemistry": 3, + "section": "Golf", + "gender": "Female" + }, { + "name": "Ramsay", + "maths": 79, + "physics": 18, + "chemistry": 97, + "section": "Alpha", + "gender": "Male" + }, { + "name": "Daenerys", + "maths": 60, + "physics": 74, + "chemistry": 10, + "section": "Alpha", + "gender": "Female" + }, { + "name": "Samwell", + "maths": 19, + "physics": 20, + "chemistry": 40, + "section": "Golf", + "gender": "Male" + }, { + "name": "Jon", + "maths": 41, + "physics": 66, + "chemistry": 71, + "section": "Alpha", + "gender": "Male" + }, { + "name": "Davos", + "maths": 19, + "physics": 11, + "chemistry": 100, + "section": "Delta", + "gender": "Male" + }, { + "name": "Robb", + "maths": 65, + "physics": 17, + "chemistry": 67, + "section": "Delta", + "gender": "Male" + }, { + "name": "Robert", + "maths": 20, + "physics": 91, + "chemistry": 96, + "section": "Golf", + "gender": "Male" + }, { + "name": "Podrick", + "maths": 80, + "physics": 1, + "chemistry": 79, + "section": "Delta", + "gender": "Male" + }, { + "name": "Missendei", + "maths": 21, + "physics": 5, + "chemistry": 5, + "section": "Delta", + "gender": "Female" + }, { + "name": "Varys", + "maths": 71, + "physics": 40, + "chemistry": 16, + "section": "Charlie", + "gender": "Male" + }, { + "name": "Margaery", + "maths": 58, + "physics": 69, + "chemistry": 86, + "section": "Echo", + "gender": "Female" + }, { + "name": "Hodor", + "maths": 20, + "physics": 84, + "chemistry": 97, + "section": "Bravo", + "gender": "Male" + }, { + "name": "Theon", + "maths": 36, + "physics": 5, + "chemistry": 41, + "section": "Foxtrot", + "gender": "Male" + }, { + "name": "Jorah", + "maths": 42, + "physics": 97, + "chemistry": 77, + "section": "Bravo", + "gender": "Male" + }, { + "name": "Khal", + "maths": 34, + "physics": 46, + "chemistry": 87, + "section": "Alpha", + "gender": "Male" + }, { + "name": "Brienne", + "maths": 90, + "physics": 2, + "chemistry": 23, + "section": "Delta", + "gender": "Female" + }, { + "name": "Daario", + "maths": 96, + "physics": 4, + "chemistry": 6, + "section": "Echo", + "gender": "Male" + }, { + "name": "Joffrey", + "maths": 50, + "physics": 9, + "chemistry": 65, + "section": "Foxtrot", + "gender": "Male" + }, { + "name": "Melisandre", + "maths": 25, + "physics": 59, + "chemistry": 20, + "section": "Echo", + "gender": "Female" + }, { + "name": "Baelish", + "maths": 2, + "physics": 13, + "chemistry": 77, + "section": "Delta", + "gender": "Male" + } + ] + var marksList = loopback.findModel('MarksList'); + before('upload test data', function (done) { + marksList.create(studentsData, {}, function (err, res) { + done(); + }); + }); + + after('Cleanup', function (done) { + marksList.destroyAll({}, {}, function modelDestroyAll(err, result) { + done(err); + }); + }); + + describe(chalk.green('Aggregation Functions Test --REST'), function () { + + var path = bootstrap.basePath + '/' + marksList.pluralModelName; + + it('Test for GROUP BY clause', function (done) { + var filter = '{"group":{"groupBy":["section"]}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(7); + expect(res.body[0]).to.include.keys('section'); + expect(res.body[0]).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + done(); + } + }); + }); + + it('Test for MAX aggregation function', function (done) { + var filter = '{"group":{"groupBy":["gender"],"max":{"maths":"maxMathsMarks","physics":"maxPhysicsMarks"}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(2); + expect(res.body[0]).to.include.keys('gender'); + expect(res.body[0]).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + expect(res.body[0].maxMathsMarks).to.be.oneOf([90, 96]); + expect(res.body[1].maxMathsMarks).to.be.oneOf([90, 96]); + expect(res.body[0].maxPhysicsMarks).to.be.oneOf([99, 97]); + expect(res.body[1].maxPhysicsMarks).to.be.oneOf([99, 97]); + done(); + } + }); + }); + + it('Test for MIN aggregation function', function (done) { + var filter = '{"group":{"groupBy":["gender"],"min":{"maths":"minMathsMarks","physics":"minPhysicsMarks"}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(2); + expect(res.body[0]).to.include.keys('gender'); + expect(res.body[0]).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + expect(res.body[0].minMathsMarks).to.be.oneOf([15, 2]); + expect(res.body[1].minMathsMarks).to.be.oneOf([15, 2]); + expect(res.body[0].minPhysicsMarks).to.be.oneOf([2, 1]); + expect(res.body[1].minPhysicsMarks).to.be.oneOf([2, 1]); + done(); + } + }); + }); + + it('Test for AVG aggregation function', function (done) { + var filter = '{"group":{"groupBy":["gender"],"avg":{"chemistry":"avgChemistryMarks","maths":"avgMathsMarks"}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(2); + expect(res.body[0]).to.include.keys('gender'); + expect(res.body[0]).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + expect(res.body[0].avgChemistryMarks).to.be.oneOf([29.8, 63.45]); + expect(res.body[1].avgChemistryMarks).to.be.oneOf([29.8, 63.45]); + expect(res.body[0].avgMathsMarks).to.be.oneOf([41.9, 40.95]); + expect(res.body[1].avgMathsMarks).to.be.oneOf([41.9, 40.95]); + done(); + } + }); + }); + + it('Test for COUNT aggregation function', function (done) { + var filter = '{"group":{"groupBy":["gender"],"count":{"gender":"noOfStudents"}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(2); + expect(res.body[0]).to.include.keys('gender'); + expect(res.body[0]).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + expect(res.body[0].noOfStudents).to.be.oneOf([10, 20]); + expect(res.body[1].noOfStudents).to.be.oneOf([10, 20]); + done(); + } + }); + }); + + it('Test for SUM aggregation function', function (done) { + var filter = '{"group":{"groupBy":["gender"],"sum":{"chemistry":"sumOfChemistryMarks","physics":"sumOfPhysicsMarks","maths":"sumOfMathsMarks"}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(2); + expect(res.body[0]).to.include.keys('gender'); + expect(res.body[0]).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + expect(res.body[0].sumOfChemistryMarks).to.be.oneOf([298, 1269]); + expect(res.body[1].sumOfChemistryMarks).to.be.oneOf([298, 1269]); + expect(res.body[0].sumOfPhysicsMarks).to.be.oneOf([443, 811]); + expect(res.body[1].sumOfPhysicsMarks).to.be.oneOf([443, 811]); + expect(res.body[0].sumOfMathsMarks).to.be.oneOf([419, 819]); + expect(res.body[1].sumOfMathsMarks).to.be.oneOf([419, 819]); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions', function (done) { + var filter = '{"group":{"groupBy":["gender"],"avg":{"chemistry":"avgChemistryMarks"},"max":{"maths":"maxMathsMarks"},"min":{"physics":"minPhysics"},"sum":{"maths":"totalMathsMarks"},"count":{"name":"headCount"}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(2); + expect(res.body[0]).to.include.keys('gender'); + expect(res.body[0]).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + expect(res.body[0].avgChemistryMarks).to.be.oneOf([29.8, 63.45]); + expect(res.body[1].avgChemistryMarks).to.be.oneOf([29.8, 63.45]); + expect(res.body[0].maxMathsMarks).to.be.oneOf([90, 96]); + expect(res.body[1].maxMathsMarks).to.be.oneOf([90, 96]); + expect(res.body[0].minPhysics).to.be.oneOf([2, 1]); + expect(res.body[1].minPhysics).to.be.oneOf([2, 1]); + expect(res.body[0].totalMathsMarks).to.be.oneOf([419, 819]); + expect(res.body[1].totalMathsMarks).to.be.oneOf([419, 819]); + expect(res.body[0].headCount).to.be.oneOf([10, 20]); + expect(res.body[1].headCount).to.be.oneOf([10, 20]); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions with "where" filter', function (done) { + var filter = '{"where":{"maths":{"gt":35}},"group":{"groupBy":["gender"],"avg":{"chemistry":"avgChemistryMarks"},"max":{"maths":"maxMathsMarks"},"min":{"physics":"minPhysics"},"sum":{"maths":"totalMathsMarks"},"count":{"name":"headCount"}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(2); + expect(res.body[0]).to.include.keys('gender'); + expect(res.body[0]).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + expect(res.body[0].avgChemistryMarks).to.be.oneOf([24.6, 59.2]); + expect(res.body[1].avgChemistryMarks).to.be.oneOf([24.6, 59.2]); + expect(res.body[0].maxMathsMarks).to.be.oneOf([90, 96]); + expect(res.body[1].maxMathsMarks).to.be.oneOf([90, 96]); + expect(res.body[0].minPhysics).to.be.oneOf([2, 1]); + expect(res.body[1].minPhysics).to.be.oneOf([2, 1]); + expect(res.body[0].totalMathsMarks).to.be.oneOf([321, 624]); + expect(res.body[1].totalMathsMarks).to.be.oneOf([321, 624]); + expect(res.body[0].headCount).to.be.oneOf([5, 10]); + expect(res.body[1].headCount).to.be.oneOf([5, 10]); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions with "fields" filter', function (done) { + var filter = '{"fields":["avgChemistryMarks","gender"],"group":{"groupBy":["gender"],"avg":{"chemistry":"avgChemistryMarks"},"max":{"maths":"maxMathsMarks"}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(2); + expect(res.body[0]).to.include.keys('gender', 'avgChemistryMarks'); + expect(res.body[0]).to.not.include.keys('maths', 'physics', 'chemistry', 'name', 'section', 'maxMathsMarks'); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions with "order" filter', function (done) { + var filter = '{"order":["section DESC"],"group":{"groupBy":["section"],"avg":{"chemistry":"avgChemistryMarks"},"max":{"maths":"maxMathsMarks"}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(7); + expect(res.body[0].section).to.be.equal('Golf'); + expect(res.body[1].section).to.be.equal('Foxtrot'); + expect(res.body[2].section).to.be.equal('Echo'); + expect(res.body[3].section).to.be.equal('Delta'); + expect(res.body[4].section).to.be.equal('Charlie'); + expect(res.body[5].section).to.be.equal('Bravo'); + expect(res.body[6].section).to.be.equal('Alpha'); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions with "order" filter(on non existing field)', function (done) { + var filter = '{"order":["section ASC", "gender DESC"],"group":{"groupBy":["section"],"avg":{"chemistry":"avgChemistryMarks"},"max":{"maths":"maxMathsMarks"}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(7); + expect(res.body[0].gender).to.be.equal(undefined); + expect(res.body[6].section).to.be.equal('Golf'); + expect(res.body[5].section).to.be.equal('Foxtrot'); + expect(res.body[4].section).to.be.equal('Echo'); + expect(res.body[3].section).to.be.equal('Delta'); + expect(res.body[2].section).to.be.equal('Charlie'); + expect(res.body[1].section).to.be.equal('Bravo'); + expect(res.body[0].section).to.be.equal('Alpha'); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions with "limit" filter', function (done) { + var filter = '{"limit":3,"order":["section ASC"],"group":{"groupBy":["section"],"avg":{"chemistry":"avgChemistryMarks"},"max":{"maths":"maxMathsMarks"}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(3); + expect(res.body[2].section).to.be.equal('Charlie'); + expect(res.body[1].section).to.be.equal('Bravo'); + expect(res.body[0].section).to.be.equal('Alpha'); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions with "skip" filter', function (done) { + var filter = '{"limit":3,"skip":2,"order":["section ASC"],"group":{"groupBy":["section"],"avg":{"chemistry":"avgChemistryMarks"},"max":{"maths":"maxMathsMarks"}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(3); + expect(res.body[0].section).to.be.equal('Charlie'); + expect(res.body[1].section).to.be.equal('Delta'); + expect(res.body[2].section).to.be.equal('Echo'); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions with "offset" filter', function (done) { + var filter = '{"limit":3,"offset":2,"order":["section ASC"],"group":{"groupBy":["section"],"avg":{"chemistry":"avgChemistryMarks"},"max":{"maths":"maxMathsMarks"}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(3); + expect(res.body[0].section).to.be.equal('Charlie'); + expect(res.body[1].section).to.be.equal('Delta'); + expect(res.body[2].section).to.be.equal('Echo'); + done(); + } + }); + }); + }); + + + describe(chalk.green('Aggregation Functions Test --Programatic'), function () { + + it('Test for GROUP BY clause', function (done) { + var filter = { "group": { "groupBy": ["section"] } }; + + marksList.find(filter, {}, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(7); + expect(res[0].__data).to.include.keys('section'); + expect(res[0].__data).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + done(); + } + }); + }); + + it('Test for MAX aggregation function', function (done) { + var filter = { "group": { "groupBy": ["gender"], "max": { "maths": "maxMathsMarks", "physics": "maxPhysicsMarks" } } }; + marksList.find(filter, {}, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(2); + expect(res[0].__data).to.include.keys('gender'); + expect(res[0].__data).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + expect(res[0].maxMathsMarks).to.be.oneOf([90, 96]); + expect(res[1].maxMathsMarks).to.be.oneOf([90, 96]); + expect(res[0].maxPhysicsMarks).to.be.oneOf([99, 97]); + expect(res[1].maxPhysicsMarks).to.be.oneOf([99, 97]); + done(); + } + }); + }); + + it('Test for MIN aggregation function', function (done) { + var filter = { "group": { "groupBy": ["gender"], "min": { "maths": "minMathsMarks", "physics": "minPhysicsMarks" } } }; + marksList.find(filter, {}, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(2); + expect(res[0].__data).to.include.keys('gender'); + expect(res[0].__data).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + expect(res[0].minMathsMarks).to.be.oneOf([15, 2]); + expect(res[1].minMathsMarks).to.be.oneOf([15, 2]); + expect(res[0].minPhysicsMarks).to.be.oneOf([2, 1]); + expect(res[1].minPhysicsMarks).to.be.oneOf([2, 1]); + done(); + } + }); + }); + + it('Test for AVG aggregation function', function (done) { + var filter = { "group": { "groupBy": ["gender"], "avg": { "chemistry": "avgChemistryMarks", "maths": "avgMathsMarks" } } }; + marksList.find(filter, {}, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(2); + expect(res[0].__data).to.include.keys('gender'); + expect(res[0].__data).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + expect(res[0].avgChemistryMarks).to.be.oneOf([29.8, 63.45]); + expect(res[1].avgChemistryMarks).to.be.oneOf([29.8, 63.45]); + expect(res[0].avgMathsMarks).to.be.oneOf([41.9, 40.95]); + expect(res[1].avgMathsMarks).to.be.oneOf([41.9, 40.95]); + done(); + } + }); + }); + + it('Test for COUNT aggregation function', function (done) { + var filter = { "group": { "groupBy": ["gender"], "count": { "gender": "noOfStudents" } } }; + marksList.find(filter, {}, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(2); + expect(res[0].__data).to.include.keys('gender'); + expect(res[0].__data).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + expect(res[0].noOfStudents).to.be.oneOf([10, 20]); + expect(res[1].noOfStudents).to.be.oneOf([10, 20]); + done(); + } + }); + }); + + it('Test for SUM aggregation function', function (done) { + var filter = { "group": { "groupBy": ["gender"], "sum": { "chemistry": "sumOfChemistryMarks", "physics": "sumOfPhysicsMarks", "maths": "sumOfMathsMarks" } } }; + marksList.find(filter, {}, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(2); + expect(res[0].__data).to.include.keys('gender'); + expect(res[0].__data).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + expect(res[0].sumOfChemistryMarks).to.be.oneOf([298, 1269]); + expect(res[1].sumOfChemistryMarks).to.be.oneOf([298, 1269]); + expect(res[0].sumOfPhysicsMarks).to.be.oneOf([443, 811]); + expect(res[1].sumOfPhysicsMarks).to.be.oneOf([443, 811]); + expect(res[0].sumOfMathsMarks).to.be.oneOf([419, 819]); + expect(res[1].sumOfMathsMarks).to.be.oneOf([419, 819]); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions', function (done) { + var filter = { "group": { "groupBy": ["gender"], "avg": { "chemistry": "avgChemistryMarks" }, "max": { "maths": "maxMathsMarks" }, "min": { "physics": "minPhysics" }, "sum": { "maths": "totalMathsMarks" }, "count": { "name": "headCount" } } }; + marksList.find(filter, {}, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(2); + expect(res[0].__data).to.include.keys('gender'); + expect(res[0].__data).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + expect(res[0].avgChemistryMarks).to.be.oneOf([29.8, 63.45]); + expect(res[1].avgChemistryMarks).to.be.oneOf([29.8, 63.45]); + expect(res[0].maxMathsMarks).to.be.oneOf([90, 96]); + expect(res[1].maxMathsMarks).to.be.oneOf([90, 96]); + expect(res[0].minPhysics).to.be.oneOf([2, 1]); + expect(res[1].minPhysics).to.be.oneOf([2, 1]); + expect(res[0].totalMathsMarks).to.be.oneOf([419, 819]); + expect(res[1].totalMathsMarks).to.be.oneOf([419, 819]); + expect(res[0].headCount).to.be.oneOf([10, 20]); + expect(res[1].headCount).to.be.oneOf([10, 20]); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions with "where" filter', function (done) { + var filter = { "where": { "maths": { "gt": 35 } }, "group": { "groupBy": ["gender"], "avg": { "chemistry": "avgChemistryMarks" }, "max": { "maths": "maxMathsMarks" }, "min": { "physics": "minPhysics" }, "sum": { "maths": "totalMathsMarks" }, "count": { "name": "headCount" } } }; + marksList.find(filter, {}, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(2); + expect(res[0].__data).to.include.keys('gender'); + expect(res[0].__data).to.not.include.keys('maths', 'physics', 'chemistry', 'name'); + expect(res[0].avgChemistryMarks).to.be.oneOf([24.6, 59.2]); + expect(res[1].avgChemistryMarks).to.be.oneOf([24.6, 59.2]); + expect(res[0].maxMathsMarks).to.be.oneOf([90, 96]); + expect(res[1].maxMathsMarks).to.be.oneOf([90, 96]); + expect(res[0].minPhysics).to.be.oneOf([2, 1]); + expect(res[1].minPhysics).to.be.oneOf([2, 1]); + expect(res[0].totalMathsMarks).to.be.oneOf([321, 624]); + expect(res[1].totalMathsMarks).to.be.oneOf([321, 624]); + expect(res[0].headCount).to.be.oneOf([5, 10]); + expect(res[1].headCount).to.be.oneOf([5, 10]); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions with "fields" filter', function (done) { + var filter = { "fields": ["avgChemistryMarks", "gender"], "group": { "groupBy": ["gender"], "avg": { "chemistry": "avgChemistryMarks" }, "max": { "maths": "maxMathsMarks" } } }; + marksList.find(filter, {}, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(2); + expect(res[0].__data).to.include.keys('gender', 'avgChemistryMarks'); + expect(res[0].__data).to.not.include.keys('maths', 'physics', 'chemistry', 'name', 'section', 'maxMathsMarks'); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions with "order" filter', function (done) { + var filter = { "order": ["section DESC"], "group": { "groupBy": ["section"], "avg": { "chemistry": "avgChemistryMarks" }, "max": { "maths": "maxMathsMarks" } } }; + marksList.find(filter, {}, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(7); + expect(res[0].section).to.be.equal('Golf'); + expect(res[1].section).to.be.equal('Foxtrot'); + expect(res[2].section).to.be.equal('Echo'); + expect(res[3].section).to.be.equal('Delta'); + expect(res[4].section).to.be.equal('Charlie'); + expect(res[5].section).to.be.equal('Bravo'); + expect(res[6].section).to.be.equal('Alpha'); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions with "order" filter(on non existing field)', function (done) { + var filter = { "order": ["section ASC", "gender DESC"], "group": { "groupBy": ["section"], "avg": { "chemistry": "avgChemistryMarks" }, "max": { "maths": "maxMathsMarks" } } }; + marksList.find(filter, {}, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(7); + expect(res[0].gender).to.be.equal(undefined); + expect(res[6].section).to.be.equal('Golf'); + expect(res[5].section).to.be.equal('Foxtrot'); + expect(res[4].section).to.be.equal('Echo'); + expect(res[3].section).to.be.equal('Delta'); + expect(res[2].section).to.be.equal('Charlie'); + expect(res[1].section).to.be.equal('Bravo'); + expect(res[0].section).to.be.equal('Alpha'); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions with "limit" filter', function (done) { + var filter = { "limit": 3, "order": ["section ASC"], "group": { "groupBy": ["section"], "avg": { "chemistry": "avgChemistryMarks" }, "max": { "maths": "maxMathsMarks" } } }; + marksList.find(filter, {}, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(3); + expect(res[2].section).to.be.equal('Charlie'); + expect(res[1].section).to.be.equal('Bravo'); + expect(res[0].section).to.be.equal('Alpha'); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions with "skip" filter', function (done) { + var filter = { "limit": 3, "skip": 2, "order": ["section ASC"], "group": { "groupBy": ["section"], "avg": { "chemistry": "avgChemistryMarks" }, "max": { "maths": "maxMathsMarks" } } }; + marksList.find(filter, {}, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(3); + expect(res[0].section).to.be.equal('Charlie'); + expect(res[1].section).to.be.equal('Delta'); + expect(res[2].section).to.be.equal('Echo'); + done(); + } + }); + }); + + it('Test for applying multiple aggregation functions with "offset" filter', function (done) { + var filter = { "limit": 3, "offset": 2, "order": ["section ASC"], "group": { "groupBy": ["section"], "avg": { "chemistry": "avgChemistryMarks" }, "max": { "maths": "maxMathsMarks" } } }; + marksList.find(filter, {}, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(3); + expect(res[0].section).to.be.equal('Charlie'); + expect(res[1].section).to.be.equal('Delta'); + expect(res[2].section).to.be.equal('Echo'); + done(); + } + }); + }); + + }); + +}); \ No newline at end of file diff --git a/test/aggregation-fn-having-test.js b/test/aggregation-fn-having-test.js new file mode 100644 index 0000000..6b8cd11 --- /dev/null +++ b/test/aggregation-fn-having-test.js @@ -0,0 +1,698 @@ +/** + * + * ©2016-2017 EdgeVerve Systems Limited (a fully owned Infosys subsidiary), + * Bangalore, India. All Rights Reserved. + * + */ + +var bootstrap = require('./bootstrap.js'); +var loopback = require('loopback'); +var chalk = require('chalk'); +var chai = require('chai'); +chai.use(require('chai-things')); +var expect = chai.expect; +var api = bootstrap.api; + +describe(chalk.blue('Aggregation Functions with having filter test'), function () { + this.timeout(10000); + + var studentsData = [ + { + "name": "Rickon", + "maths": 14, + "physics": 32, + "chemistry": 23, + "section": "Bravo", + "gender": "Male" + }, { + "name": "Sansa", + "maths": 15, + "physics": 16, + "chemistry": 51, + "section": "Bravo", + "gender": "Female" + }, { + "name": "Bran", + "maths": 14, + "physics": 72, + "chemistry": 96, + "section": "Charlie", + "gender": "Male" + }, { + "name": "Cersei", + "maths": 17, + "physics": 2, + "chemistry": 29, + "section": "Charlie", + "gender": "Female" + }, { + "name": "Arya", + "maths": 20, + "physics": 81, + "chemistry": 70, + "section": "Delta", + "gender": "Female" + }, { + "name": "Eddard", + "maths": 33, + "physics": 72, + "chemistry": 36, + "section": "Charlie", + "gender": "Male" + }, { + "name": "Tyrion", + "maths": 64, + "physics": 34, + "chemistry": 73, + "section": "Echo", + "gender": "Male" + }, { + "name": "Jaime", + "maths": 20, + "physics": 79, + "chemistry": 25, + "section": "Delta", + "gender": "Male" + }, { + "name": "Shae", + "maths": 47, + "physics": 36, + "chemistry": 1, + "section": "Echo", + "gender": "Female" + }, { + "name": "Ygritte", + "maths": 66, + "physics": 99, + "chemistry": 3, + "section": "Golf", + "gender": "Female" + }, { + "name": "Ramsay", + "maths": 79, + "physics": 18, + "chemistry": 97, + "section": "Alpha", + "gender": "Male" + }, { + "name": "Daenerys", + "maths": 60, + "physics": 74, + "chemistry": 10, + "section": "Alpha", + "gender": "Female" + }, { + "name": "Samwell", + "maths": 19, + "physics": 20, + "chemistry": 40, + "section": "Golf", + "gender": "Male" + }, { + "name": "Jon", + "maths": 41, + "physics": 66, + "chemistry": 71, + "section": "Alpha", + "gender": "Male" + }, { + "name": "Davos", + "maths": 19, + "physics": 11, + "chemistry": 100, + "section": "Delta", + "gender": "Male" + }, { + "name": "Robb", + "maths": 65, + "physics": 17, + "chemistry": 67, + "section": "Delta", + "gender": "Male" + }, { + "name": "Robert", + "maths": 20, + "physics": 91, + "chemistry": 96, + "section": "Golf", + "gender": "Male" + }, { + "name": "Podrick", + "maths": 80, + "physics": 1, + "chemistry": 79, + "section": "Delta", + "gender": "Male" + }, { + "name": "Missendei", + "maths": 21, + "physics": 5, + "chemistry": 5, + "section": "Delta", + "gender": "Female" + }, { + "name": "Varys", + "maths": 71, + "physics": 40, + "chemistry": 16, + "section": "Charlie", + "gender": "Male" + }, { + "name": "Margaery", + "maths": 58, + "physics": 69, + "chemistry": 86, + "section": "Echo", + "gender": "Female" + }, { + "name": "Hodor", + "maths": 20, + "physics": 84, + "chemistry": 97, + "section": "Bravo", + "gender": "Male" + }, { + "name": "Theon", + "maths": 36, + "physics": 5, + "chemistry": 41, + "section": "Foxtrot", + "gender": "Male" + }, { + "name": "Jorah", + "maths": 42, + "physics": 97, + "chemistry": 77, + "section": "Bravo", + "gender": "Male" + }, { + "name": "Khal", + "maths": 34, + "physics": 46, + "chemistry": 87, + "section": "Alpha", + "gender": "Male" + }, { + "name": "Brienne", + "maths": 90, + "physics": 2, + "chemistry": 23, + "section": "Delta", + "gender": "Female" + }, { + "name": "Daario", + "maths": 96, + "physics": 4, + "chemistry": 6, + "section": "Echo", + "gender": "Male" + }, { + "name": "Joffrey", + "maths": 50, + "physics": 9, + "chemistry": 65, + "section": "Foxtrot", + "gender": "Male" + }, { + "name": "Melisandre", + "maths": 25, + "physics": 59, + "chemistry": 20, + "section": "Echo", + "gender": "Female" + }, { + "name": "Baelish", + "maths": 2, + "physics": 13, + "chemistry": 77, + "section": "Delta", + "gender": "Male" + } + ] + var marksList = loopback.findModel('MarksList'); + before('upload test data', function (done) { + marksList.create(studentsData, {}, function (err, res) { + done(); + }); + }); + + after('Cleanup', function (done) { + marksList.destroyAll({}, {}, function modelDestroyAll(err, result) { + done(err); + }); + }); + + describe(chalk.green('Aggregation Functions having clause Test --REST'), function () { + + var path = bootstrap.basePath + '/' + marksList.pluralModelName; + + it('Test for having clause "=" operator', function (done) { + var filter = '{"group":{"groupBy":["section","gender"],"max":{"maths":"maxMaths"}},"order":["section ASC"],"having":{"max":{"maths":60}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(1); + expect(res.body[0].maxMaths).to.be.equal(60); + done(); + } + }); + }); + + it('Test for having clause "neq" operator', function (done) { + var filter = '{"group":{"groupBy":["section","gender"],"max":{"maths":"maxMaths"}},"order":["section ASC"],"having":{"max":{"maths":{"neq":60}}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(12); + expect(res.body[0].maxMaths).to.be.not.equal(60); + done(); + } + }); + }); + + it('Test for having clause "gt" operator', function (done) { + var filter = '{"group":{"groupBy":["section","gender"],"max":{"maths":"maxMaths"}},"order":["section ASC"],"having":{"max":{"maths":{"gt":60}}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(6); + expect(res.body[0].maxMaths).to.be.gt(60); + expect(res.body[1].maxMaths).to.be.gt(60); + done(); + } + }); + }); + + it('Test for having clause "gte" operator', function (done) { + var filter = '{"group":{"groupBy":["section","gender"],"max":{"maths":"maxMaths"}},"order":["section ASC"],"having":{"max":{"maths":{"gte":60}}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(7); + expect(res.body[0].maxMaths).to.be.gte(60); + expect(res.body[1].maxMaths).to.be.gte(60); + done(); + } + }); + }); + + it('Test for having clause "lt" operator', function (done) { + var filter = '{"group":{"groupBy":["section","gender"],"max":{"maths":"maxMaths"}},"order":["section ASC"],"having":{"max":{"maths":{"lt":50}}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(4); + expect(res.body[0].maxMaths).to.be.lt(50); + expect(res.body[1].maxMaths).to.be.lt(50); + done(); + } + }); + }); + + it('Test for having clause "lte" operator', function (done) { + var filter = '{"group":{"groupBy":["section","gender"],"max":{"maths":"maxMaths"}},"order":["section ASC"],"having":{"max":{"maths":{"lte":50}}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(5); + expect(res.body[0].maxMaths).to.be.lte(50); + expect(res.body[1].maxMaths).to.be.lte(50); + done(); + } + }); + }); + + it('Test for having clause "between" operator', function (done) { + var filter = '{"group":{"groupBy":["section","gender"],"max":{"maths":"maxMaths"}},"order":["section ASC"],"having":{"max":{"maths":{"between":[40,80]}}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(8); + expect(res.body[0].maxMaths).to.be.gte(40); + expect(res.body[0].maxMaths).to.be.lte(80); + done(); + } + }); + }); + + it('Test for having clause "inq" operator', function (done) { + var filter = '{"group":{"groupBy":["section","gender"],"max":{"maths":"maxMaths"}},"order":["section ASC"],"having":{"max":{"maths":{"inq":[60,50,80]}}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(3); + expect(res.body[0].maxMaths).to.be.equal(60); + expect(res.body[1].maxMaths).to.be.equal(80); + expect(res.body[2].maxMaths).to.be.equal(50); + done(); + } + }); + }); + + it('Test for having clause "nin" operator', function (done) { + var filter = '{"group":{"groupBy":["section","gender"],"max":{"maths":"maxMaths"}},"order":["section ASC"],"having":{"max":{"maths":{"nin":[60,50,80]}}}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(10); + expect(res.body[0].maxMaths).to.be.not.equal(60); + done(); + } + }); + }); + + it('Test for having clause "and" operator', function (done) { + var filter = '{"group":{"groupBy":["section","gender"],"max":{"maths":"maxMaths"}},"order":["section ASC"],"having":{"and":[{"max":{"maths":60}},{"max":{"maths":{"inq":[50,60,80]}}}]}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(1); + expect(res.body[0].maxMaths).to.be.equal(60); + done(); + } + }); + }); + + it('Test for having clause "or" operator', function (done) { + var filter = '{"group":{"groupBy":["section","gender"],"max":{"maths":"maxMaths"}},"order":["section ASC"],"having":{"or":[{"max":{"maths":{"gte":96}}},{"max":{"maths":{"inq":[50,60,80]}}}]}}'; + var url = path + '?filter=' + filter; + api + .get(url) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json') + .expect(200).end(function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res.body); + expect(res.body).not.to.be.null; + expect(res.body).not.to.be.empty; + expect(res.body).not.to.be.undefined; + expect(res.body).to.have.length(4); + expect(res.body[0].maxMaths).to.be.equal(60); + done(); + } + }); + }); + + }); + + describe(chalk.green('Aggregation Functions having clause Test --Programatic'), function () { + + it('Test for having clause "=" operator', function (done) { + var filter = { "group": { "groupBy": ["section", "gender"], "max": { "maths": "maxMaths" } }, "order": ["section ASC"], "having": { "max": { "maths": 60 } } }; + marksList.find(filter, bootstrap.defaultContext, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(1); + expect(res[0].maxMaths).to.be.equal(60); + done(); + } + }); + }); + + it('Test for having clause "neq" operator', function (done) { + var filter = { "group": { "groupBy": ["section", "gender"], "max": { "maths": "maxMaths" } }, "order": ["section ASC"], "having": { "max": { "maths": { "neq": 60 } } } }; + marksList.find(filter, bootstrap.defaultContext, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(12); + expect(res[0].maxMaths).to.be.not.equal(60); + done(); + } + }); + }); + + it('Test for having clause "gt" operator', function (done) { + var filter = { "group": { "groupBy": ["section", "gender"], "max": { "maths": "maxMaths" } }, "order": ["section ASC"], "having": { "max": { "maths": { "gt": 60 } } } }; + marksList.find(filter, bootstrap.defaultContext, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(6); + expect(res[0].maxMaths).to.be.gt(60); + expect(res[1].maxMaths).to.be.gt(60); + done(); + } + }); + }); + + it('Test for having clause "gte" operator', function (done) { + var filter = { "group": { "groupBy": ["section", "gender"], "max": { "maths": "maxMaths" } }, "order": ["section ASC"], "having": { "max": { "maths": { "gte": 60 } } } }; + marksList.find(filter, bootstrap.defaultContext, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(7); + expect(res[0].maxMaths).to.be.gte(60); + expect(res[1].maxMaths).to.be.gte(60); + done(); + } + }); + }); + + it('Test for having clause "lt" operator', function (done) { + var filter = { "group": { "groupBy": ["section", "gender"], "max": { "maths": "maxMaths" } }, "order": ["section ASC"], "having": { "max": { "maths": { "lt": 50 } } } }; + marksList.find(filter, bootstrap.defaultContext, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(4); + expect(res[0].maxMaths).to.be.lt(50); + expect(res[1].maxMaths).to.be.lt(50); + done(); + } + }); + }); + + it('Test for having clause "lte" operator', function (done) { + var filter = { "group": { "groupBy": ["section", "gender"], "max": { "maths": "maxMaths" } }, "order": ["section ASC"], "having": { "max": { "maths": { "lte": 50 } } } }; + marksList.find(filter, bootstrap.defaultContext, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(5); + expect(res[0].maxMaths).to.be.lte(50); + expect(res[1].maxMaths).to.be.lte(50); + done(); + } + }); + }); + + it('Test for having clause "between" operator', function (done) { + var filter = { "group": { "groupBy": ["section", "gender"], "max": { "maths": "maxMaths" } }, "order": ["section ASC"], "having": { "max": { "maths": { "between": [40, 80] } } } }; + marksList.find(filter, bootstrap.defaultContext, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(8); + expect(res[0].maxMaths).to.be.gte(40); + expect(res[0].maxMaths).to.be.lte(80); + done(); + } + }); + }); + + it('Test for having clause "inq" operator', function (done) { + var filter = { "group": { "groupBy": ["section", "gender"], "max": { "maths": "maxMaths" } }, "order": ["section ASC"], "having": { "max": { "maths": { "inq": [60, 50, 80] } } } }; + marksList.find(filter, bootstrap.defaultContext, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(3); + expect(res[0].maxMaths).to.be.equal(60); + expect(res[1].maxMaths).to.be.equal(80); + expect(res[2].maxMaths).to.be.equal(50); + done(); + } + }); + }); + + it('Test for having clause "nin" operator', function (done) { + var filter = { "group": { "groupBy": ["section", "gender"], "max": { "maths": "maxMaths" } }, "order": ["section ASC"], "having": { "max": { "maths": { "nin": [60, 50, 80] } } } }; + marksList.find(filter, bootstrap.defaultContext, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(10); + expect(res[0].maxMaths).to.be.not.equal(60); + done(); + } + }); + }); + + it('Test for having clause "and" operator', function (done) { + var filter = { "group": { "groupBy": ["section", "gender"], "max": { "maths": "maxMaths" } }, "order": ["section ASC"], "having": { "and": [{ "max": { "maths": 60 } }, { "max": { "maths": { "inq": [50, 60, 80] } } }] } }; + marksList.find(filter, bootstrap.defaultContext, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(1); + expect(res[0].maxMaths).to.be.equal(60); + done(); + } + }); + }); + + it('Test for having clause "or" operator', function (done) { + var filter = { "group": { "groupBy": ["section", "gender"], "max": { "maths": "maxMaths" } }, "order": ["section ASC"], "having": { "or": [{ "max": { "maths": { "gte": 96 } } }, { "max": { "maths": { "inq": [50, 60, 80] } } }] } }; + marksList.find(filter, bootstrap.defaultContext, function (err, res) { + if (err) { + done(err); + } else { + // console.log("---> result", res); + expect(res).not.to.be.null; + expect(res).not.to.be.empty; + expect(res).not.to.be.undefined; + expect(res).to.have.length(4); + expect(res[0].maxMaths).to.be.equal(60); + done(); + } + }); + }); + + }); + +}); \ No newline at end of file diff --git a/test/bootstrap.js b/test/bootstrap.js new file mode 100644 index 0000000..f496bb7 --- /dev/null +++ b/test/bootstrap.js @@ -0,0 +1,44 @@ +/** + * + * 2018-2019 EdgeVerve Systems Limited (a fully owned Infosys subsidiary), + * Bangalore, India. All Rights Reserved. + * + */ + +const _require = require; + +require = function (a) { + if (a === 'oe-cloud') { + return _require('../index.js'); + } + return _require(a); +}; + +var oecloud = require('oe-cloud'); +var defaults = require('superagent-defaults'); +var supertest = require('supertest'); +var api = defaults(supertest(oecloud)); + +oecloud.boot(__dirname, function (err) { + if (err) { + console.log(err); + process.exit(1); + } + oecloud.start(); + oecloud.emit('test-start'); +}); + +describe('oe-cloud test Started', function () { + this.timeout(10000); + it('Waiting for application to start', function (done) { + oecloud.on('test-start', function () { + done(); + }); + }); +}); + +module.exports = { + app: oecloud, + api: api, + basePath: oecloud.get('restApiRoot') +}; diff --git a/test/common/models/MarksList.json b/test/common/models/MarksList.json new file mode 100644 index 0000000..a328acd --- /dev/null +++ b/test/common/models/MarksList.json @@ -0,0 +1,14 @@ +{ + "name": "MarksList", + "Base": "BaseEntity", + "plural": "MarksLists", + "properties": { + "name": "string", + "maths": "number", + "physics": "number", + "chemistry": "number", + "section": "string", + "gender": "string" + }, + "strict": true +} \ No newline at end of file diff --git a/test/model-config.json b/test/model-config.json index 611894c..4a4b0c3 100644 --- a/test/model-config.json +++ b/test/model-config.json @@ -41,5 +41,9 @@ "Spouse": { "dataSource": "db", "public": true + }, + "MarksList": { + "dataSource": "db", + "public": true } } diff --git a/test/test.js b/test/test.js index 8db3a8b..ae313dc 100644 --- a/test/test.js +++ b/test/test.js @@ -5,64 +5,36 @@ * */ -const _require = require; -require = function (a) { - if (a === 'oe-cloud') { - return _require('../index.js'); - } - return _require(a); -}; - -var oecloud = require('oe-cloud'); +var bootstrap = require('./bootstrap.js'); var loopback = require('loopback'); +var chalk = require('chalk'); +var chai = require('chai'); +chai.use(require('chai-things')); +var expect = chai.expect; +var app = bootstrap.app; +var api = bootstrap.api; +var basePath = bootstrap.basePath; // Atul : Below code is commented - kept is as reference to test behavior when by default BaseEntity write operation is protected. // when this code is uncommented, t-17 would fail. -// oecloud.setACLToBaseEntity({ +// app.setACLToBaseEntity({ // "accessType": "WRITE", // "principalType": "ROLE", // "principalId": "$unauthenticated", // "permission": "DENY" // }); -oecloud.observe('loaded', function (ctx, next) { - if (!oecloud.options.baseEntitySources) { - oecloud.options.baseEntitySources = [process.cwd() + '/test/common/models/base-entity-test.js']; +app.observe('loaded', function (ctx, next) { + if (!app.options.baseEntitySources) { + app.options.baseEntitySources = [process.cwd() + '/test/common/models/base-entity-test.js']; } - oecloud.attachMixinsToModelDefinition('NewMixin'); - oecloud.attachMixinsToBaseEntity('TestMixin'); - oecloud.addSettingsToBaseEntity({ mysettings: true }); - oecloud.addSettingsToModelDefinition({ xsettings: true }); + app.attachMixinsToModelDefinition('NewMixin'); + app.attachMixinsToBaseEntity('TestMixin'); + app.addSettingsToBaseEntity({ mysettings: true }); + app.addSettingsToModelDefinition({ xsettings: true }); return next(); }); -oecloud.boot(__dirname, function (err) { - if (err) { - console.log(err); - process.exit(1); - } - oecloud.start(); - oecloud.emit('test-start'); -}); - - -var chalk = require('chalk'); -var chai = require('chai'); -var async = require('async'); -chai.use(require('chai-things')); - -var expect = chai.expect; - -var app = oecloud; -var defaults = require('superagent-defaults'); -var supertest = require('supertest'); -var api = defaults(supertest(app)); -var basePath = app.get('restApiRoot'); -var url = basePath + '/Employees'; - -var models = oecloud.models; - - function deleteAllUsers(done) { var userModel = loopback.findModel('User'); userModel.destroyAll({}, {}, function (err) { @@ -83,7 +55,7 @@ function deleteAllUsers(done) { var globalCtx = { ignoreAutoScope: true, - ctx: { tenantId: '/default'} + ctx: { tenantId: '/default' } }; var defaultContext = { ctx: { tenantId: '/default' } @@ -94,10 +66,8 @@ describe(chalk.blue('oeCloud Test Started'), function (done) { this.timeout(10000); before('wait for boot scripts to complete', function (done) { - app.on('test-start', function () { - deleteAllUsers(function () { - return done(); - }); + deleteAllUsers(function () { + return done(); }); }); @@ -119,11 +89,11 @@ describe(chalk.blue('oeCloud Test Started'), function (done) { api.set('Accept', 'application/json') .post(url) .send([{ id: 'admin', username: 'admin', password: 'admin', email: 'admin@admin.com' }, - {id: 'evuser', username: 'evuser', password: 'evuser', email: 'evuser@evuser.com' }, - {id: 'infyuser', username: 'infyuser', password: 'infyuser', email: 'infyuser@infyuser.com' }, - { id: 'bpouser', username: 'bpouser', password: 'bpouser', email: 'bpouser@bpouser.com' }, - { id: 'iciciuser', username: 'iciciuser', password: 'iciciuser', email: 'iciciuser@iciciuser.com' }, - { id: 'citiuser', username: 'citiuser', password: 'citiuser', email: 'citiuser@citiuser.com' } + { id: 'evuser', username: 'evuser', password: 'evuser', email: 'evuser@evuser.com' }, + { id: 'infyuser', username: 'infyuser', password: 'infyuser', email: 'infyuser@infyuser.com' }, + { id: 'bpouser', username: 'bpouser', password: 'bpouser', email: 'bpouser@bpouser.com' }, + { id: 'iciciuser', username: 'iciciuser', password: 'iciciuser', email: 'iciciuser@iciciuser.com' }, + { id: 'citiuser', username: 'citiuser', password: 'citiuser', email: 'citiuser@citiuser.com' } ]) .end(function (err, response) { var result = response.body; @@ -145,10 +115,10 @@ describe(chalk.blue('oeCloud Test Started'), function (done) { // validations.id.shift(); //} roleModel.create([ - {id: 'admin', name: 'admin'}, - {id: 'businessUser', name: 'businessUser'}, - {id: 'guest', name: 'guest'}, - {id: 'poweruser', name: 'poweruser'} + { id: 'admin', name: 'admin' }, + { id: 'businessUser', name: 'businessUser' }, + { id: 'guest', name: 'guest' }, + { id: 'poweruser', name: 'poweruser' } ], function (err, result) { return done(err); }); @@ -162,10 +132,10 @@ describe(chalk.blue('oeCloud Test Started'), function (done) { //} //roleMapping.settings.forceId = false; roleMapping.create([ - {id: 'adminuser', principalType: roleMapping.USER, principalId: 'admin', roleId: 'admin'}, - {id: 'businessUser', principalType: roleMapping.USER, principalId: 'infyuser', roleId: 'businessUser'}, - {id: 'poweruser', principalType: roleMapping.USER, principalId: 'infyuser', roleId: 'poweruser'}, - {id: 'guestuser', principalType: roleMapping.USER, principalId: 'evuser', roleId: 'guest' } + { id: 'adminuser', principalType: roleMapping.USER, principalId: 'admin', roleId: 'admin' }, + { id: 'businessUser', principalType: roleMapping.USER, principalId: 'infyuser', roleId: 'businessUser' }, + { id: 'poweruser', principalType: roleMapping.USER, principalId: 'infyuser', roleId: 'poweruser' }, + { id: 'guestuser', principalType: roleMapping.USER, principalId: 'evuser', roleId: 'guest' } ], function (err, result) { return done(err); }); @@ -298,7 +268,7 @@ describe(chalk.blue('oeCloud Test Started'), function (done) { it('t7 hooking observer', function (done) { var modelDefinition = loopback.findModel('ModelDefinition'); modelDefinition.find({ - where: {name: 'NewCustomer'} + where: { name: 'NewCustomer' } }, {}, function (err, results) { var item = results[0]; expect(item.name).to.be.equal('NewCustomer'); @@ -339,7 +309,7 @@ describe(chalk.blue('oeCloud Test Started'), function (done) { if (!x) { return done(new Error('Expcted instnace query flag to be true')); } - x = utils.isInstanceQuery(newCustomerModel, { where: { and: [{ name: 'x' }, { age: 1 }, { and: [{ id: 1 }, {age: 10}]}] } }); + x = utils.isInstanceQuery(newCustomerModel, { where: { and: [{ name: 'x' }, { age: 1 }, { and: [{ id: 1 }, { age: 10 }] }] } }); if (!x) { return done(new Error('Expcted instnace query flag to be true')); } @@ -354,11 +324,11 @@ describe(chalk.blue('oeCloud Test Started'), function (done) { var id = utils.getIdValue(newCustomerModel, { id: 10, name: 'A' }); - var f = utils.checkDependency(oecloud, ['./']); - f = utils.checkDependency(oecloud, './'); + var f = utils.checkDependency(app, ['./']); + f = utils.checkDependency(app, './'); var o1 = { x: 'x', a1: [12, 3, 4] }; - var o2 = { y: 'y', a2: [1122, 33, 44], a1: [1122, 33, 44] }; + var o2 = { y: 'y', a2: [1122, 33, 44], a1: [1122, 33, 44] }; var o3 = utils.mergeObjects(o1, o2); expect(o3.x).to.be.equal('x'); @@ -561,7 +531,7 @@ describe(chalk.blue('oeCloud Test Started'), function (done) { console.log(JSON.stringify(newds)); - var datasources = [ newds ]; + var datasources = [newds]; it('t12 - creating datasource', function (done) { var ds = datasources[0]; @@ -582,19 +552,19 @@ describe(chalk.blue('oeCloud Test Started'), function (done) { }); it('t13 - changing datasource of model', function (done) { - var ds = oecloud.datasources['oe-cloud-test-newdb']; + var ds = app.datasources['oe-cloud-test-newdb']; var newCustomerModel = loopback.findModel('NewCustomer'); ds.attach(newCustomerModel); - newCustomerModel.create({name: 'CustomerInNewDB'}, defaultContext, function (err, res) { + newCustomerModel.create({ name: 'CustomerInNewDB' }, defaultContext, function (err, res) { return done(err); }); }); it('t14 - testing group by', function (done) { var customerModel = loopback.findModel('Customer'); - customerModel.create([{name: 'A', age: 30}, { name: 'B', age: 30 }], {}, function (err, r) { + customerModel.create([{ name: 'A', age: 30 }, { name: 'B', age: 30 }], {}, function (err, r) { if (err) return done(err); - customerModel.find({group: {groupBy: ['age']}}, {}, function (err2, r2) { + customerModel.find({ group: { groupBy: ['age'] } }, {}, function (err2, r2) { if (err2) return done(err2); console.log(r2); return done(); @@ -648,18 +618,18 @@ describe(chalk.blue('oeCloud Test Started'), function (done) { console.log(response.error); done(err); }); - }); + }); it('t17 - Able to create record in customer without passing access token', function (done) { var url = basePath + '/customers'; api.set('Accept', 'application/json') .post(url) - .send({name : "customer created without access token", age : 10}) + .send({ name: "customer created without access token", age: 10 }) .end(function (err, response) { console.log(response.error); done(err); }); - }); + }); it('t17 - Should not Able to create record in customer without passing valid access token', function (done) { var acl = { accessType: 'WRITE', permission: 'DENY', principalId: '$unauthenticated', principalType: 'ROLE' }; var baseEntity = loopback.findModel('Customer'); @@ -668,12 +638,12 @@ describe(chalk.blue('oeCloud Test Started'), function (done) { var url = basePath + '/customers'; api.set('Accept', 'application/json') .post(url) - .send({name : "Another customer created without access token", age : 10}) + .send({ name: "Another customer created without access token", age: 10 }) .end(function (err, response) { - if(response.status != 401){ + if (response.status != 401) { return done(new Error("unauthorized access should not be allowed")); } done(); }); - }); + }); }); From 42396ec08ee5bef1b70ba549cb304047fb2617cb Mon Sep 17 00:00:00 2001 From: Ramesh Date: Tue, 5 Mar 2019 11:52:23 +0530 Subject: [PATCH 04/36] modified script to accept protocol(http/https) from user --- test/performance/oe-cloud-test.jmx | 31 +++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/test/performance/oe-cloud-test.jmx b/test/performance/oe-cloud-test.jmx index e3836e4..f02718a 100644 --- a/test/performance/oe-cloud-test.jmx +++ b/test/performance/oe-cloud-test.jmx @@ -13,6 +13,11 @@ + + secure + ${__P(secure,http)} + = + hostname ${__P(host,10.73.97.32)} @@ -23,11 +28,6 @@ ${__P(port,3000)} = - - model - CustomerPerfModel - = - users ${__P(users,1)} @@ -43,6 +43,11 @@ ${__P(loopcount,5)} = + + model + CustomerPerfModel + = + relatedmodel AccountPerfModel @@ -83,7 +88,7 @@ ${hostname} ${port} - http + ${secure} /api/Users/ POST @@ -147,7 +152,7 @@ ${hostname} ${port} - http + ${secure} /api/Users/login POST @@ -246,7 +251,7 @@ props.put("access_token", access); ${hostname} ${port} - http + ${secure} /api/ModelDefinitions?access_token=${access_token} POST @@ -359,7 +364,7 @@ props.put("access_token", access); ${hostname} ${port} - http + ${secure} /api/ModelDefinitions?access_token=${access_token} POST @@ -576,7 +581,7 @@ props.put("access_token", access); ${hostname} ${port} - http + ${secure} /api/${model} POST @@ -634,7 +639,7 @@ props.put("access_token", access); ${hostname} ${port} - http + ${secure} /api/${model}/customer${id} GET @@ -696,7 +701,7 @@ props.put("access_token", access); ${hostname} ${port} - http + ${secure} /api/${model}/customer${id} PUT @@ -754,7 +759,7 @@ props.put("access_token", access); ${hostname} ${port} - http + ${secure} /api/${model}/customer${id} DELETE From c779000dbf498a999e9305392c23fc7b121d8810 Mon Sep 17 00:00:00 2001 From: ujwala Date: Thu, 7 Mar 2019 14:22:56 +0530 Subject: [PATCH 05/36] added changes to enable performance job --- .dockerignore | 5 +++ .gitlab-ci.yml | 53 +++++++++++++++++++++++++++-- test/performance/Dockerfile | 15 ++++++++ test/performance/docker-compose.yml | 19 +++++++++++ 4 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 .dockerignore create mode 100644 test/performance/Dockerfile create mode 100644 test/performance/docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a1af603 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +.git +.gitlab-ci.yml +.gitattributes +.gitignore +./test/performance/docker-compose.yml \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 944ae0b..d4d91d8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,9 +1,12 @@ stages: - pre-build - pre-build-test + - performance -image: $REGISTRY/evfoundation-executor-docker13:node8alpine36 +image: $REGISTRY/jmeter:docker variables: + DOMAIN_NAME: oecloud.local + PERF_DOMAIN_NAME: oecloud.perf REGISTRY: registry.${DOMAIN_NAME} before_script: @@ -11,6 +14,10 @@ before_script: - export group=${CI_PROJECT_NAMESPACE//[^[:alnum:]]/} - export branch=${CI_BUILD_REF_NAME//[^[:alnum:]]/} - export pipelineId=${CI_PIPELINE_ID//[^[:alnum:]]/} + - if [ $branch == "master" ]; then export APP_IMAGE_NAME=$group"-"$project; else export APP_IMAGE_NAME=$group"-"$branch"-"$project; fi + - export APP_IMAGE_NAME=$(echo $APP_IMAGE_NAME | tr '[:upper:]' '[:lower:]') + - export APP_TAG=latest + npminstall: stage: pre-build @@ -20,6 +27,7 @@ npminstall: - exit_status=0 - npm set progress=false - npm config set registry http://10.188.25.62:9002/ + #- npm config set registry https://registry.npmjs.org/ - time npm install eslint babel-eslint --no-optional - if npm run lint; then status_eslint=0; else status_eslint=1; fi - if [ $status_eslint == 1 ]; then exit_status=1 && echo "esLint errors exist"; fi @@ -102,6 +110,45 @@ oracletest: - echo "Oracle user details:"${ORACLE_USERNAME}"/"${ORACLE_PASSWORD} - npm run grunt-cover tags: - - CEP_RUNNER_EE - + - CEP_RUNNER_EE +performancejob: + stage: performance + script: + - npm set progress=false + - npm config set registry http://10.188.25.62:9002/ + #- npm config set registry https://registry.npmjs.org/ + - time npm install --no-optional + - export MONGO_HOST=10.73.97.17 + - sed 's/\$REGISTRY/'"$REGISTRY"'/g' ./test/performance/Dockerfile > Dockerfile + - echo "Building ${APP_IMAGE_NAME} image and pushing to registry..." + - time docker image build -t ${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG} --no-cache --pull . + - time docker image push ${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG} + - echo "Image (${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG}) built and pushed to registry" + - docker stack deploy --compose-file ./test/performance/docker-compose.yml ${APP_IMAGE_NAME} + - export HTTP_RESPONSE_CODE=200 + - export countElapsed=0 + - export app_exit_status=0 + - export no_proxy=$no_proxy,${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME} + - export NO_PROXY=$no_proxy + - echo "${DOCKER_ROUTER_HOST} ${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}" >> /etc/hosts + - export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/) + - while [ ${isStarted} -ne ${HTTP_RESPONSE_CODE} ]; do let countElapsed=countElapsed+1; echo ""; sleep 10; export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/); echo -n "Waiting till the URL is up..."; echo ${isStarted}; if [ $countElapsed -eq 18 ] ; then export app_exit_status=1; export isStarted=${HTTP_RESPONSE_CODE}; fi; done + - if [ $app_exit_status -eq 1 ]; then echo "App failed to start....."; docker stack ps ${APP_IMAGE_NAME}; docker service logs ${APP_IMAGE_NAME}_web; docker stack rm ${APP_IMAGE_NAME}; else echo "Your application URL is accessible @ https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/"; fi + - jmeter -n -t ./test/performance/oe-cloud-test.jmx -Jloopcount=100 -Jhost=ujwala-perf-oecloud.oecloud.perf -Jport=80 -Jusers=1 -l oe-cloud-test.jtl -Jsecure=https + - JMeterPluginsCMD.sh --generate-csv oe-cloud-test.csv --input-jtl oe-cloud-test.jtl --plugin-type AggregateReport + - if [$app_exit_status -eq 1]; then exit $app_exit_status; else echo "proceeding with performance testing"; fi + - cat oe-cloud-test.csv + - export DATE=`date '+%Y-%m-%d-%H-%M-%S'` + - cp oe-cloud-test.csv $DATE-oe-cloud-test.csv + - if docker rm -f ${APP_IMAGE_NAME}; then echo "container removed"; else echo "There is no such container"; fi + - docker run -d --name ${APP_IMAGE_NAME} -v /datadisk/cep-data/oecloud-portal/performance:/perf ${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG} tail -f ./test/server.js + - if docker exec ${APP_IMAGE_NAME} mkdir /perf/${CI_PROJECT_NAME}; then echo "folder in the path is created"; else echo "folder exists in specified path"; fi + - docker cp ./oe-cloud-test.csv ${APP_IMAGE_NAME}:/perf/${CI_PROJECT_NAME} + - docker cp ./$DATE-oe-cloud-test.csv ${APP_IMAGE_NAME}:/perf/${CI_PROJECT_NAME} + - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi + only: + - tags + when: manual + tags: + - PERF_RUNNER \ No newline at end of file diff --git a/test/performance/Dockerfile b/test/performance/Dockerfile new file mode 100644 index 0000000..9c4151f --- /dev/null +++ b/test/performance/Dockerfile @@ -0,0 +1,15 @@ +FROM $REGISTRY/alpine-node:8.11.4 + +RUN mkdir -p /home/src + +EXPOSE 3000 + +ENV NODE_ENV docker + +ENV MONGO_HOST mongo + +WORKDIR /home/src + +COPY . /home/src + +CMD node test/server.js diff --git a/test/performance/docker-compose.yml b/test/performance/docker-compose.yml new file mode 100644 index 0000000..e0ae055 --- /dev/null +++ b/test/performance/docker-compose.yml @@ -0,0 +1,19 @@ +version: "3" +services: + + web: + image: "${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG}" + environment: + NODE_ENV: "mongo" + MONGO_HOST: ${MONGO_HOST} + SERVICE_PORTS: "3000" + VIRTUAL_HOST: "https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME},${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}" + + deploy: + mode: replicated + replicas: 1 + networks: + - router_network +networks: + router_network: + external: true \ No newline at end of file From fd13f787959df56ff5e79f29819b54d4b702e26e Mon Sep 17 00:00:00 2001 From: ujwala Date: Thu, 7 Mar 2019 14:28:15 +0530 Subject: [PATCH 06/36] made performance job as manual job --- .gitlab-ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d4d91d8..33c3862 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -147,8 +147,6 @@ performancejob: - docker cp ./oe-cloud-test.csv ${APP_IMAGE_NAME}:/perf/${CI_PROJECT_NAME} - docker cp ./$DATE-oe-cloud-test.csv ${APP_IMAGE_NAME}:/perf/${CI_PROJECT_NAME} - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi - only: - - tags when: manual tags: - PERF_RUNNER \ No newline at end of file From f0e1c5f00b7f8126816f0f391671dbd780dd68da Mon Sep 17 00:00:00 2001 From: ujwala Date: Thu, 7 Mar 2019 15:34:06 +0530 Subject: [PATCH 07/36] modified .gitlab-ci.yml file --- .gitlab-ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 33c3862..2bf6d04 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -135,17 +135,17 @@ performancejob: - export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/) - while [ ${isStarted} -ne ${HTTP_RESPONSE_CODE} ]; do let countElapsed=countElapsed+1; echo ""; sleep 10; export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/); echo -n "Waiting till the URL is up..."; echo ${isStarted}; if [ $countElapsed -eq 18 ] ; then export app_exit_status=1; export isStarted=${HTTP_RESPONSE_CODE}; fi; done - if [ $app_exit_status -eq 1 ]; then echo "App failed to start....."; docker stack ps ${APP_IMAGE_NAME}; docker service logs ${APP_IMAGE_NAME}_web; docker stack rm ${APP_IMAGE_NAME}; else echo "Your application URL is accessible @ https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/"; fi - - jmeter -n -t ./test/performance/oe-cloud-test.jmx -Jloopcount=100 -Jhost=ujwala-perf-oecloud.oecloud.perf -Jport=80 -Jusers=1 -l oe-cloud-test.jtl -Jsecure=https - - JMeterPluginsCMD.sh --generate-csv oe-cloud-test.csv --input-jtl oe-cloud-test.jtl --plugin-type AggregateReport + - jmeter -n -t ./test/performance/oe-cloud-test.jmx -Jloopcount=50000 -Jhost=${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME} -Jport=443 -Jsecure=https -Jusers=1 -l ${CI_PROJECT_NAME}.jtl + - JMeterPluginsCMD.sh --generate-csv ${CI_PROJECT_NAME}.csv --input-jtl ${CI_PROJECT_NAME}.jtl --plugin-type AggregateReport - if [$app_exit_status -eq 1]; then exit $app_exit_status; else echo "proceeding with performance testing"; fi - - cat oe-cloud-test.csv + - cat ${CI_PROJECT_NAME}.csv - export DATE=`date '+%Y-%m-%d-%H-%M-%S'` - - cp oe-cloud-test.csv $DATE-oe-cloud-test.csv + - cp ${CI_PROJECT_NAME}.csv $DATE-${CI_PROJECT_NAME}.csv - if docker rm -f ${APP_IMAGE_NAME}; then echo "container removed"; else echo "There is no such container"; fi - docker run -d --name ${APP_IMAGE_NAME} -v /datadisk/cep-data/oecloud-portal/performance:/perf ${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG} tail -f ./test/server.js - if docker exec ${APP_IMAGE_NAME} mkdir /perf/${CI_PROJECT_NAME}; then echo "folder in the path is created"; else echo "folder exists in specified path"; fi - - docker cp ./oe-cloud-test.csv ${APP_IMAGE_NAME}:/perf/${CI_PROJECT_NAME} - - docker cp ./$DATE-oe-cloud-test.csv ${APP_IMAGE_NAME}:/perf/${CI_PROJECT_NAME} + - docker cp ./${CI_PROJECT_NAME}.csv ${APP_IMAGE_NAME}:/perf/${CI_PROJECT_NAME} + - docker cp ./$DATE-${CI_PROJECT_NAME}.csv ${APP_IMAGE_NAME}:/perf/${CI_PROJECT_NAME} - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi when: manual tags: From d4ac197b8564822e3d6eb05e3bffa63e44db0a8b Mon Sep 17 00:00:00 2001 From: ujwala Date: Fri, 8 Mar 2019 11:22:21 +0530 Subject: [PATCH 08/36] added performance.sh file --- .gitlab-ci.yml | 10 +++++----- test/performance/performance.sh | 13 +++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 test/performance/performance.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2bf6d04..575fa7c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,7 +3,7 @@ stages: - pre-build-test - performance -image: $REGISTRY/jmeter:docker +image: $REGISTRY/evfoundation-executor-docker13:node8alpine36 variables: DOMAIN_NAME: oecloud.local PERF_DOMAIN_NAME: oecloud.perf @@ -113,11 +113,13 @@ oracletest: - CEP_RUNNER_EE performancejob: + image: $REGISTRY/jmeter:docker stage: performance script: - npm set progress=false - npm config set registry http://10.188.25.62:9002/ #- npm config set registry https://registry.npmjs.org/ + - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi - time npm install --no-optional - export MONGO_HOST=10.73.97.17 - sed 's/\$REGISTRY/'"$REGISTRY"'/g' ./test/performance/Dockerfile > Dockerfile @@ -134,10 +136,8 @@ performancejob: - echo "${DOCKER_ROUTER_HOST} ${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}" >> /etc/hosts - export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/) - while [ ${isStarted} -ne ${HTTP_RESPONSE_CODE} ]; do let countElapsed=countElapsed+1; echo ""; sleep 10; export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/); echo -n "Waiting till the URL is up..."; echo ${isStarted}; if [ $countElapsed -eq 18 ] ; then export app_exit_status=1; export isStarted=${HTTP_RESPONSE_CODE}; fi; done - - if [ $app_exit_status -eq 1 ]; then echo "App failed to start....."; docker stack ps ${APP_IMAGE_NAME}; docker service logs ${APP_IMAGE_NAME}_web; docker stack rm ${APP_IMAGE_NAME}; else echo "Your application URL is accessible @ https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/"; fi - - jmeter -n -t ./test/performance/oe-cloud-test.jmx -Jloopcount=50000 -Jhost=${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME} -Jport=443 -Jsecure=https -Jusers=1 -l ${CI_PROJECT_NAME}.jtl - - JMeterPluginsCMD.sh --generate-csv ${CI_PROJECT_NAME}.csv --input-jtl ${CI_PROJECT_NAME}.jtl --plugin-type AggregateReport - - if [$app_exit_status -eq 1]; then exit $app_exit_status; else echo "proceeding with performance testing"; fi + - if [ $app_exit_status -eq 1 ]; then echo "App failed to start....."; docker stack ps ${APP_IMAGE_NAME}; docker service logs ${APP_IMAGE_NAME}_web; docker stack rm ${APP_IMAGE_NAME}; exit $app_exit_status; else echo "Your application URL is accessible @ https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/"; fi + - sh test/performance/performance.sh - cat ${CI_PROJECT_NAME}.csv - export DATE=`date '+%Y-%m-%d-%H-%M-%S'` - cp ${CI_PROJECT_NAME}.csv $DATE-${CI_PROJECT_NAME}.csv diff --git a/test/performance/performance.sh b/test/performance/performance.sh new file mode 100644 index 0000000..19d28bb --- /dev/null +++ b/test/performance/performance.sh @@ -0,0 +1,13 @@ +#!/bin/sh +JMX_FILES=`ls -1 ./test/performance/*.jmx` +for f in $JMX_FILES +do +JTL_FILE=${f%%.jmx}.jtl +CSV_FILE=${f%%.jmx}.csv +rm $JTL_FILE $CSV_FILE +echo "Running script $f" +jmeter -n -t $f -Jloopcount=50000 -Jhost=${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME} -Jport=443 -Jsecure=https -Jusers=1 -l $JTL_FILE +echo "Script completed $f" +echo "Converting jtl to csv for $f" +JMeterPluginsCMD.sh --generate-csv $CSV_FILE --input-jtl $JTL_FILE --plugin-type AggregateReport +done From 8c795b6f06be54ec314b19dc777fb8b6ec746e26 Mon Sep 17 00:00:00 2001 From: ujwala Date: Fri, 8 Mar 2019 12:00:41 +0530 Subject: [PATCH 09/36] added a push_performance_results.sh file --- .gitlab-ci.yml | 6 +----- test/performance/performance.sh | 1 - test/performance/push_performance_results.sh | 11 +++++++++++ 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 test/performance/push_performance_results.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 575fa7c..5d2b6f6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -138,14 +138,10 @@ performancejob: - while [ ${isStarted} -ne ${HTTP_RESPONSE_CODE} ]; do let countElapsed=countElapsed+1; echo ""; sleep 10; export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/); echo -n "Waiting till the URL is up..."; echo ${isStarted}; if [ $countElapsed -eq 18 ] ; then export app_exit_status=1; export isStarted=${HTTP_RESPONSE_CODE}; fi; done - if [ $app_exit_status -eq 1 ]; then echo "App failed to start....."; docker stack ps ${APP_IMAGE_NAME}; docker service logs ${APP_IMAGE_NAME}_web; docker stack rm ${APP_IMAGE_NAME}; exit $app_exit_status; else echo "Your application URL is accessible @ https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/"; fi - sh test/performance/performance.sh - - cat ${CI_PROJECT_NAME}.csv - - export DATE=`date '+%Y-%m-%d-%H-%M-%S'` - - cp ${CI_PROJECT_NAME}.csv $DATE-${CI_PROJECT_NAME}.csv - if docker rm -f ${APP_IMAGE_NAME}; then echo "container removed"; else echo "There is no such container"; fi - docker run -d --name ${APP_IMAGE_NAME} -v /datadisk/cep-data/oecloud-portal/performance:/perf ${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG} tail -f ./test/server.js - if docker exec ${APP_IMAGE_NAME} mkdir /perf/${CI_PROJECT_NAME}; then echo "folder in the path is created"; else echo "folder exists in specified path"; fi - - docker cp ./${CI_PROJECT_NAME}.csv ${APP_IMAGE_NAME}:/perf/${CI_PROJECT_NAME} - - docker cp ./$DATE-${CI_PROJECT_NAME}.csv ${APP_IMAGE_NAME}:/perf/${CI_PROJECT_NAME} + - sh test/performance/push_performance_results.sh - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi when: manual tags: diff --git a/test/performance/performance.sh b/test/performance/performance.sh index 19d28bb..e96fc79 100644 --- a/test/performance/performance.sh +++ b/test/performance/performance.sh @@ -4,7 +4,6 @@ for f in $JMX_FILES do JTL_FILE=${f%%.jmx}.jtl CSV_FILE=${f%%.jmx}.csv -rm $JTL_FILE $CSV_FILE echo "Running script $f" jmeter -n -t $f -Jloopcount=50000 -Jhost=${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME} -Jport=443 -Jsecure=https -Jusers=1 -l $JTL_FILE echo "Script completed $f" diff --git a/test/performance/push_performance_results.sh b/test/performance/push_performance_results.sh new file mode 100644 index 0000000..36c7146 --- /dev/null +++ b/test/performance/push_performance_results.sh @@ -0,0 +1,11 @@ +#!/bin/sh +cd ./test/performance/ +CSV_FILES=`ls -1 *.csv` +FILE_PREFIX=`date '+%Y-%m-%d-%H-%M-%S'` +for f in $CSV_FILES +do +cat $f +cp $f ${FILE_PREFIX}-${f} +docker cp $f ${APP_IMAGE_NAME}:/perf/${CI_PROJECT_NAME} +docker cp ${FILE_PREFIX}-${f} ${APP_IMAGE_NAME}:/perf/${CI_PROJECT_NAME} +done From fba095e5830dc9d8ae48f4edf33eaca3d30be986 Mon Sep 17 00:00:00 2001 From: ujwala Date: Fri, 8 Mar 2019 14:01:22 +0530 Subject: [PATCH 10/36] added env variable DB_NAME --- .gitlab-ci.yml | 1 + test/performance/docker-compose.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5d2b6f6..ad99306 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -122,6 +122,7 @@ performancejob: - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi - time npm install --no-optional - export MONGO_HOST=10.73.97.17 + - export DB_NAME=${CI_JOB_ID}_mongo - sed 's/\$REGISTRY/'"$REGISTRY"'/g' ./test/performance/Dockerfile > Dockerfile - echo "Building ${APP_IMAGE_NAME} image and pushing to registry..." - time docker image build -t ${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG} --no-cache --pull . diff --git a/test/performance/docker-compose.yml b/test/performance/docker-compose.yml index e0ae055..6d311e5 100644 --- a/test/performance/docker-compose.yml +++ b/test/performance/docker-compose.yml @@ -5,6 +5,7 @@ services: image: "${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG}" environment: NODE_ENV: "mongo" + DB_NAME: ${DB_NAME} MONGO_HOST: ${MONGO_HOST} SERVICE_PORTS: "3000" VIRTUAL_HOST: "https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME},${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}" From f68786719ec5ad91a2ef301f33d1cb6c33714b4d Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Mon, 11 Mar 2019 11:57:20 +0530 Subject: [PATCH 11/36] modified gitlab-ci.yml file --- .gitlab-ci.yml | 22 +++++++++++--------- test/performance/Dockerfile | 15 ------------- test/performance/docker-compose.yml | 20 ------------------ test/performance/performance.sh | 12 ----------- test/performance/push_performance_results.sh | 11 ---------- 5 files changed, 12 insertions(+), 68 deletions(-) delete mode 100644 test/performance/Dockerfile delete mode 100644 test/performance/docker-compose.yml delete mode 100644 test/performance/performance.sh delete mode 100644 test/performance/push_performance_results.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ad99306..e722474 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,6 @@ stages: - - pre-build - - pre-build-test + #- pre-build + #- pre-build-test - performance image: $REGISTRY/evfoundation-executor-docker13:node8alpine36 @@ -19,7 +19,7 @@ before_script: - export APP_TAG=latest -npminstall: +.npminstall: stage: pre-build script: - echo "Performing code style check..." @@ -50,7 +50,7 @@ npminstall: - CEP_RUNNER_EE -mongotest: +.mongotest: stage: pre-build-test script: - echo 'Performing MongoDB Test' @@ -70,7 +70,7 @@ mongotest: tags: - CEP_RUNNER_EE -postgrestest: +.postgrestest: stage: pre-build-test script: - echo 'Performing PostgreSQL Test' @@ -85,7 +85,7 @@ postgrestest: tags: - CEP_RUNNER_EE -oracletest: +.oracletest: image: $REGISTRY/debian-node-oracle-docker stage: pre-build-test script: @@ -123,12 +123,14 @@ performancejob: - time npm install --no-optional - export MONGO_HOST=10.73.97.17 - export DB_NAME=${CI_JOB_ID}_mongo - - sed 's/\$REGISTRY/'"$REGISTRY"'/g' ./test/performance/Dockerfile > Dockerfile + - git clone git@10.73.97.24:oecloud.io/build-tools.git + - cd build-tools/performance + - sed 's/\$REGISTRY/'"$REGISTRY"'/g' ./performance/Dockerfile > Dockerfile - echo "Building ${APP_IMAGE_NAME} image and pushing to registry..." - time docker image build -t ${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG} --no-cache --pull . - time docker image push ${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG} - echo "Image (${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG}) built and pushed to registry" - - docker stack deploy --compose-file ./test/performance/docker-compose.yml ${APP_IMAGE_NAME} + - docker stack deploy --compose-file ./performance/docker-compose.yml ${APP_IMAGE_NAME} - export HTTP_RESPONSE_CODE=200 - export countElapsed=0 - export app_exit_status=0 @@ -138,11 +140,11 @@ performancejob: - export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/) - while [ ${isStarted} -ne ${HTTP_RESPONSE_CODE} ]; do let countElapsed=countElapsed+1; echo ""; sleep 10; export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/); echo -n "Waiting till the URL is up..."; echo ${isStarted}; if [ $countElapsed -eq 18 ] ; then export app_exit_status=1; export isStarted=${HTTP_RESPONSE_CODE}; fi; done - if [ $app_exit_status -eq 1 ]; then echo "App failed to start....."; docker stack ps ${APP_IMAGE_NAME}; docker service logs ${APP_IMAGE_NAME}_web; docker stack rm ${APP_IMAGE_NAME}; exit $app_exit_status; else echo "Your application URL is accessible @ https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/"; fi - - sh test/performance/performance.sh + - sh performance/performance.sh - if docker rm -f ${APP_IMAGE_NAME}; then echo "container removed"; else echo "There is no such container"; fi - docker run -d --name ${APP_IMAGE_NAME} -v /datadisk/cep-data/oecloud-portal/performance:/perf ${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG} tail -f ./test/server.js - if docker exec ${APP_IMAGE_NAME} mkdir /perf/${CI_PROJECT_NAME}; then echo "folder in the path is created"; else echo "folder exists in specified path"; fi - - sh test/performance/push_performance_results.sh + - sh performance/push_performance_results.sh - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi when: manual tags: diff --git a/test/performance/Dockerfile b/test/performance/Dockerfile deleted file mode 100644 index 9c4151f..0000000 --- a/test/performance/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM $REGISTRY/alpine-node:8.11.4 - -RUN mkdir -p /home/src - -EXPOSE 3000 - -ENV NODE_ENV docker - -ENV MONGO_HOST mongo - -WORKDIR /home/src - -COPY . /home/src - -CMD node test/server.js diff --git a/test/performance/docker-compose.yml b/test/performance/docker-compose.yml deleted file mode 100644 index 6d311e5..0000000 --- a/test/performance/docker-compose.yml +++ /dev/null @@ -1,20 +0,0 @@ -version: "3" -services: - - web: - image: "${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG}" - environment: - NODE_ENV: "mongo" - DB_NAME: ${DB_NAME} - MONGO_HOST: ${MONGO_HOST} - SERVICE_PORTS: "3000" - VIRTUAL_HOST: "https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME},${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}" - - deploy: - mode: replicated - replicas: 1 - networks: - - router_network -networks: - router_network: - external: true \ No newline at end of file diff --git a/test/performance/performance.sh b/test/performance/performance.sh deleted file mode 100644 index e96fc79..0000000 --- a/test/performance/performance.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -JMX_FILES=`ls -1 ./test/performance/*.jmx` -for f in $JMX_FILES -do -JTL_FILE=${f%%.jmx}.jtl -CSV_FILE=${f%%.jmx}.csv -echo "Running script $f" -jmeter -n -t $f -Jloopcount=50000 -Jhost=${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME} -Jport=443 -Jsecure=https -Jusers=1 -l $JTL_FILE -echo "Script completed $f" -echo "Converting jtl to csv for $f" -JMeterPluginsCMD.sh --generate-csv $CSV_FILE --input-jtl $JTL_FILE --plugin-type AggregateReport -done diff --git a/test/performance/push_performance_results.sh b/test/performance/push_performance_results.sh deleted file mode 100644 index 36c7146..0000000 --- a/test/performance/push_performance_results.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -cd ./test/performance/ -CSV_FILES=`ls -1 *.csv` -FILE_PREFIX=`date '+%Y-%m-%d-%H-%M-%S'` -for f in $CSV_FILES -do -cat $f -cp $f ${FILE_PREFIX}-${f} -docker cp $f ${APP_IMAGE_NAME}:/perf/${CI_PROJECT_NAME} -docker cp ${FILE_PREFIX}-${f} ${APP_IMAGE_NAME}:/perf/${CI_PROJECT_NAME} -done From d74e386bb4fe43ed6bd88f62a2728eb32134c951 Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Mon, 11 Mar 2019 12:11:49 +0530 Subject: [PATCH 12/36] modified gitlab-ci.yml file --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e722474..bd36cc8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -124,7 +124,7 @@ performancejob: - export MONGO_HOST=10.73.97.17 - export DB_NAME=${CI_JOB_ID}_mongo - git clone git@10.73.97.24:oecloud.io/build-tools.git - - cd build-tools/performance + - cd build-tools - sed 's/\$REGISTRY/'"$REGISTRY"'/g' ./performance/Dockerfile > Dockerfile - echo "Building ${APP_IMAGE_NAME} image and pushing to registry..." - time docker image build -t ${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG} --no-cache --pull . From 62fc08cfc7986fe60482f8e6244b801e464e5d93 Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Mon, 11 Mar 2019 12:17:12 +0530 Subject: [PATCH 13/36] modified gitlab-ci.yml file --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bd36cc8..e604fcd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -123,7 +123,7 @@ performancejob: - time npm install --no-optional - export MONGO_HOST=10.73.97.17 - export DB_NAME=${CI_JOB_ID}_mongo - - git clone git@10.73.97.24:oecloud.io/build-tools.git + - git clone http://10.73.97.24/oecloud.io/build-tools.git - cd build-tools - sed 's/\$REGISTRY/'"$REGISTRY"'/g' ./performance/Dockerfile > Dockerfile - echo "Building ${APP_IMAGE_NAME} image and pushing to registry..." From 7c1738eff6675a153a59806e1524497110839c78 Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Mon, 11 Mar 2019 12:32:30 +0530 Subject: [PATCH 14/36] modified gitlab-ci.yml file --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e604fcd..472caed 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -125,12 +125,12 @@ performancejob: - export DB_NAME=${CI_JOB_ID}_mongo - git clone http://10.73.97.24/oecloud.io/build-tools.git - cd build-tools - - sed 's/\$REGISTRY/'"$REGISTRY"'/g' ./performance/Dockerfile > Dockerfile + - sed 's/\$REGISTRY/'"$REGISTRY"'/g' ./build-tools/performance/Dockerfile > Dockerfile - echo "Building ${APP_IMAGE_NAME} image and pushing to registry..." - time docker image build -t ${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG} --no-cache --pull . - time docker image push ${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG} - echo "Image (${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG}) built and pushed to registry" - - docker stack deploy --compose-file ./performance/docker-compose.yml ${APP_IMAGE_NAME} + - docker stack deploy --compose-file ./build-tools/performance/docker-compose.yml ${APP_IMAGE_NAME} - export HTTP_RESPONSE_CODE=200 - export countElapsed=0 - export app_exit_status=0 @@ -140,11 +140,11 @@ performancejob: - export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/) - while [ ${isStarted} -ne ${HTTP_RESPONSE_CODE} ]; do let countElapsed=countElapsed+1; echo ""; sleep 10; export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/); echo -n "Waiting till the URL is up..."; echo ${isStarted}; if [ $countElapsed -eq 18 ] ; then export app_exit_status=1; export isStarted=${HTTP_RESPONSE_CODE}; fi; done - if [ $app_exit_status -eq 1 ]; then echo "App failed to start....."; docker stack ps ${APP_IMAGE_NAME}; docker service logs ${APP_IMAGE_NAME}_web; docker stack rm ${APP_IMAGE_NAME}; exit $app_exit_status; else echo "Your application URL is accessible @ https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/"; fi - - sh performance/performance.sh + - sh ./build-tools/performance/performance.sh - if docker rm -f ${APP_IMAGE_NAME}; then echo "container removed"; else echo "There is no such container"; fi - docker run -d --name ${APP_IMAGE_NAME} -v /datadisk/cep-data/oecloud-portal/performance:/perf ${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG} tail -f ./test/server.js - if docker exec ${APP_IMAGE_NAME} mkdir /perf/${CI_PROJECT_NAME}; then echo "folder in the path is created"; else echo "folder exists in specified path"; fi - - sh performance/push_performance_results.sh + - sh ./build-tools/performance/push_performance_results.sh - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi when: manual tags: From 5183b2b04c723dc61da0ebf33720f2f051672d2f Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Mon, 11 Mar 2019 12:37:31 +0530 Subject: [PATCH 15/36] Update .gitlab-ci.yml --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 472caed..bbefc9c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -124,7 +124,6 @@ performancejob: - export MONGO_HOST=10.73.97.17 - export DB_NAME=${CI_JOB_ID}_mongo - git clone http://10.73.97.24/oecloud.io/build-tools.git - - cd build-tools - sed 's/\$REGISTRY/'"$REGISTRY"'/g' ./build-tools/performance/Dockerfile > Dockerfile - echo "Building ${APP_IMAGE_NAME} image and pushing to registry..." - time docker image build -t ${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG} --no-cache --pull . From 07a8633aa7f9538b2f5a2df997bf31884dac6c01 Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Mon, 11 Mar 2019 12:49:02 +0530 Subject: [PATCH 16/36] moved sh scripts out of project --- .gitlab-ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bbefc9c..9a37167 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,6 @@ stages: - #- pre-build - #- pre-build-test + - pre-build + - pre-build-test - performance image: $REGISTRY/evfoundation-executor-docker13:node8alpine36 @@ -19,7 +19,7 @@ before_script: - export APP_TAG=latest -.npminstall: +npminstall: stage: pre-build script: - echo "Performing code style check..." @@ -70,7 +70,7 @@ before_script: tags: - CEP_RUNNER_EE -.postgrestest: +postgrestest: stage: pre-build-test script: - echo 'Performing PostgreSQL Test' @@ -85,7 +85,7 @@ before_script: tags: - CEP_RUNNER_EE -.oracletest: +oracletest: image: $REGISTRY/debian-node-oracle-docker stage: pre-build-test script: From c8b163170bea63b8a346f978b44705e5ac41b06c Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Mon, 11 Mar 2019 12:54:54 +0530 Subject: [PATCH 17/36] moved sh scripts out of project --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9a37167..a098f67 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -50,7 +50,7 @@ npminstall: - CEP_RUNNER_EE -.mongotest: +mongotest: stage: pre-build-test script: - echo 'Performing MongoDB Test' From edb2dae7a0ea6ca0b79e0af650083593216f52cb Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Tue, 12 Mar 2019 16:12:36 +0530 Subject: [PATCH 18/36] modified gitlab-ci.yml file --- .gitlab-ci.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a098f67..e34ab6a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -139,12 +139,10 @@ performancejob: - export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/) - while [ ${isStarted} -ne ${HTTP_RESPONSE_CODE} ]; do let countElapsed=countElapsed+1; echo ""; sleep 10; export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/); echo -n "Waiting till the URL is up..."; echo ${isStarted}; if [ $countElapsed -eq 18 ] ; then export app_exit_status=1; export isStarted=${HTTP_RESPONSE_CODE}; fi; done - if [ $app_exit_status -eq 1 ]; then echo "App failed to start....."; docker stack ps ${APP_IMAGE_NAME}; docker service logs ${APP_IMAGE_NAME}_web; docker stack rm ${APP_IMAGE_NAME}; exit $app_exit_status; else echo "Your application URL is accessible @ https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/"; fi + - mkdir -p /artifacts/${CI_PROJECT_NAME}/performance - sh ./build-tools/performance/performance.sh - - if docker rm -f ${APP_IMAGE_NAME}; then echo "container removed"; else echo "There is no such container"; fi - - docker run -d --name ${APP_IMAGE_NAME} -v /datadisk/cep-data/oecloud-portal/performance:/perf ${REGISTRY}/${APP_IMAGE_NAME}:${APP_TAG} tail -f ./test/server.js - - if docker exec ${APP_IMAGE_NAME} mkdir /perf/${CI_PROJECT_NAME}; then echo "folder in the path is created"; else echo "folder exists in specified path"; fi - - sh ./build-tools/performance/push_performance_results.sh - - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi + - sh ./build-tools/performance/push_artifacts.sh + - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi when: manual tags: - PERF_RUNNER \ No newline at end of file From a8cc218dfdf4629bdc4257d0294cf1b2bb9efef2 Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Tue, 12 Mar 2019 16:15:21 +0530 Subject: [PATCH 19/36] modified gitlab-ci.yml file --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e34ab6a..55c8c0e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -139,10 +139,10 @@ performancejob: - export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/) - while [ ${isStarted} -ne ${HTTP_RESPONSE_CODE} ]; do let countElapsed=countElapsed+1; echo ""; sleep 10; export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/); echo -n "Waiting till the URL is up..."; echo ${isStarted}; if [ $countElapsed -eq 18 ] ; then export app_exit_status=1; export isStarted=${HTTP_RESPONSE_CODE}; fi; done - if [ $app_exit_status -eq 1 ]; then echo "App failed to start....."; docker stack ps ${APP_IMAGE_NAME}; docker service logs ${APP_IMAGE_NAME}_web; docker stack rm ${APP_IMAGE_NAME}; exit $app_exit_status; else echo "Your application URL is accessible @ https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/"; fi - - mkdir -p /artifacts/${CI_PROJECT_NAME}/performance + - mkdir -p /artifacts/${CI_PROJECT_NAME}/performance - sh ./build-tools/performance/performance.sh - - sh ./build-tools/performance/push_artifacts.sh - - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi + - sh ./build-tools/performance/push_artifacts.sh + - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi when: manual tags: - PERF_RUNNER \ No newline at end of file From dcfa65b80f30949b91c39411f64ab2220797108d Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Fri, 15 Mar 2019 11:52:12 +0530 Subject: [PATCH 20/36] modified gitlab-ci.yml file --- .gitlab-ci.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 55c8c0e..6d1b8b9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -36,7 +36,9 @@ npminstall: - time npm install --no-optional - echo "Node Modules Installed" - npm config rm registry - - npm audit --json >vulnerabilities.json + - mkdir -p /artifacts/${CI_PROJECT_NAME}/performance + - export FILE_PREFIX=`date '+%Y-%m-%d-%H-%M-%S'` + - npm audit --json >/artifacts/${CI_PROJECT_NAME}/audit/${FILE_PREFIX}-vulnerabiliites.json - status_vulnerabilities='echo $?' - npm audit - if [ $status_vulnerabilities == 1 ]; then exit_status=1 && echo "Dependency vulnerabilities exist"; fi @@ -50,7 +52,7 @@ npminstall: - CEP_RUNNER_EE -mongotest: +.mongotest: stage: pre-build-test script: - echo 'Performing MongoDB Test' @@ -70,7 +72,7 @@ mongotest: tags: - CEP_RUNNER_EE -postgrestest: +.postgrestest: stage: pre-build-test script: - echo 'Performing PostgreSQL Test' @@ -85,7 +87,7 @@ postgrestest: tags: - CEP_RUNNER_EE -oracletest: +.oracletest: image: $REGISTRY/debian-node-oracle-docker stage: pre-build-test script: From eab04e5af610a15eea46b5ffd86a0f713a38451f Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Fri, 15 Mar 2019 11:57:25 +0530 Subject: [PATCH 21/36] Update .gitlab-ci.yml --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6d1b8b9..06a7aad 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -36,7 +36,7 @@ npminstall: - time npm install --no-optional - echo "Node Modules Installed" - npm config rm registry - - mkdir -p /artifacts/${CI_PROJECT_NAME}/performance + - mkdir -p /artifacts/${CI_PROJECT_NAME}/audit - export FILE_PREFIX=`date '+%Y-%m-%d-%H-%M-%S'` - npm audit --json >/artifacts/${CI_PROJECT_NAME}/audit/${FILE_PREFIX}-vulnerabiliites.json - status_vulnerabilities='echo $?' From 2217e829b10964ab75dd3e7ce63b041a1aa7acb6 Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Mon, 18 Mar 2019 15:07:21 +0530 Subject: [PATCH 22/36] added copy-artifacts stage --- .gitlab-ci.yml | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6d1b8b9..a98391a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,6 +2,7 @@ stages: - pre-build - pre-build-test - performance + - copy-artifacts image: $REGISTRY/evfoundation-executor-docker13:node8alpine36 variables: @@ -36,9 +37,8 @@ npminstall: - time npm install --no-optional - echo "Node Modules Installed" - npm config rm registry - - mkdir -p /artifacts/${CI_PROJECT_NAME}/performance - - export FILE_PREFIX=`date '+%Y-%m-%d-%H-%M-%S'` - - npm audit --json >/artifacts/${CI_PROJECT_NAME}/audit/${FILE_PREFIX}-vulnerabiliites.json + - mkdir -p /artifacts/${CI_PROJECT_NAME}/ + - npm audit --json > /artifacts/${CI_PROJECT_NAME}/vulnerabilities.json - status_vulnerabilities='echo $?' - npm audit - if [ $status_vulnerabilities == 1 ]; then exit_status=1 && echo "Dependency vulnerabilities exist"; fi @@ -48,11 +48,12 @@ npminstall: expire_in: 2h paths: - node_modules/ + - /artifacts/ tags: - CEP_RUNNER_EE -.mongotest: +mongotest: stage: pre-build-test script: - echo 'Performing MongoDB Test' @@ -63,16 +64,18 @@ npminstall: - npm config set registry http://10.188.25.62:9002/ - time npm install --no-optional - npm run grunt-cover + - mkdir -p /artifacts/${CI_PROJECT_NAME}/ + - cp -r coverage /artifacts/${CI_PROJECT_NAME}/ artifacts: untracked: true expire_in: 2h paths: - coverage/ - when: on_failure + - /artifacts/ tags: - CEP_RUNNER_EE -.postgrestest: +postgrestest: stage: pre-build-test script: - echo 'Performing PostgreSQL Test' @@ -87,7 +90,7 @@ npminstall: tags: - CEP_RUNNER_EE -.oracletest: +oracletest: image: $REGISTRY/debian-node-oracle-docker stage: pre-build-test script: @@ -143,8 +146,22 @@ performancejob: - if [ $app_exit_status -eq 1 ]; then echo "App failed to start....."; docker stack ps ${APP_IMAGE_NAME}; docker service logs ${APP_IMAGE_NAME}_web; docker stack rm ${APP_IMAGE_NAME}; exit $app_exit_status; else echo "Your application URL is accessible @ https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/"; fi - mkdir -p /artifacts/${CI_PROJECT_NAME}/performance - sh ./build-tools/performance/performance.sh - - sh ./build-tools/performance/push_artifacts.sh - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi when: manual + artifacts: + untracked: true + expire_in: 2h + paths: + - node_modules/ + - /artifacts/ + tags: + - PERF_RUNNER + +copyartifacts: + stage: copy-artifacts + script: + - echo "Copying all artifacts" + - git clone http://10.73.97.24/oecloud.io/build-tools.git + - sh ./build-tools/push_artifacts.sh tags: - - PERF_RUNNER \ No newline at end of file + - CEP_RUNNER \ No newline at end of file From 0d0deaea92a499060a71be38f23578239282199b Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Tue, 19 Mar 2019 10:02:10 +0530 Subject: [PATCH 23/36] modified .gitlab-ci.yml file --- .gitlab-ci.yml | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a98391a..197bec0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,7 +2,7 @@ stages: - pre-build - pre-build-test - performance - - copy-artifacts + - push-artifacts image: $REGISTRY/evfoundation-executor-docker13:node8alpine36 variables: @@ -117,17 +117,33 @@ oracletest: tags: - CEP_RUNNER_EE +pushartifacts: + stage: push-artifacts + script: + - echo "Copying all artifacts" + - git clone http://10.73.97.24/oecloud.io/build-tools.git + - sh ./build-tools/push_artifacts.sh + tags: + - CEP_RUNNER + performancejob: image: $REGISTRY/jmeter:docker stage: performance + variables: + NODE_ENV: mongo + MONGO_HOST: 10.73.97.17 + DB_NAME: ${CI_JOB_ID}_mongo + APP_PROTOCOL: https + APP_PORT: 443 + PERF_USERS: 1 + PERF_LOOPCOUNT: 50000 script: - npm set progress=false - npm config set registry http://10.188.25.62:9002/ #- npm config set registry https://registry.npmjs.org/ - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi - time npm install --no-optional - - export MONGO_HOST=10.73.97.17 - - export DB_NAME=${CI_JOB_ID}_mongo + - export APP_URL=${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME} - git clone http://10.73.97.24/oecloud.io/build-tools.git - sed 's/\$REGISTRY/'"$REGISTRY"'/g' ./build-tools/performance/Dockerfile > Dockerfile - echo "Building ${APP_IMAGE_NAME} image and pushing to registry..." @@ -145,7 +161,7 @@ performancejob: - while [ ${isStarted} -ne ${HTTP_RESPONSE_CODE} ]; do let countElapsed=countElapsed+1; echo ""; sleep 10; export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/); echo -n "Waiting till the URL is up..."; echo ${isStarted}; if [ $countElapsed -eq 18 ] ; then export app_exit_status=1; export isStarted=${HTTP_RESPONSE_CODE}; fi; done - if [ $app_exit_status -eq 1 ]; then echo "App failed to start....."; docker stack ps ${APP_IMAGE_NAME}; docker service logs ${APP_IMAGE_NAME}_web; docker stack rm ${APP_IMAGE_NAME}; exit $app_exit_status; else echo "Your application URL is accessible @ https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/"; fi - mkdir -p /artifacts/${CI_PROJECT_NAME}/performance - - sh ./build-tools/performance/performance.sh + - sh ./build-tools/push_artifacts.sh - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi when: manual artifacts: @@ -157,11 +173,3 @@ performancejob: tags: - PERF_RUNNER -copyartifacts: - stage: copy-artifacts - script: - - echo "Copying all artifacts" - - git clone http://10.73.97.24/oecloud.io/build-tools.git - - sh ./build-tools/push_artifacts.sh - tags: - - CEP_RUNNER \ No newline at end of file From 73fc3948b7c761fabaff12bf1ccf6849d1600d3e Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Tue, 19 Mar 2019 10:12:34 +0530 Subject: [PATCH 24/36] added variables field to performance step --- .gitlab-ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 197bec0..2f4325d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -130,13 +130,13 @@ performancejob: image: $REGISTRY/jmeter:docker stage: performance variables: - NODE_ENV: mongo - MONGO_HOST: 10.73.97.17 - DB_NAME: ${CI_JOB_ID}_mongo - APP_PROTOCOL: https - APP_PORT: 443 - PERF_USERS: 1 - PERF_LOOPCOUNT: 50000 + NODE_ENV: "mongo" + MONGO_HOST: "10.73.97.17" + DB_NAME: "${CI_JOB_ID}_mongo" + APP_PROTOCOL: "https" + APP_PORT: "443" + PERF_USERS: "1" + PERF_LOOPCOUNT: "50000" script: - npm set progress=false - npm config set registry http://10.188.25.62:9002/ From 9d533b7fbfa32b3bae39065dbe9101e051df0a3c Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Tue, 19 Mar 2019 10:15:01 +0530 Subject: [PATCH 25/36] minor changes in gitlab-ci.yml file --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2f4325d..41d2c05 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,8 +1,9 @@ stages: - pre-build - pre-build-test - - performance - push-artifacts + - performance + image: $REGISTRY/evfoundation-executor-docker13:node8alpine36 variables: From f1404aa3ad00ad19f2f3866b207cf739f92955f9 Mon Sep 17 00:00:00 2001 From: kvsrohit Date: Tue, 19 Mar 2019 12:09:10 +0530 Subject: [PATCH 26/36] support https --- lib/load.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/load.js b/lib/load.js index f90af76..205ee9e 100644 --- a/lib/load.js +++ b/lib/load.js @@ -69,7 +69,27 @@ app.createServer = function () { if ( app.server ) { return app.server; } - var server = require('http').createServer(app); + + var server; + if (process.env.REQUIRE_HTTPS === true || process.env.REQUIRE_HTTPS === 'true') { + var keyPath = process.env.SSL_KEY_PATH || ''; + var certPath = process.env.SSL_CERT_PATH || ''; + if (!(keyPath && certPath)) { + console.log('HTTPS Enabled but SSL_KEY_PATH or SSL_CERT_PATH are missing'); + process.exit(1); + } + /** + * SSL_KEY_PATH & SSL_CERT_PATH should be absolute paths + */ + let configOptions = { + key: fs.readFileSync(keyPath).toString(), + cert: fs.readFileSync(certPath).toString() + }; + server = require('https').createServer(configOptions, app); + } else { + server = require('http').createServer(app); + } + app.server = server; return server; }; From a6d6cb39fd9e6d0ea79f3fd73da369b8abb5647a Mon Sep 17 00:00:00 2001 From: kvsrohit Date: Tue, 19 Mar 2019 15:53:29 +0530 Subject: [PATCH 27/36] throw error instead of process.exit --- lib/load.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/load.js b/lib/load.js index 205ee9e..9d1c90c 100644 --- a/lib/load.js +++ b/lib/load.js @@ -15,6 +15,7 @@ var debug = require('debug')('oe-cloud:oe-cloud'); var async = require('async'); var jutil = require('loopback-datasource-juggler/lib/jutil'); var observerMixin = require('loopback-datasource-juggler/lib/observer'); +var fs = require('fs'); wrapper.initWrapper(); @@ -75,8 +76,7 @@ app.createServer = function () { var keyPath = process.env.SSL_KEY_PATH || ''; var certPath = process.env.SSL_CERT_PATH || ''; if (!(keyPath && certPath)) { - console.log('HTTPS Enabled but SSL_KEY_PATH or SSL_CERT_PATH are missing'); - process.exit(1); + throw new Error('HTTPS Enabled but SSL_KEY_PATH or SSL_CERT_PATH are not defined'); } /** * SSL_KEY_PATH & SSL_CERT_PATH should be absolute paths From a8cd9981f48c2424568de3b74f8694c6f6af708a Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Wed, 20 Mar 2019 10:17:11 +0530 Subject: [PATCH 28/36] verifying atifacts copy --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 41d2c05..e9c55ae 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,7 +38,7 @@ npminstall: - time npm install --no-optional - echo "Node Modules Installed" - npm config rm registry - - mkdir -p /artifacts/${CI_PROJECT_NAME}/ + - mkdir -p ./artifacts/${CI_PROJECT_NAME}/ - npm audit --json > /artifacts/${CI_PROJECT_NAME}/vulnerabilities.json - status_vulnerabilities='echo $?' - npm audit @@ -66,7 +66,7 @@ mongotest: - time npm install --no-optional - npm run grunt-cover - mkdir -p /artifacts/${CI_PROJECT_NAME}/ - - cp -r coverage /artifacts/${CI_PROJECT_NAME}/ + - cp -r coverage ./artifacts/${CI_PROJECT_NAME}/ artifacts: untracked: true expire_in: 2h @@ -76,7 +76,7 @@ mongotest: tags: - CEP_RUNNER_EE -postgrestest: +.postgrestest: stage: pre-build-test script: - echo 'Performing PostgreSQL Test' @@ -91,7 +91,7 @@ postgrestest: tags: - CEP_RUNNER_EE -oracletest: +.oracletest: image: $REGISTRY/debian-node-oracle-docker stage: pre-build-test script: From ae5226b30c0ab11401994b9a6de9707db609c7a7 Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Wed, 20 Mar 2019 10:22:49 +0530 Subject: [PATCH 29/36] Update .gitlab-ci.yml --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e9c55ae..8a13c24 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -39,7 +39,7 @@ npminstall: - echo "Node Modules Installed" - npm config rm registry - mkdir -p ./artifacts/${CI_PROJECT_NAME}/ - - npm audit --json > /artifacts/${CI_PROJECT_NAME}/vulnerabilities.json + - npm audit --json > ./artifacts/${CI_PROJECT_NAME}/vulnerabilities.json - status_vulnerabilities='echo $?' - npm audit - if [ $status_vulnerabilities == 1 ]; then exit_status=1 && echo "Dependency vulnerabilities exist"; fi @@ -65,7 +65,7 @@ mongotest: - npm config set registry http://10.188.25.62:9002/ - time npm install --no-optional - npm run grunt-cover - - mkdir -p /artifacts/${CI_PROJECT_NAME}/ + - mkdir -p ./artifacts/${CI_PROJECT_NAME}/ - cp -r coverage ./artifacts/${CI_PROJECT_NAME}/ artifacts: untracked: true From f3847fbacb3b0343011b16356f74101b53e840db Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Wed, 20 Mar 2019 10:43:18 +0530 Subject: [PATCH 30/36] modified gitlab-ci.yml file --- .gitlab-ci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8a13c24..b9434ab 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -76,7 +76,7 @@ mongotest: tags: - CEP_RUNNER_EE -.postgrestest: +postgrestest: stage: pre-build-test script: - echo 'Performing PostgreSQL Test' @@ -91,7 +91,7 @@ mongotest: tags: - CEP_RUNNER_EE -.oracletest: +oracletest: image: $REGISTRY/debian-node-oracle-docker stage: pre-build-test script: @@ -161,7 +161,8 @@ performancejob: - export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/) - while [ ${isStarted} -ne ${HTTP_RESPONSE_CODE} ]; do let countElapsed=countElapsed+1; echo ""; sleep 10; export isStarted=$(curl -k --write-out %{http_code} --output curl.out --silent https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/explorer/); echo -n "Waiting till the URL is up..."; echo ${isStarted}; if [ $countElapsed -eq 18 ] ; then export app_exit_status=1; export isStarted=${HTTP_RESPONSE_CODE}; fi; done - if [ $app_exit_status -eq 1 ]; then echo "App failed to start....."; docker stack ps ${APP_IMAGE_NAME}; docker service logs ${APP_IMAGE_NAME}_web; docker stack rm ${APP_IMAGE_NAME}; exit $app_exit_status; else echo "Your application URL is accessible @ https://${APP_IMAGE_NAME}.${PERF_DOMAIN_NAME}/"; fi - - mkdir -p /artifacts/${CI_PROJECT_NAME}/performance + - mkdir -p ./artifacts/${CI_PROJECT_NAME}/performance + - sh ./build-tools/performance/performance.sh - sh ./build-tools/push_artifacts.sh - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi when: manual From fe0ab9364d766ace6d14c4d92f02225a2861c697 Mon Sep 17 00:00:00 2001 From: ujwala sirigineedi Date: Wed, 20 Mar 2019 12:33:38 +0530 Subject: [PATCH 31/36] minor changes --- .gitlab-ci.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b9434ab..310d086 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,7 +49,7 @@ npminstall: expire_in: 2h paths: - node_modules/ - - /artifacts/ + - artifacts/ tags: - CEP_RUNNER_EE @@ -72,7 +72,7 @@ mongotest: expire_in: 2h paths: - coverage/ - - /artifacts/ + - artifacts/ tags: - CEP_RUNNER_EE @@ -166,12 +166,6 @@ performancejob: - sh ./build-tools/push_artifacts.sh - if docker stack rm ${APP_IMAGE_NAME}; then echo "stack removed"; else echo "nothing found in stack"; fi when: manual - artifacts: - untracked: true - expire_in: 2h - paths: - - node_modules/ - - /artifacts/ tags: - PERF_RUNNER From 147784eb724a11b95606c3d824ee583cc7cfc4fe Mon Sep 17 00:00:00 2001 From: vamsee Date: Thu, 20 Jun 2019 14:37:24 +0530 Subject: [PATCH 32/36] loopback updated to 3.26.0 and fixed lint issues --- .gitlab-ci.yml | 14 +++++++------- lib/load.js | 4 ++++ lib/loopback-boot-utility/index.js | 2 +- lib/loopback-datasource-juggler-wrapper/coerce.js | 2 ++ .../dao-wrapper.js | 2 ++ .../relation-definition.js | 4 ++++ package.json | 2 +- server/boot/db-models.js | 1 + 8 files changed, 22 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 310d086..822f83c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,7 +3,7 @@ stages: - pre-build-test - push-artifacts - performance - + image: $REGISTRY/evfoundation-executor-docker13:node8alpine36 variables: @@ -51,7 +51,7 @@ npminstall: - node_modules/ - artifacts/ tags: - - CEP_RUNNER_EE + - CEP_RUNNER mongotest: @@ -74,7 +74,7 @@ mongotest: - coverage/ - artifacts/ tags: - - CEP_RUNNER_EE + - CEP_RUNNER postgrestest: stage: pre-build-test @@ -89,8 +89,8 @@ postgrestest: - time npm install --no-optional - npm run grunt-cover tags: - - CEP_RUNNER_EE - + - CEP_RUNNER + oracletest: image: $REGISTRY/debian-node-oracle-docker stage: pre-build-test @@ -103,7 +103,7 @@ oracletest: - export ORACLE_SYSPASSWORD=oeadmin - export ORACLE_SID=ORCLCDB - export DB_NAME=${CI_PIPELINE_ID}_pg - - export ENABLE_DS_AUTOUPDATE=true + - export ENABLE_DS_AUTOUPDATE=true - npm config set registry http://10.188.25.62:9002/ - time npm install git+http://evgit/oecloud.io/oe-connector-oracle.git --no-optional - time npm install --no-optional @@ -116,7 +116,7 @@ oracletest: - echo "Oracle user details:"${ORACLE_USERNAME}"/"${ORACLE_PASSWORD} - npm run grunt-cover tags: - - CEP_RUNNER_EE + - CEP_RUNNER pushartifacts: stage: push-artifacts diff --git a/lib/load.js b/lib/load.js index 9d1c90c..48651de 100644 --- a/lib/load.js +++ b/lib/load.js @@ -31,6 +31,7 @@ function getRootFolder() { try { rootFolder = path.dirname(module.parent.parent.filename); } catch (e) { + // eslint-disable-next-line no-console console.error('**** ERROR : Not able to get root folder from parent module. ****', e); } @@ -38,6 +39,7 @@ function getRootFolder() { try { rootFolder = process.cwd() + '/server'; } catch (e) { + // eslint-disable-next-line no-console console.error('**** ERROR : Not able to get current working directory. ****', e); } } @@ -167,9 +169,11 @@ app.start = function () { return app.listen( () => { app.emit('started'); var baseUrl = app.get('url').replace(/\/$/, ''); + // eslint-disable-next-line no-console console.log('Web server listening at: %s', baseUrl); if (app.get('loopback-component-explorer')) { var explorerPath = app.get('loopback-component-explorer').mountPath; + // eslint-disable-next-line no-console console.log('Browse your REST API at %s%s', baseUrl, explorerPath); } }); diff --git a/lib/loopback-boot-utility/index.js b/lib/loopback-boot-utility/index.js index 4d83a4b..527bd56 100644 --- a/lib/loopback-boot-utility/index.js +++ b/lib/loopback-boot-utility/index.js @@ -5,7 +5,7 @@ function normalizeMixinName(str, normalization) { switch (normalization) { case false: case 'none': return str; - + // eslint-disable-next-line no-undefined case undefined: case 'classify': str = String(str).replace(/([A-Z]+)/g, ' $1').trim(); diff --git a/lib/loopback-datasource-juggler-wrapper/coerce.js b/lib/loopback-datasource-juggler-wrapper/coerce.js index 8e27332..e255daf 100644 --- a/lib/loopback-datasource-juggler-wrapper/coerce.js +++ b/lib/loopback-datasource-juggler-wrapper/coerce.js @@ -174,6 +174,7 @@ DataAccessObject._coerce = function (where, options) { } var val = where[p]; + // eslint-disable-next-line no-undefined if (val === null || val === undefined) { continue; } @@ -243,6 +244,7 @@ DataAccessObject._coerce = function (where, options) { // Coerce the array items if (Array.isArray(val)) { for (var i = 0; i < val.length; i++) { + // eslint-disable-next-line no-undefined if (val[i] !== null && val[i] !== undefined) { val[i] = DataType(val[i]); } diff --git a/lib/loopback-datasource-juggler-wrapper/dao-wrapper.js b/lib/loopback-datasource-juggler-wrapper/dao-wrapper.js index 00430ef..acd6f0c 100644 --- a/lib/loopback-datasource-juggler-wrapper/dao-wrapper.js +++ b/lib/loopback-datasource-juggler-wrapper/dao-wrapper.js @@ -15,6 +15,8 @@ const assert = require('assert'); const async = require('async'); require('./coerce'); +/* eslint-disable no-undefined */ + /* eslint-disable no-unused-vars */ // Atul : Keep this function for reference // function checkForOverrideAndCall(self, fn, args, optionIndex) { diff --git a/lib/loopback-datasource-juggler-wrapper/relation-definition.js b/lib/loopback-datasource-juggler-wrapper/relation-definition.js index a020e7f..8a2f225 100644 --- a/lib/loopback-datasource-juggler-wrapper/relation-definition.js +++ b/lib/loopback-datasource-juggler-wrapper/relation-definition.js @@ -12,6 +12,8 @@ const HasOne = require('loopback-datasource-juggler/lib/relation-definition').Ha const ModelBaseClass = require('loopback-datasource-juggler/lib/model.js'); const g = require('strong-globalize')(); +/* eslint-disable no-undefined */ + // Atul : Functions from relation-definition.js are overloaded. These functions are overriden so that // this.fetch() call, options can be passed. HasOne.prototype.update and destroy() overriden // default loopback-datasource-juggler doesn't pass options and hence it would crash @@ -39,6 +41,7 @@ HasOne.prototype.update = function (targetModelData, options, cb) { cb = cb || utils.createPromiseCallback(); var definition = this.definition; var fk = this.definition.keyTo; + // eslint-disable-next-line handle-callback-err this.fetch(null, options, function (err, targetModel) { if (targetModel instanceof ModelBaseClass) { // Ensures Foreign Key cannot be changed! @@ -60,6 +63,7 @@ HasOne.prototype.destroy = function (options, cb) { } cb = cb || utils.createPromiseCallback(); var definition = this.definition; + // eslint-disable-next-line handle-callback-err this.fetch(null, options, function (err, targetModel) { if (targetModel instanceof ModelBaseClass) { targetModel.destroy(options, cb); diff --git a/package.json b/package.json index 5738df5..d2555c8 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "compression": "1.7.3", "cookie-parser": "1.4.3", "lodash": "4.17.11", - "loopback": "3.22.3", + "loopback": "3.26.0", "loopback-boot": "2.27.1", "loopback-component-explorer": "5.4.0", "loopback-datasource-juggler": "3.24.0", diff --git a/server/boot/db-models.js b/server/boot/db-models.js index f7212f5..6efdfe5 100644 --- a/server/boot/db-models.js +++ b/server/boot/db-models.js @@ -18,6 +18,7 @@ function loadModelsFromDB(app, cb) { // design break when used fetchAllScopes modelDefinition.find({where: {filebased: false}}, {fetchAllScopes: true}, (err, result) => { if (err) { + // eslint-disable-next-line no-console console.error('******* Could not able to load models from Database ********* '); return cb(); } From 40da71056bcae8768ac28c6a83ae791e9e212c6c Mon Sep 17 00:00:00 2001 From: vamsee Date: Thu, 20 Jun 2019 15:50:00 +0530 Subject: [PATCH 33/36] Update grunt to 1.0.4 fix audit vulnerabilities --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index d2555c8..36a5d1e 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "chai-things": "0.2.0", "chalk": "1.1.1", "eslint": "4.10.0", - "grunt": "1.0.3", + "grunt": "1.0.4", "grunt-banner": "0.6.0", "grunt-cli": "1.3.2", "grunt-contrib-clean": "2.0.0", @@ -46,11 +46,11 @@ "grunt-mocha-test": "0.13.3", "istanbul": "0.4.5", "mocha": "5.2.0", - "superagent-defaults": "0.1.14", - "supertest": "3.4.2", - "oe-skeleton": "git+http://evgit/oecloud.io/oe-skeleton.git#master", "oe-connector-mongodb": "git+http://evgit/oecloud.io/oe-connector-mongodb.git#master", - "oe-connector-postgresql": "git+http://evgit/oecloud.io/oe-connector-postgresql.git#master" + "oe-connector-postgresql": "git+http://evgit/oecloud.io/oe-connector-postgresql.git#master", + "oe-skeleton": "git+http://evgit/oecloud.io/oe-skeleton.git#master", + "superagent-defaults": "0.1.14", + "supertest": "3.4.2" }, "author": "Atul Pandit ", "repository": { From cdae764116637087e11dc66bb71712b0efa98ab8 Mon Sep 17 00:00:00 2001 From: vamsee Date: Fri, 28 Jun 2019 18:05:28 +0530 Subject: [PATCH 34/36] Changed dependencies to 2.1.0 --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 36a5d1e..1bfe760 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oe-cloud", - "version": "2.0.0", + "version": "2.1.0", "description": "oe-cloud modularization aka oecloud.io", "engines": { "node": ">=6" @@ -23,7 +23,7 @@ "loopback-component-explorer": "5.4.0", "loopback-datasource-juggler": "3.24.0", "mustache": "2.3.2", - "oe-logger": "git+http://evgit/oecloud.io/oe-logger.git#master", + "oe-logger": "git+http://evgit/oecloud.io/oe-logger.git#2.1.0", "serve-favicon": "2.5.0", "serve-static": "1.13.2", "strong-error-handler": "2.3.2" @@ -46,9 +46,9 @@ "grunt-mocha-test": "0.13.3", "istanbul": "0.4.5", "mocha": "5.2.0", - "oe-connector-mongodb": "git+http://evgit/oecloud.io/oe-connector-mongodb.git#master", - "oe-connector-postgresql": "git+http://evgit/oecloud.io/oe-connector-postgresql.git#master", - "oe-skeleton": "git+http://evgit/oecloud.io/oe-skeleton.git#master", + "oe-connector-mongodb": "git+http://evgit/oecloud.io/oe-connector-mongodb.git#2.1.0", + "oe-connector-postgresql": "git+http://evgit/oecloud.io/oe-connector-postgresql.git#2.0.0", + "oe-skeleton": "git+http://evgit/oecloud.io/oe-skeleton.git#2.0.0", "superagent-defaults": "0.1.14", "supertest": "3.4.2" }, @@ -58,4 +58,4 @@ "url": "https://github.com/EdgeVerve/oe-cloud.git" }, "license": "MIT" -} +} \ No newline at end of file From f9e4ef95f36658040be6e5ccb69a37f6b1bf069f Mon Sep 17 00:00:00 2001 From: vamsee Date: Tue, 2 Jul 2019 17:50:03 +0530 Subject: [PATCH 35/36] Removed explorer and skeleton dependencies --- package.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/package.json b/package.json index 1bfe760..f254c19 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "lodash": "4.17.11", "loopback": "3.26.0", "loopback-boot": "2.27.1", - "loopback-component-explorer": "5.4.0", "loopback-datasource-juggler": "3.24.0", "mustache": "2.3.2", "oe-logger": "git+http://evgit/oecloud.io/oe-logger.git#2.1.0", @@ -48,7 +47,6 @@ "mocha": "5.2.0", "oe-connector-mongodb": "git+http://evgit/oecloud.io/oe-connector-mongodb.git#2.1.0", "oe-connector-postgresql": "git+http://evgit/oecloud.io/oe-connector-postgresql.git#2.0.0", - "oe-skeleton": "git+http://evgit/oecloud.io/oe-skeleton.git#2.0.0", "superagent-defaults": "0.1.14", "supertest": "3.4.2" }, @@ -58,4 +56,4 @@ "url": "https://github.com/EdgeVerve/oe-cloud.git" }, "license": "MIT" -} \ No newline at end of file +} From be4e183fd30c85622a415c65818efbacf0807e69 Mon Sep 17 00:00:00 2001 From: vamsee Date: Wed, 17 Jul 2019 17:33:36 +0530 Subject: [PATCH 36/36] Removing explorer from component-config.json --- .gitlab-ci.yml | 7 +++---- package.json | 3 ++- test/app-list.json | 4 ---- test/component-config.json | 3 --- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 822f83c..d00adfe 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -39,10 +39,9 @@ npminstall: - echo "Node Modules Installed" - npm config rm registry - mkdir -p ./artifacts/${CI_PROJECT_NAME}/ - - npm audit --json > ./artifacts/${CI_PROJECT_NAME}/vulnerabilities.json - - status_vulnerabilities='echo $?' - - npm audit - - if [ $status_vulnerabilities == 1 ]; then exit_status=1 && echo "Dependency vulnerabilities exist"; fi + - npm audit --json > ./artifacts/${CI_PROJECT_NAME}/vulnerabilities.json || true + - if npm audit; then status_vulnerabilities=0; else status_vulnerabilities=1; fi + - if [ $status_vulnerabilities == 1 ]; then echo "Dependency vulnerabilities exist"; fi - if [ $exit_status == 1 ]; then exit $exit_status; fi artifacts: untracked: true diff --git a/package.json b/package.json index f254c19..7c968a0 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "async": "2.6.1", "compression": "1.7.3", "cookie-parser": "1.4.3", - "lodash": "4.17.11", + "cors": "2.8.5", + "lodash": "4.17.14", "loopback": "3.26.0", "loopback-boot": "2.27.1", "loopback-datasource-juggler": "3.24.0", diff --git a/test/app-list.json b/test/app-list.json index 9bd4393..193d4b2 100644 --- a/test/app-list.json +++ b/test/app-list.json @@ -3,10 +3,6 @@ "path": "./", "forceLoad": "index.js" }, - { - "path": "oe-skeleton", - "enabled": true - }, { "path": "./", "enabled": true, diff --git a/test/component-config.json b/test/component-config.json index f36959a..2c63c08 100644 --- a/test/component-config.json +++ b/test/component-config.json @@ -1,5 +1,2 @@ { - "loopback-component-explorer": { - "mountPath": "/explorer" - } }