This commit is contained in:
Carlos Jimenez Ruiz 2019-02-12 13:33:21 +01:00
commit 8ef6db9017
8 changed files with 264 additions and 152 deletions

32
Jenkinsfile vendored
View File

@ -7,8 +7,8 @@ pipeline {
} }
environment { environment {
REGISTRY = 'registry.verdnatura.es' REGISTRY = 'registry.verdnatura.es'
DOCKER_HOST_1 = 'tcp://vch1.verdnatura.es:2376' DOCKER_HOST_1 = 'vch1.verdnatura.es'
DOCKER_HOST_2 = 'tcp://vch2.verdnatura.es:2376' DOCKER_HOST_2 = 'vch2.verdnatura.es'
TAG = "${env.BRANCH_NAME}" TAG = "${env.BRANCH_NAME}"
} }
stages { stages {
@ -44,15 +44,15 @@ pipeline {
echo "Committer: ${env.GIT_COMMITTER_EMAIL}" echo "Committer: ${env.GIT_COMMITTER_EMAIL}"
} }
} }
stage('Build') { stage('Install') {
environment {
NODE_ENV = ""
}
steps { steps {
nodejs('node-lts') { nodejs('node-lts') {
withEnv(['NODE_ENV=']) {
sh 'npm install --no-audit' sh 'npm install --no-audit'
sh 'gulp install' sh 'gulp install'
} }
sh 'gulp build'
}
} }
} }
stage('Test') { stage('Test') {
@ -62,18 +62,16 @@ pipeline {
environment { environment {
NODE_ENV = "" NODE_ENV = ""
FIREFOX_BIN = "/opt/firefox/firefox-bin" FIREFOX_BIN = "/opt/firefox/firefox-bin"
DB_HOST = "${env.DOCKER_HOST_2}"
} }
steps { steps {
nodejs('node-lts') { nodejs('node-lts') {
sh 'karma start --junit' sh 'karma start --junit'
sh 'gulp backTestDocker --junit --random --run-chown'
sh 'gulp docker'
// sh 'gulp backendUnitTest --junit' // FIXME: Docker isn't at localhost
sh 'docker rm -f salix-db'
} }
} }
} }
stage('Docker') { stage('Build') {
when { when {
not { branch 'dev' } not { branch 'dev' }
} }
@ -81,6 +79,10 @@ pipeline {
CREDS = credentials('docker-registry') CREDS = credentials('docker-registry')
} }
steps { steps {
nodejs('node-lts') {
sh 'gulp build'
}
sh 'docker login --username $CREDS_USR --password $CREDS_PSW $REGISTRY' sh 'docker login --username $CREDS_USR --password $CREDS_PSW $REGISTRY'
sh 'docker-compose build --parallel' sh 'docker-compose build --parallel'
sh 'docker-compose push' sh 'docker-compose push'
@ -96,7 +98,7 @@ pipeline {
parallel { parallel {
stage('Host 1') { stage('Host 1') {
environment { environment {
DOCKER_HOST = "${env.DOCKER_HOST_1}" DOCKER_HOST = "tcp://${env.DOCKER_HOST_1}:2376"
} }
steps { steps {
withCredentials([dockerCert(credentialsId: 'docker', variable: 'DOCKER_CERT_PATH')]) { withCredentials([dockerCert(credentialsId: 'docker', variable: 'DOCKER_CERT_PATH')]) {
@ -107,7 +109,7 @@ pipeline {
} }
stage('Host 2') { stage('Host 2') {
environment { environment {
DOCKER_HOST = "${env.DOCKER_HOST_2}" DOCKER_HOST = "tcp://${env.DOCKER_HOST_2}:2376"
} }
steps { steps {
withCredentials([dockerCert(credentialsId: 'docker', variable: 'DOCKER_CERT_PATH')]) { withCredentials([dockerCert(credentialsId: 'docker', variable: 'DOCKER_CERT_PATH')]) {
@ -130,8 +132,10 @@ pipeline {
post { post {
always { always {
script { script {
if (env.BRANCH_NAME == 'dev') if (env.BRANCH_NAME == 'dev') {
junit '*/junitresults.xml' junit '*/junitresults.xml'
junit 'junitresults.xml'
}
if (!env.GIT_COMMITTER_EMAIL) return if (!env.GIT_COMMITTER_EMAIL) return
try { try {

View File

@ -16,6 +16,13 @@ let langs = ['es', 'en'];
let srcDir = './front'; let srcDir = './front';
let modulesDir = './modules'; let modulesDir = './modules';
let buildDir = 'dist'; let buildDir = 'dist';
let containerId = 'salix-db';
let dataSources = require('./loopback/server/datasources.json');
let dbConf = dataSources.vn;
if (process.env.DB_HOST)
dbConf.host = process.env.DB_HOST;
let backSources = [ let backSources = [
'!node_modules', '!node_modules',
@ -67,43 +74,55 @@ defaultTask.description = `Starts all application services`;
// Backend tests // Backend tests
function backendUnitTest() { async function backTestOnly() {
let app = require(`./loopback/server/server`); let bootOptions;
let specFiles = [ if (argv['random'])
bootOptions = {dataSources};
let app = require(`./loopback/server/server`);
app.boot(bootOptions);
await new Promise((resolve, reject) => {
const jasmine = require('gulp-jasmine');
let options = {errorOnFail: false};
if (argv.junit) {
const reporters = require('jasmine-reporters');
options.reporter = new reporters.JUnitXmlReporter();
}
let backSpecFiles = [
'back/**/*.spec.js', 'back/**/*.spec.js',
'loopback/**/*.spec.js', 'loopback/**/*.spec.js',
'modules/*/back/**/*.spec.js' 'modules/*/back/**/*.spec.js'
]; ];
const jasmine = require('gulp-jasmine'); gulp.src(backSpecFiles)
let options = {errorOnFail: false};
if (argv.junit || argv.j) {
const reporters = require('jasmine-reporters');
options.reporter = new reporters.JUnitXmlReporter();
}
return gulp.src(specFiles)
.pipe(jasmine(options)) .pipe(jasmine(options))
.on('jasmineDone', function() { .on('end', resolve)
app.disconnect(); .resume();
}); });
}
backendUnitTest.description = `Runs the backend tests only, can receive args --junit or -j to save reports on a xml file`;
const dockerAndBackTest = gulp.series(docker, backendUnitTest); await app.disconnect();
dockerAndBackTest.description = `Restarts database and runs the backend tests`; }
backTestOnly.description = `Runs the backend tests only, can receive --junit arg to save reports on a xml file`;
async function backTestDocker() {
let containerId = await docker();
await backTestOnly();
if (argv['random'])
await execP(`docker rm -fv ${containerId}`);
}
backTestDocker.description = `Runs backend tests using in site container`;
function backTest(done) { function backTest(done) {
const nodemon = require('gulp-nodemon'); const nodemon = require('gulp-nodemon');
let gulpBin = isWindows
? 'node_modules/.bin/gulp.cmd'
: 'node_modules/.bin/gulp';
nodemon({ nodemon({
exec: gulpBin, exec: ['node ./node_modules/gulp/bin/gulp.js'],
args: ['backendUnitTest'], args: ['backTestOnly'],
watch: backSources, watch: backSources,
done: done done: done
}); });
@ -325,20 +344,35 @@ watch.description = `Watches for changes in routes and locale files`;
* to avoid a bug with OverlayFS driver on MacOS. * to avoid a bug with OverlayFS driver on MacOS.
*/ */
async function docker() { async function docker() {
try {
await execP('docker rm -fv salix-db');
} catch (e) {}
let d = new Date(); let d = new Date();
let pad = v => v < 10 ? '0' + v : v; let pad = v => v < 10 ? '0' + v : v;
let stamp = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`; let stamp = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
await execP(`docker build --build-arg STAMP=${stamp} -t salix-db ./services/db`); await execP(`docker build --build-arg STAMP=${stamp} -t salix-db ./services/db`);
let dockerArgs = `--name ${containerId} -p 3306:${dbConf.port}`;
if (argv['random'])
dockerArgs = '-p 3306';
else {
try {
await execP(`docker rm -fv ${containerId}`);
} catch (e) {}
}
let runChown = process.platform != 'linux'; let runChown = process.platform != 'linux';
if (argv['run-chown']) runChown = true; if (argv['run-chown']) runChown = true;
await execP(`docker run --env RUN_CHOWN=${runChown} -d --name salix-db -p 3306:3306 salix-db`); let result = await execP(`docker run --env RUN_CHOWN=${runChown} -d ${dockerArgs} salix-db`);
containerId = result.stdout;
if (argv['random']) {
let inspect = await execP(`docker inspect -f "{{json .NetworkSettings.Ports}}" ${containerId}`);
let ports = JSON.parse(inspect.stdout);
dbConf.port = ports['3306/tcp'][0]['HostPort'];
}
if (runChown) await dockerWait(); if (runChown) await dockerWait();
return containerId;
} }
docker.description = `Builds the database image and runs a container`; docker.description = `Builds the database image and runs a container`;
@ -351,7 +385,7 @@ docker.description = `Builds the database image and runs a container`;
async function dockerStart() { async function dockerStart() {
let state; let state;
try { try {
let result = await execP('docker container inspect -f "{{json .State}}" salix-db'); let result = await execP(`docker inspect -f "{{json .State}}" ${containerId}`);
state = JSON.parse(result.stdout); state = JSON.parse(result.stdout);
} catch (err) { } catch (err) {
return await docker(); return await docker();
@ -361,7 +395,7 @@ async function dockerStart() {
case 'running': case 'running':
return; return;
case 'exited': case 'exited':
await execP('docker start salix-db'); await execP(`docker start ${containerId}`);
await dockerWait(); await dockerWait();
return; return;
default: default:
@ -376,7 +410,14 @@ function dockerWait() {
let interval = 100; let interval = 100;
let elapsedTime = 0; let elapsedTime = 0;
let maxInterval = 5 * 60 * 1000; let maxInterval = 4 * 60 * 1000;
let myConf = {
user: dbConf.username,
password: dbConf.password,
host: dbConf.host,
port: dbConf.port
};
log('Waiting for MySQL init process...'); log('Waiting for MySQL init process...');
checker(); checker();
@ -386,7 +427,7 @@ function dockerWait() {
let state; let state;
try { try {
let result = await execP('docker container inspect -f "{{json .State}}" salix-db'); let result = await execP(`docker container inspect -f "{{json .State}}" ${containerId}`);
state = JSON.parse(result.stdout); state = JSON.parse(result.stdout);
} catch (err) { } catch (err) {
return reject(new Error(err.message)); return reject(new Error(err.message));
@ -395,18 +436,17 @@ function dockerWait() {
if (state.Status === 'exited') if (state.Status === 'exited')
return reject(new Error('Docker exited, please see the docker logs for more info')); return reject(new Error('Docker exited, please see the docker logs for more info'));
let conn = mysql.createConnection({ let conn = mysql.createConnection(myConf);
host: 'localhost',
user: 'root',
password: 'root'
});
conn.on('error', () => {}); conn.on('error', () => {});
conn.connect(err => { conn.connect(err => {
conn.destroy(); conn.destroy();
if (!err) return resolve(); if (!err) {
log('MySQL process ready.');
return resolve();
}
if (elapsedTime >= maxInterval) if (elapsedTime >= maxInterval)
reject(new Error(`MySQL not initialized whithin ${elapsedTime} secs`)); reject(new Error(`MySQL not initialized whithin ${elapsedTime / 1000} secs`));
else else
setTimeout(checker, interval); setTimeout(checker, interval);
}); });
@ -444,21 +484,21 @@ module.exports = {
back, back,
backOnly, backOnly,
backWatch, backWatch,
backendUnitTest,
dockerAndBackTest,
backTest, backTest,
e2eOnly, backTestOnly,
backTestDocker,
e2e, e2e,
smokesOnly, e2eOnly,
smokes, smokes,
install, smokesOnly,
i, i,
install,
build, build,
clean, clean,
webpack, webpack,
webpackDevServer, webpackDevServer,
locales,
routes, routes,
locales,
localesRoutes, localesRoutes,
watch, watch,
docker, docker,

View File

@ -34,73 +34,80 @@ let modulesDir = `${appDir}/modules`;
// Internationalization // Internationalization
if (fs.existsSync(localeDir)) { app.disconnect = async function() {
let promises = [];
for (let ds in this.dataSources)
promises.push(this.dataSources[ds].disconnect());
return await Promise.all(promises);
};
app.start = function(bootOptions, port, callback) {
let onListen = () => {
let baseUrl = this.get('url').replace(/\/$/, '');
let explorerPath = this.get('loopback-component-explorer').mountPath;
console.log(`Browse your REST API at: %s`, `${baseUrl}${explorerPath}`);
this.emit('started');
callback && callback();
};
this.boot(bootOptions, err => {
if (err) throw err;
let args = port ? [port, onListen] : [onListen];
return this.listen(...args);
});
};
app.boot = function(bootOptions, callback) {
// Internatinalization
if (fs.existsSync(localeDir)) {
i18n.configure({ i18n.configure({
directory: localeDir, directory: localeDir,
defaultLocale: 'es' defaultLocale: 'es'
}); });
app.use(i18n.init); this.use(i18n.init);
}
// Initialization
app.disconnect = async function() {
let promises = [];
for (let ds in app.dataSources)
promises.push(app.dataSources[ds].disconnect());
return await Promise.all(promises);
};
app.start = function(port) {
function onListen() {
let baseUrl = app.get('url').replace(/\/$/, '');
let explorerPath = app.get('loopback-component-explorer').mountPath;
console.log(`Browse your REST API at: %s`, `${baseUrl}${explorerPath}`);
app.emit('started');
} }
let args = port ? [port, onListen] : [onListen]; // Initialization
return app.listen(...args);
};
let config = require('./config.json'); let config = require('./config.json');
for (let key in config) for (let key in config)
app.set(key, config[key]); this.set(key, config[key]);
let modelConfigFiles = [ let modelConfigFiles = [
`${__dirname}/model-config.json` `${__dirname}/model-config.json`
]; ];
let modelSources = [ let modelSources = [
`loopback/common/models`, `loopback/common/models`,
`loopback/server/models`, `loopback/server/models`,
`${__dirname}/../common/models` `${__dirname}/../common/models`
]; ];
let mixinDirs = [ let mixinDirs = [
`loopback/common/mixins`, `loopback/common/mixins`,
`loopback/server/mixins`, `loopback/server/mixins`,
`${__dirname}/../common/mixins` `${__dirname}/../common/mixins`
]; ];
let bootDirs = [ let bootDirs = [
`${__dirname}/boot` `${__dirname}/boot`
]; ];
addPath(`${appDir}/back`); addPath(`${appDir}/back`);
let modules = fs.readdirSync(modulesDir); let modules = fs.readdirSync(modulesDir);
for (let mod of modules) for (let mod of modules)
addPath(`${modulesDir}/${mod}/back`); addPath(`${modulesDir}/${mod}/back`);
function addPath(path) { function addPath(path) {
modelConfigFiles.push(`${path}/model-config.json`); modelConfigFiles.push(`${path}/model-config.json`);
modelSources.push(`${path}/models`); modelSources.push(`${path}/models`);
mixinDirs.push(`${path}/mixins`); mixinDirs.push(`${path}/mixins`);
bootDirs.push(`${path}/boot`); bootDirs.push(`${path}/boot`);
} }
let models = {}; let models = {};
for (file of modelConfigFiles) { for (file of modelConfigFiles) {
if (fs.existsSync(file)) { if (fs.existsSync(file)) {
let fileModels = require(file); let fileModels = require(file);
for (let key in fileModels) { for (let key in fileModels) {
@ -109,9 +116,9 @@ for (file of modelConfigFiles) {
} }
Object.assign(models, fileModels); Object.assign(models, fileModels);
} }
} }
let bootOptions = { let myBootOptions = {
appRootDir: __dirname, appRootDir: __dirname,
appConfigRootDir: rootDir, appConfigRootDir: rootDir,
modelsRootDir: rootDir, modelsRootDir: rootDir,
@ -119,13 +126,14 @@ let bootOptions = {
modelSources: modelSources, modelSources: modelSources,
mixinDirs: mixinDirs, mixinDirs: mixinDirs,
bootDirs: bootDirs bootDirs: bootDirs
};
if (fs.existsSync(`/etc/salix`))
myBootOptions.dsRootDir = `/etc/salix`;
Object.assign(myBootOptions, bootOptions);
boot(this, myBootOptions, callback);
}; };
if (fs.existsSync(`/etc/salix`)) if (require.main === module)
bootOptions.dsRootDir = `/etc/salix`;
boot(app, bootOptions, function(err) {
if (err) throw err;
if (require.main === module)
app.start(); app.start();
});

View File

@ -70,8 +70,8 @@
</div> </div>
</div> </div>
<vn-confirm <vn-confirm
vn-id="confirm-dialog" vn-id="confirm-pickup-order"
on-response="$ctrl.returnDialog(response)" on-response="$ctrl.sendPickupOrder(response)"
question="Pickup order" question="Send Pickup order"
message="Do you want to send it directly?"> message="Are you sure you want to send it?">
</vn-confirm> </vn-confirm>

View File

@ -8,7 +8,8 @@ class Controller {
this.$translate = $translate; this.$translate = $translate;
this.vnApp = vnApp; this.vnApp = vnApp;
this.moreOptions = [ this.moreOptions = [
{callback: this.showConfirmDialog, name: 'Pickup order'} {callback: this.showPickupOrder, name: 'Show Pickup order'},
{callback: this.confirmPickupOrder, name: 'Send Pickup order'}
]; ];
} }
@ -47,15 +48,17 @@ class Controller {
callback.call(this); callback.call(this);
} }
showConfirmDialog() { showPickupOrder() {
this.$scope.confirmDialog.show();
}
returnDialog(response) {
if (response === 'CANCEL') {
let url = `/api/report/rpt-claim-pickup-order?claimFk=${this.claim.id}`; let url = `/api/report/rpt-claim-pickup-order?claimFk=${this.claim.id}`;
window.open(url); window.open(url);
} else if (response === 'ACCEPT') { }
confirmPickupOrder() {
this.$scope.confirmPickupOrder.show();
}
sendPickupOrder(response) {
if (response === 'ACCEPT') {
this.$http.post(`/api/email/claim-pickup-order`, {claimFk: this.claim.id}).then( this.$http.post(`/api/email/claim-pickup-order`, {claimFk: this.claim.id}).then(
() => this.vnApp.showMessage(this.$translate.instant('Notification sent!')) () => this.vnApp.showMessage(this.$translate.instant('Notification sent!'))
); );

View File

@ -0,0 +1,52 @@
import './index.js';
describe('Item Component vnClaimDescriptor', () => {
let $httpBackend;
let controller;
beforeEach(() => {
ngModule('claim');
});
beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => {
$httpBackend = _$httpBackend_;
controller = $componentController('vnClaimDescriptor');
controller.claim = {id: 2};
}));
describe('showPickupOrder()', () => {
it('should open a new window showing a pickup order PDF document', () => {
let expectedPath = '/api/report/rpt-claim-pickup-order?claimFk=2';
spyOn(window, 'open');
controller.showPickupOrder();
expect(window.open).toHaveBeenCalledWith(expectedPath);
});
});
describe('confirmPickupOrder()', () => {
it('should call confirmPickupOrder.show()', () => {
controller.$scope.confirmPickupOrder = {
show: jasmine.createSpy('show')
};
controller.claim = {id: 2};
controller.confirmPickupOrder();
expect(controller.$scope.confirmPickupOrder.show).toHaveBeenCalledWith();
});
});
describe('sendPickupOrder(response)', () => {
it('should make a query and call vnApp.showMessage() if the response is ACCEPT', () => {
spyOn(controller.vnApp, 'showMessage');
$httpBackend.when('POST', `/api/email/claim-pickup-order`, {claimFk: 2}).respond();
$httpBackend.expect('POST', `/api/email/claim-pickup-order`, {claimFk: 2}).respond();
controller.sendPickupOrder('ACCEPT');
$httpBackend.flush();
expect(controller.vnApp.showMessage).toHaveBeenCalledWith('Notification sent!');
});
});
});

View File

@ -1,5 +1,6 @@
#Ordenar alfabeticamente #Ordenar alfabeticamente
Add sale: Añadir linea Add sale: Añadir linea
Are you sure you want to send it?: ¿Seguro que quieres enviarlo?
Client Id: Id cliente Client Id: Id cliente
Claimed ticket: Ticket reclamado Claimed ticket: Ticket reclamado
Observation: Observación Observation: Observación
@ -7,5 +8,6 @@ Responsible: Responsable
Remove sale: Borrar linea Remove sale: Borrar linea
Claim Id: Id reclamación Claim Id: Id reclamación
Created: Creado Created: Creado
Pickup order: Orden de recogida Send Pickup order: Enviar orden de recogida
Do you want to send it directly?: ¿Quieres enviarlo directamente? Show Pickup order: Ver orden de recogida

View File

@ -17,6 +17,9 @@ export default class Controller {
if (!value) return; if (!value) return;
if (!value.bankEntityFk)
this.autofillBic();
this.newBankEntity = { this.newBankEntity = {
countryFk: Number.parseInt(value.countryFk) countryFk: Number.parseInt(value.countryFk)
}; };