diff --git a/back/methods/dms/download.js b/back/methods/dms/downloadFile.js
similarity index 77%
rename from back/methods/dms/download.js
rename to back/methods/dms/downloadFile.js
index 9211cc8f8..574ad6972 100644
--- a/back/methods/dms/download.js
+++ b/back/methods/dms/downloadFile.js
@@ -2,7 +2,7 @@ const UserError = require('vn-loopback/util/user-error');
const fs = require('fs-extra');
module.exports = Self => {
- Self.remoteMethodCtx('download', {
+ Self.remoteMethodCtx('downloadFile', {
description: 'Download a document',
accessType: 'READ',
accepts: [
@@ -29,15 +29,15 @@ module.exports = Self => {
}
],
http: {
- path: `/:id/download`,
+ path: `/:id/downloadFile`,
verb: 'GET'
}
});
- Self.download = async function(ctx, id) {
- const userId = ctx.req.accessToken.userId;
+ Self.downloadFile = async function(ctx, id) {
const env = process.env.NODE_ENV;
- const document = await Self.findById(id, {
+ const models = Self.app.models;
+ const dms = await Self.findById(id, {
include: {
relation: 'dmsType',
scope: {
@@ -48,18 +48,17 @@ module.exports = Self => {
}
}
});
- const readRole = document.dmsType().readRole().name;
- const hasRequiredRole = await Self.app.models.Account.hasRole(userId, readRole);
- if (!hasRequiredRole)
+ const hasReadRole = await models.DmsType.hasReadRole(ctx, dms.dmsTypeFk);
+ if (!hasReadRole)
throw new UserError(`You don't have enough privileges`);
if (env && env != 'development') {
- const path = `/${document.companyFk}/${document.dmsType().path}/${document.file}`;
+ const path = `/${dms.companyFk}/${dms.dmsType().path}/${dms.file}`;
file = {
path: `/var/lib/salix/dms/${path}`,
contentType: 'application/octet-stream',
- name: document.file
+ name: dms.file
};
} else {
file = {
diff --git a/back/methods/dms/removeFile.js b/back/methods/dms/removeFile.js
new file mode 100644
index 000000000..71d9cc2c8
--- /dev/null
+++ b/back/methods/dms/removeFile.js
@@ -0,0 +1,46 @@
+const UserError = require('vn-loopback/util/user-error');
+const fs = require('fs-extra');
+
+module.exports = Self => {
+ Self.remoteMethodCtx('removeFile', {
+ description: 'Makes a logical delete moving a file to a trash folder',
+ accessType: 'WRITE',
+ accepts: [{
+ arg: 'id',
+ type: 'Number',
+ description: 'The document id',
+ http: {source: 'path'}
+ }],
+ returns: {
+ type: 'Object',
+ root: true
+ },
+ http: {
+ path: `/:id/removeFile`,
+ verb: 'POST'
+ }
+ });
+
+ Self.removeFile = async(ctx, id) => {
+ const models = Self.app.models;
+ const dms = await models.Dms.findById(id);
+ const dmsType = await models.DmsType.findById(dms.dmsTypeFk);
+ const trashDmsType = await models.DmsType.findOne({
+ where: {
+ code: 'trash'
+ }
+ });
+
+ const hasWriteRole = await models.DmsType.hasWriteRole(ctx, dms.dmsTypeFk);
+ if (!hasWriteRole)
+ throw new UserError(`You don't have enough privileges`);
+
+ const file = await models.Container.getFile(dmsType.path, dms.file);
+ const originPath = `${file.client.root}/${dmsType.path}/${file.name}`;
+ const destinationPath = `${file.client.root}/${trashDmsType.path}/${file.name}`;
+
+ await fs.rename(originPath, destinationPath);
+
+ return dms.updateAttribute('dmsTypeFk', trashDmsType.id);
+ };
+};
diff --git a/back/methods/dms/specs/download.spec.js b/back/methods/dms/specs/downloadFile.spec.js
similarity index 69%
rename from back/methods/dms/specs/download.spec.js
rename to back/methods/dms/specs/downloadFile.spec.js
index f7f3eb630..ae8a4fe53 100644
--- a/back/methods/dms/specs/download.spec.js
+++ b/back/methods/dms/specs/downloadFile.spec.js
@@ -1,11 +1,11 @@
const app = require('vn-loopback/server/server');
-describe('dms download()', () => {
+describe('dms downloadFile()', () => {
let dmsId = 1;
it('should return a response for an employee with text content-type', async() => {
- let workerFk = 107;
- let ctx = {req: {accessToken: {userId: workerFk}}};
- const result = await app.models.Dms.download(ctx, dmsId);
+ let workerId = 107;
+ let ctx = {req: {accessToken: {userId: workerId}}};
+ const result = await app.models.Dms.downloadFile(ctx, dmsId);
expect(result[1]).toEqual('text/plain');
});
@@ -15,7 +15,7 @@ describe('dms download()', () => {
let ctx = {req: {accessToken: {userId: clientId}}};
let error;
- await app.models.Dms.download(ctx, dmsId).catch(e => {
+ await app.models.Dms.downloadFile(ctx, dmsId).catch(e => {
error = e;
}).finally(() => {
expect(error.message).toEqual(`You don't have enough privileges`);
diff --git a/back/methods/dms/specs/removeFile.spec.js b/back/methods/dms/specs/removeFile.spec.js
new file mode 100644
index 000000000..1006d1329
--- /dev/null
+++ b/back/methods/dms/specs/removeFile.spec.js
@@ -0,0 +1,19 @@
+const app = require('vn-loopback/server/server');
+
+describe('dms removeFile()', () => {
+ let dmsId = 1;
+
+ it(`should return an error for a user without enough privileges`, async() => {
+ let clientId = 101;
+ let ctx = {req: {accessToken: {userId: clientId}}};
+
+ let error;
+ await app.models.Dms.removeFile(ctx, dmsId).catch(e => {
+ error = e;
+ }).finally(() => {
+ expect(error.message).toEqual(`You don't have enough privileges`);
+ });
+
+ expect(error).toBeDefined();
+ });
+});
diff --git a/back/methods/dms/specs/uploadFile.spec.js b/back/methods/dms/specs/uploadFile.spec.js
new file mode 100644
index 000000000..afd70d068
--- /dev/null
+++ b/back/methods/dms/specs/uploadFile.spec.js
@@ -0,0 +1,18 @@
+const app = require('vn-loopback/server/server');
+
+describe('dms uploadFile()', () => {
+ it(`should return an error for a user without enough privileges`, async() => {
+ let clientId = 101;
+ let ticketDmsTypeId = 14;
+ let ctx = {req: {accessToken: {userId: clientId}}, args: {dmsTypeId: ticketDmsTypeId}};
+
+ let error;
+ await app.models.Dms.uploadFile(ctx).catch(e => {
+ error = e;
+ }).finally(() => {
+ expect(error.message).toEqual(`You don't have enough privileges`);
+ });
+
+ expect(error).toBeDefined();
+ });
+});
diff --git a/back/methods/dms/uploadFile.js b/back/methods/dms/uploadFile.js
new file mode 100644
index 000000000..1604a6afa
--- /dev/null
+++ b/back/methods/dms/uploadFile.js
@@ -0,0 +1,118 @@
+const fs = require('fs-extra');
+const UserError = require('vn-loopback/util/user-error');
+
+module.exports = Self => {
+ Self.remoteMethodCtx('uploadFile', {
+ description: 'Uploads a file and inserts into dms model',
+ accessType: 'WRITE',
+ accepts: [{
+ arg: 'options',
+ type: 'object'
+ },
+ {
+ arg: 'warehouseId',
+ type: 'Number',
+ description: ''
+ },
+ {
+ arg: 'companyId',
+ type: 'Number',
+ description: ''
+ },
+ {
+ arg: 'dmsTypeId',
+ type: 'Number',
+ description: ''
+ },
+ {
+ arg: 'reference',
+ type: 'String',
+ description: ''
+ },
+ {
+ arg: 'description',
+ type: 'String',
+ description: ''
+ },
+ {
+ arg: 'hasFile',
+ type: 'Boolean',
+ description: ''
+ }],
+ returns: {
+ type: 'Object',
+ root: true
+ },
+ http: {
+ path: `/uploadFile`,
+ verb: 'POST'
+ }
+ });
+
+ Self.uploadFile = async(ctx, options = {}) => {
+ const models = Self.app.models;
+ const storageConnector = Self.app.dataSources.storage.connector;
+ const myUserId = ctx.req.accessToken.userId;
+ const myWorker = await models.Worker.findOne({where: {userFk: myUserId}});
+ const args = ctx.args;
+ const fileOptions = {};
+ const hasParentTransaction = options && options.transaction;
+
+ if (!options.transaction)
+ options.transaction = await Self.beginTransaction({});
+
+ try {
+ const hasWriteRole = await models.DmsType.hasWriteRole(ctx, args.dmsTypeId);
+ if (!hasWriteRole)
+ throw new UserError(`You don't have enough privileges`);
+
+ const uploaded = await models.Container.upload('temp', ctx.req, ctx.result, fileOptions);
+ const files = Object.values(uploaded.files).map(file => {
+ return file[0];
+ });
+
+ const dmsType = await models.DmsType.findById(args.dmsTypeId);
+ const promises = [];
+
+ files.forEach(file => {
+ const newDms = Self.create({
+ workerFk: myWorker.id,
+ dmsTypeFk: args.dmsTypeId,
+ companyFk: args.companyId,
+ warehouseFk: args.warehouseId,
+ reference: args.reference,
+ description: args.description,
+ hasFile: args.hasFile
+ }, options).then(newDms => {
+ const extension = storageConnector.getFileExtension(file.name);
+ const fileName = `${newDms.id}.${extension}`;
+
+ return newDms.updateAttribute('file', fileName, options);
+ }).then(dms => {
+ return models.Container.getContainer('temp').then(container => {
+ const originPath = `${container.client.root}/${container.name}/${file.name}`;
+ const destinationPath = `${container.client.root}/${dmsType.path}/${dms.file}`;
+
+ return fs.rename(originPath, destinationPath).then(() => {
+ return dms;
+ });
+ });
+ });
+
+ promises.push(newDms);
+ });
+
+ const resolvedPromise = await Promise.all(promises);
+
+ if (!hasParentTransaction)
+ await options.transaction.commit();
+
+ return resolvedPromise;
+ } catch (e) {
+ if (!hasParentTransaction)
+ await options.transaction.rollback();
+
+ throw e;
+ }
+ };
+};
diff --git a/back/model-config.json b/back/model-config.json
index dc67a5422..a0c3d8740 100644
--- a/back/model-config.json
+++ b/back/model-config.json
@@ -43,6 +43,9 @@
},
"DmsType": {
"dataSource": "vn"
+ },
+ "Container": {
+ "dataSource": "storage"
}
}
diff --git a/back/models/container.json b/back/models/container.json
new file mode 100644
index 000000000..eca4c30c7
--- /dev/null
+++ b/back/models/container.json
@@ -0,0 +1,13 @@
+{
+ "name": "Container",
+ "base": "VnModel",
+ "idInjection": true,
+ "options": {
+ "validateUpsert": true
+ },
+ "properties": {},
+ "validations": [],
+ "relations": {},
+ "acls": [],
+ "methods": []
+ }
\ No newline at end of file
diff --git a/back/models/dms.js b/back/models/dms.js
index 97a405eef..d3471178b 100644
--- a/back/models/dms.js
+++ b/back/models/dms.js
@@ -1,3 +1,5 @@
module.exports = Self => {
- require('../methods/dms/download')(Self);
+ require('../methods/dms/downloadFile')(Self);
+ require('../methods/dms/uploadFile')(Self);
+ require('../methods/dms/removeFile')(Self);
};
diff --git a/back/models/dmsType.js b/back/models/dmsType.js
new file mode 100644
index 000000000..f76b095df
--- /dev/null
+++ b/back/models/dmsType.js
@@ -0,0 +1,62 @@
+module.exports = Self => {
+ /**
+ * Checks if current user has
+ * read privileges over a dms
+ *
+ * @param {Object} ctx - Request context
+ * @param {Interger} id - DmsType id
+ * @return {Boolean} True for user with read privileges
+ */
+ Self.hasReadRole = async(ctx, id) => {
+ const models = Self.app.models;
+ const dmsType = await models.DmsType.findById(id, {
+ include: {
+ relation: 'readRole'
+ }
+ });
+
+ return await hasRole(ctx, dmsType);
+ };
+
+ /**
+ * Checks if current user has
+ * write privileges over a dms
+ *
+ * @param {Object} ctx - Request context
+ * @param {Interger} id - DmsType id
+ * @return {Boolean} True for user with write privileges
+ */
+ Self.hasWriteRole = async(ctx, id) => {
+ const models = Self.app.models;
+ const dmsType = await models.DmsType.findById(id, {
+ include: {
+ relation: 'writeRole'
+ }
+ });
+
+ return await hasRole(ctx, dmsType);
+ };
+
+ /**
+ * Checks if current user has
+ * read or write privileges
+ * @param {Object} ctx - Context
+ * @param {Object} dmsType - Dms type [read/write]
+ */
+ async function hasRole(ctx, dmsType) {
+ const models = Self.app.models;
+ const myUserId = ctx.req.accessToken.userId;
+
+ const readRole = dmsType.readRole() && dmsType.readRole().name;
+ const writeRole = dmsType.writeRole() && dmsType.writeRole().name;
+ const requiredRole = readRole || writeRole;
+
+ const hasRequiredRole = await models.Account.hasRole(myUserId, requiredRole);
+ const isRoot = await models.Account.hasRole(myUserId, 'root');
+
+ if (isRoot || hasRequiredRole)
+ return true;
+
+ return false;
+ }
+};
diff --git a/db/changes/10050-pentecostes/00-ACL.sql b/db/changes/10050-pentecostes/00-ACL.sql
index ca05184c1..6934ccecb 100644
--- a/db/changes/10050-pentecostes/00-ACL.sql
+++ b/db/changes/10050-pentecostes/00-ACL.sql
@@ -1,3 +1,12 @@
-INSERT INTO `salix`.`ACL` ( `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ( 'ClientDms', 'remove', 'WRITE', 'ALLOW', 'ROLE', 'employee');
-INSERT INTO `salix`.`ACL` ( `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ( 'ClientDms', '*', 'READ', 'ALLOW', 'ROLE', 'employee');
-INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) VALUES ('Route', 'updateVolume', 'WRITE', 'ALLOW', 'ROLE', 'deliveryBoss');
+INSERT INTO `salix`.`ACL` ( `model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
+ VALUES
+ ('Dms', 'removeFile', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
+ ('Dms', 'uploadFile', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
+ ('Dms', 'downloadFile', 'READ', 'ALLOW', 'ROLE', 'employee'),
+ ('Client', 'uploadFile', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
+ ('ClientDms', 'removeFile', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
+ ('ClientDms', '*', 'READ', 'ALLOW', 'ROLE', 'employee'),
+ ('Ticket', 'uploadFile', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
+ ('TicketDms', 'removeFile', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
+ ('TicketDms', '*', 'READ', 'ALLOW', 'ROLE', 'employee'),
+ ('Route', 'updateVolume', 'WRITE', 'ALLOW', 'ROLE', 'deliveryBoss');
diff --git a/db/changes/10050-pentecostes/00-clientesgestdoc.sql b/db/changes/10050-pentecostes/00-clientesgestdoc.sql
index 3e7a023c9..113fd23e1 100644
--- a/db/changes/10050-pentecostes/00-clientesgestdoc.sql
+++ b/db/changes/10050-pentecostes/00-clientesgestdoc.sql
@@ -4,10 +4,6 @@ ALTER TABLE `vn2008`.`clientes_gestdoc`
DROP PRIMARY KEY,
ADD PRIMARY KEY (`gest_doc_id`);
-
-ALTER TABLE `vn2008`.`clientes_gestdoc`
-DROP INDEX `fk_clientes_gestdoc_1_idx` ;
-
ALTER TABLE `vn2008`.`clientes_gestdoc`
ADD INDEX `fk_clientes_gestdoc_1_idx` (`Id_Cliente` ASC);
diff --git a/db/changes/10050-pentecostes/00-dmsType.sql b/db/changes/10050-pentecostes/00-dmsType.sql
new file mode 100644
index 000000000..1692c6f50
--- /dev/null
+++ b/db/changes/10050-pentecostes/00-dmsType.sql
@@ -0,0 +1,4 @@
+UPDATE `vn2008`.`gesttip` SET `writeRoleFk`='1', `readRoleFk`='1' WHERE `id`='5';
+UPDATE `vn2008`.`gesttip` SET `writeRoleFk`='1', `readRoleFk`='1' WHERE `id`='12';
+UPDATE `vn2008`.`gesttip` SET `writeRoleFk`='1', `readRoleFk`='1' WHERE `id`='14';
+UPDATE `vn2008`.`gesttip` SET `writeRoleFk`='1', `readRoleFk`='1' WHERE `id`='13';
diff --git a/db/changes/10050-pentecostes/01-clienteesGestdoc.sql b/db/changes/10050-pentecostes/01-clienteesGestdoc.sql
deleted file mode 100644
index 294a39577..000000000
--- a/db/changes/10050-pentecostes/01-clienteesGestdoc.sql
+++ /dev/null
@@ -1,9 +0,0 @@
-ALTER TABLE `vn2008`.`clientes_gestdoc`
-ADD INDEX `fk_clientes_gestdoc_1_idx` (`Id_Cliente` ASC);
-
-ALTER TABLE `vn2008`.`clientes_gestdoc`
-ADD CONSTRAINT `fk_clientes_gestdoc_3`
- FOREIGN KEY (`Id_Cliente`)
- REFERENCES `vn2008`.`Clientes` (`id_cliente`)
- ON DELETE RESTRICT
- ON UPDATE CASCADE;
\ No newline at end of file
diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index d23da60f4..a6e6f14a0 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -1439,16 +1439,16 @@ INSERT INTO `vn`.`dmsType`(`id`, `name`, `path`, `readRoleFk`, `writeRoleFk`, `c
(2, 'Doc oficial', 'oficial', NULL, NULL, 'officialDoc'),
(3, 'Laboral', 'laboral', NULL, NULL, 'hhrrData'),
(4, 'Albaranes recibidos', 'entradas', NULL, NULL, 'deliveryNote'),
- (5, 'Otros', 'otros', 1, NULL, 'miscellaneous'),
+ (5, 'Otros', 'otros', 1, 1, 'miscellaneous'),
(6, 'Pruebas', 'pruebas', NULL, NULL, 'tests'),
(7, 'IAE Clientes', 'IAE_Clientes', NULL, NULL, 'economicActivitiesTax'),
(8, 'Fiscal', 'fiscal', NULL, NULL, 'fiscal'),
(9, 'Vehiculos', 'vehiculos', NULL, NULL, 'vehicles'),
(10, 'Plantillas', 'plantillas', NULL, NULL, 'templates'),
(11, 'Contratos', 'contratos', NULL, NULL, 'contracts'),
- (12, 'ley de pagos', 'ley pagos', NULL, NULL, 'paymentsLaw'),
- (13, 'Basura', 'basura', NULL, NULL, 'trash'),
- (14, 'Ticket', 'tickets', 1, NULL, 'ticket'),
+ (12, 'ley de pagos', 'ley pagos', 1, 1, 'paymentsLaw'),
+ (13, 'Basura', 'basura', 1, 1, 'trash'),
+ (14, 'Ticket', 'tickets', 1, 1, 'ticket'),
(15, 'Presupuestos', 'Presupuestos', NULL, NULL, 'budgets'),
(16, 'Logistica', 'logistica', NULL, NULL, 'logistics'),
(17, 'cmr', 'cmr', NULL, NULL, 'cmr'),
diff --git a/front/core/components/check/style.scss b/front/core/components/check/style.scss
index 122c3e659..ade79c4c5 100644
--- a/front/core/components/check/style.scss
+++ b/front/core/components/check/style.scss
@@ -16,7 +16,6 @@ vn-check {
}
md-checkbox {
- margin-bottom: 0.8em;
- width: 20px;
+ margin-bottom: 0.8em
}
}
diff --git a/front/core/components/index.js b/front/core/components/index.js
index 9c8ae2578..d626470c3 100644
--- a/front/core/components/index.js
+++ b/front/core/components/index.js
@@ -41,6 +41,7 @@ import './chip';
import './color-legend';
import './input-number';
import './input-time';
+import './input-file';
import './treeview';
import './treeview/child';
import './calendar';
diff --git a/front/core/components/input-file/index.html b/front/core/components/input-file/index.html
new file mode 100644
index 000000000..59cfcff7d
--- /dev/null
+++ b/front/core/components/input-file/index.html
@@ -0,0 +1,39 @@
+
');
+ controller = $componentController('vnInputFile', {$element, $scope, $attrs, $timeout, $transclude: () => {}});
+ controller.input = $element[0].querySelector('input');
+ controller.validate = () => {};
+ }));
+
+ describe('files() setter', () => {
+ it(`should set a value, and then add the class 'not-empty'`, () => {
+ controller.files = [{name: 'MyFile'}];
+
+ let classes = controller.element.classList.toString();
+
+ expect(classes).toContain('not-empty');
+ });
+
+ it(`should set an empty value, and then remove the class 'not-empty'`, () => {
+ controller.files = null;
+
+ let classes = controller.element.classList.toString();
+
+ expect(classes).not.toContain('not-empty');
+ });
+ });
+});
diff --git a/front/core/components/input-file/style.scss b/front/core/components/input-file/style.scss
new file mode 100644
index 000000000..83a66a262
--- /dev/null
+++ b/front/core/components/input-file/style.scss
@@ -0,0 +1,15 @@
+@import "variables";
+@import '../textfield/style.scss';
+
+vn-input-file {
+ @extend vn-textfield;
+ .value {
+ color: $color-font-secondary;
+ cursor: pointer;
+ padding: 4px 0;
+ outline: 0
+ }
+ input {
+ display: none !important
+ }
+}
\ No newline at end of file
diff --git a/front/core/components/input-number/index.js b/front/core/components/input-number/index.js
index 6c32ebb98..7eb8cbd54 100644
--- a/front/core/components/input-number/index.js
+++ b/front/core/components/input-number/index.js
@@ -184,6 +184,10 @@ InputNumber.$inject = ['$element', '$scope', '$attrs', 'vnTemplate'];
ngModule.component('vnInputNumber', {
template: require('./index.html'),
controller: InputNumber,
+ transclude: {
+ leftIcons: '?tLeftIcons',
+ rightIcons: '?tRightIcons'
+ },
bindings: {
label: '@?',
name: '@?',
diff --git a/front/core/components/multi-check/multi-check.js b/front/core/components/multi-check/multi-check.js
index 9eb9a5148..1e5a1c569 100644
--- a/front/core/components/multi-check/multi-check.js
+++ b/front/core/components/multi-check/multi-check.js
@@ -1,5 +1,6 @@
import ngModule from '../../module';
import Input from '../../lib/input';
+import './style.scss';
/**
* Draw checkbox with a drop-down and multi options
diff --git a/front/core/components/multi-check/style.scss b/front/core/components/multi-check/style.scss
new file mode 100644
index 000000000..5ae5f4f0b
--- /dev/null
+++ b/front/core/components/multi-check/style.scss
@@ -0,0 +1,9 @@
+vn-multi-check {
+ md-checkbox {
+ margin-bottom: 0.8em;
+
+ .md-label {
+ margin: 0
+ }
+ }
+}
\ No newline at end of file
diff --git a/front/core/components/textfield/style.scss b/front/core/components/textfield/style.scss
index 36792ef90..3ec4134bc 100644
--- a/front/core/components/textfield/style.scss
+++ b/front/core/components/textfield/style.scss
@@ -29,6 +29,10 @@ vn-textfield {
}
}
+ .suffix vn-icon-button {
+ padding: 0
+ }
+
t-left-icons {
padding-right: 0.5em
}
@@ -47,6 +51,7 @@ vn-textfield {
i.clear {
visibility: hidden;
cursor: pointer;
+ outline: 0;
&:hover {
color: #222;
diff --git a/front/core/locale/es.yml b/front/core/locale/es.yml
index bdf42192f..ee57de376 100644
--- a/front/core/locale/es.yml
+++ b/front/core/locale/es.yml
@@ -49,4 +49,5 @@ Loading: Cargando
Fields to show: Campos a mostrar
Create new one: Crear nuevo
Toggle: Desplegar/Plegar
-Check all: Seleccionar todo
\ No newline at end of file
+Check all: Seleccionar todo
+Select a file: Selecciona un fichero
\ No newline at end of file
diff --git a/loopback/server/boot/root.js b/loopback/server/boot/root.js
index 7a4563f61..7a5591571 100644
--- a/loopback/server/boot/root.js
+++ b/loopback/server/boot/root.js
@@ -1,3 +1,4 @@
+const uuid = require('uuid/v1');
module.exports = function(app) {
let models = app.models();
@@ -27,9 +28,19 @@ module.exports = function(app) {
app.enableAuth();
+ // eslint-disable-next-line new-cap
let router = app.loopback.Router();
router.get('/status', app.loopback.status());
app.use(router);
+
+ const storageConnector = app.dataSources.storage.connector;
+ storageConnector.getFilename = function(file) {
+ return `${uuid()}.${storageConnector.getFileExtension(file.name)}`;
+ };
+
+ storageConnector.getFileExtension = function(fileName) {
+ return fileName.split('.').pop();
+ };
/*
let ds = app.dataSources.auth;
//ds.automigrate(function() {
diff --git a/loopback/server/datasources.json b/loopback/server/datasources.json
index 401b9483d..2a64532cc 100644
--- a/loopback/server/datasources.json
+++ b/loopback/server/datasources.json
@@ -14,5 +14,20 @@
"multipleStatements": true,
"connectTimeout": 20000,
"acquireTimeout": 20000
+ },
+ "storage": {
+ "name": "storage",
+ "connector": "loopback-component-storage",
+ "provider": "filesystem",
+ "root": "/mnt/storage/dms",
+ "maxFileSize": "10485760",
+ "allowedContentTypes": [
+ "application/pdf",
+ "application/zip",
+ "application/rar",
+ "image/png",
+ "image/jpeg",
+ "image/jpg"
+ ]
}
}
diff --git a/modules/client/back/methods/client-dms/removeFile.js b/modules/client/back/methods/client-dms/removeFile.js
new file mode 100644
index 000000000..96110bc11
--- /dev/null
+++ b/modules/client/back/methods/client-dms/removeFile.js
@@ -0,0 +1,33 @@
+module.exports = Self => {
+ Self.remoteMethodCtx('removeFile', {
+ description: 'Removes a client document',
+ accessType: 'WRITE',
+ accepts: {
+ arg: 'id',
+ type: 'Number',
+ description: 'The document id',
+ http: {source: 'path'}
+ },
+ returns: {
+ type: 'Object',
+ root: true
+ },
+ http: {
+ path: `/:id/removeFile`,
+ verb: 'POST'
+ }
+ });
+
+ Self.removeFile = async(ctx, id) => {
+ const models = Self.app.models;
+ const targetClientDms = await models.ClientDms.findById(id);
+ const targetDms = await models.Dms.findById(targetClientDms.dmsFk);
+ const trashDmsType = await models.DmsType.findOne({where: {code: 'trash'}});
+
+ await models.Dms.removeFile(ctx, targetClientDms.dmsFk);
+ await targetClientDms.destroy();
+
+ return targetDms.updateAttribute('dmsTypeFk', trashDmsType.id);
+ };
+};
+
diff --git a/modules/client/back/methods/client-dms/specs/removeFile.spec.js b/modules/client/back/methods/client-dms/specs/removeFile.spec.js
new file mode 100644
index 000000000..01cf1977b
--- /dev/null
+++ b/modules/client/back/methods/client-dms/specs/removeFile.spec.js
@@ -0,0 +1,18 @@
+const app = require('vn-loopback/server/server');
+
+describe('ClientDms removeFile()', () => {
+ const clientDmsFk = 3;
+ it(`should return an error for a user without enough privileges`, async() => {
+ let clientId = 101;
+ let ctx = {req: {accessToken: {userId: clientId}}};
+
+ let error;
+ await app.models.ClientDms.removeFile(ctx, clientDmsFk).catch(e => {
+ error = e;
+ }).finally(() => {
+ expect(error.message).toEqual(`You don't have enough privileges`);
+ });
+
+ expect(error).toBeDefined();
+ });
+});
diff --git a/modules/client/back/methods/client/specs/uploadFile.spec.js b/modules/client/back/methods/client/specs/uploadFile.spec.js
new file mode 100644
index 000000000..15110731e
--- /dev/null
+++ b/modules/client/back/methods/client/specs/uploadFile.spec.js
@@ -0,0 +1,19 @@
+const app = require('vn-loopback/server/server');
+
+describe('Client uploadFile()', () => {
+ it(`should return an error for a user without enough privileges`, async() => {
+ let clientId = 101;
+ let currentUserId = 102;
+ let paymentLawTypeId = 12;
+ let ctx = {req: {accessToken: {userId: currentUserId}}, args: {dmsTypeId: paymentLawTypeId}};
+
+ let error;
+ await app.models.Client.uploadFile(ctx, clientId).catch(e => {
+ error = e;
+ }).finally(() => {
+ expect(error.message).toEqual(`You don't have enough privileges`);
+ });
+
+ expect(error).toBeDefined();
+ });
+});
diff --git a/modules/client/back/methods/client/uploadFile.js b/modules/client/back/methods/client/uploadFile.js
new file mode 100644
index 000000000..7187d684f
--- /dev/null
+++ b/modules/client/back/methods/client/uploadFile.js
@@ -0,0 +1,77 @@
+module.exports = Self => {
+ Self.remoteMethodCtx('uploadFile', {
+ description: 'Upload and attach a file to a client',
+ accessType: 'WRITE',
+ accepts: [{
+ arg: 'id',
+ type: 'Number',
+ description: 'The client id',
+ http: {source: 'path'}
+ },
+ {
+ arg: 'warehouseId',
+ type: 'Number',
+ description: ''
+ },
+ {
+ arg: 'companyId',
+ type: 'Number',
+ description: ''
+ },
+ {
+ arg: 'dmsTypeId',
+ type: 'Number',
+ description: ''
+ },
+ {
+ arg: 'reference',
+ type: 'String',
+ description: ''
+ },
+ {
+ arg: 'description',
+ type: 'String',
+ description: ''
+ },
+ {
+ arg: 'hasFile',
+ type: 'Boolean',
+ description: ''
+ }],
+ returns: {
+ type: 'Object',
+ root: true
+ },
+ http: {
+ path: `/:id/uploadFile`,
+ verb: 'POST'
+ }
+ });
+
+ Self.uploadFile = async(ctx, id) => {
+ const models = Self.app.models;
+ const transaction = await Self.beginTransaction({});
+ const options = {transaction};
+ const promises = [];
+
+ try {
+ const uploadedFiles = await models.Dms.uploadFile(ctx, options);
+ uploadedFiles.forEach(dms => {
+ const newClientDms = models.ClientDms.create({
+ clientFk: id,
+ dmsFk: dms.id
+ }, options);
+
+ promises.push(newClientDms);
+ });
+ const resolvedPromises = await Promise.all(promises);
+
+ await transaction.commit();
+
+ return resolvedPromises;
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ };
+};
diff --git a/modules/client/back/methods/dms/removes.js b/modules/client/back/methods/dms/removes.js
deleted file mode 100644
index c3aeeed52..000000000
--- a/modules/client/back/methods/dms/removes.js
+++ /dev/null
@@ -1,35 +0,0 @@
-const UserError = require('vn-loopback/util/user-error');
-
-module.exports = Self => {
- Self.remoteMethod('removes', {
- description: 'Delete an client dms',
- accessType: 'WRITE',
- accepts: {
- arg: 'dmsId',
- type: 'number',
- required: true,
- description: 'dms identifier',
- },
- returns: {
- type: 'string',
- root: true
- },
- http: {
- path: `/removes`,
- verb: 'POST'
- }
- });
-
- Self.removes = async dmsId => {
- if (!dmsId)
- throw new UserError('There is nothing to delete');
-
- let targetClientDms = await Self.app.models.ClientDms.findOne({where: {dmsFk: dmsId}});
- let targetDms = await Self.app.models.Dms.findById(dmsId);
- let trashDmsType = await Self.app.models.DmsType.findOne({where: {code: 'trash'}});
-
- await targetClientDms.destroy();
- await targetDms.updateAttribute('dmsTypeFk', trashDmsType.id);
- };
-};
-
diff --git a/modules/client/back/methods/dms/removes.spec.js b/modules/client/back/methods/dms/removes.spec.js
deleted file mode 100644
index a5d653f7e..000000000
--- a/modules/client/back/methods/dms/removes.spec.js
+++ /dev/null
@@ -1,29 +0,0 @@
-const app = require('vn-loopback/server/server');
-
-describe('Client dms stuff', () => {
- let dmsToRestore;
- let dmsTypeToRestore;
- afterAll(async done => {
- await app.models.ClientDms.create(dmsToRestore);
- await dmsTypeToRestore.save();
-
- done();
- });
-
-
- it('should delete a dms from a client and update the dmsType to trash', async() => {
- const dmsId = 2;
-
- dmsToRestore = await app.models.ClientDms.findOne({where: {dmsFk: dmsId}});
- dmsTypeToRestore = await app.models.Dms.findById(dmsToRestore.dmsFk);
-
- await app.models.ClientDms.removes(dmsId);
-
- let destroyedDms = await app.models.ClientDms.findOne({where: {dmsFk: dmsId}});
-
- let alteredDmsType = await app.models.Dms.findById(dmsId);
-
- expect(destroyedDms).toBeNull();
- expect(alteredDmsType.dmsTypeFk).toEqual(13);
- });
-});
diff --git a/modules/client/back/models/client-dms.js b/modules/client/back/models/client-dms.js
index ef74b3c39..9e5da9132 100644
--- a/modules/client/back/models/client-dms.js
+++ b/modules/client/back/models/client-dms.js
@@ -1,3 +1,3 @@
module.exports = Self => {
- require('../methods/dms/removes')(Self);
+ require('../methods/client-dms/removeFile')(Self);
};
diff --git a/modules/client/back/models/client.js b/modules/client/back/models/client.js
index 66c875aa5..dc60d3111 100644
--- a/modules/client/back/models/client.js
+++ b/modules/client/back/models/client.js
@@ -20,6 +20,7 @@ module.exports = Self => {
require('../methods/client/getTransactions')(Self);
require('../methods/client/confirmTransaction')(Self);
require('../methods/client/canBeInvoiced')(Self);
+ require('../methods/client/uploadFile')(Self);
// Validations
diff --git a/modules/client/front/dms/create/index.html b/modules/client/front/dms/create/index.html
new file mode 100644
index 000000000..3fa96a9da
--- /dev/null
+++ b/modules/client/front/dms/create/index.html
@@ -0,0 +1,64 @@
+
+
+
+
diff --git a/modules/client/front/dms/create/index.js b/modules/client/front/dms/create/index.js
new file mode 100644
index 000000000..a3b32228f
--- /dev/null
+++ b/modules/client/front/dms/create/index.js
@@ -0,0 +1,97 @@
+import ngModule from '../../module';
+import './style.scss';
+
+class Controller {
+ constructor($scope, $http, $state, $translate, vnApp) {
+ this.$ = $scope;
+ this.$http = $http;
+ this.$state = $state;
+ this.$translate = $translate;
+ this.vnApp = vnApp;
+ this.dms = {
+ files: [],
+ hasFile: false
+ };
+ }
+
+ get client() {
+ return this._client;
+ }
+
+ set client(value) {
+ this._client = value;
+
+ if (value)
+ this.setDefaultParams();
+ }
+
+ setDefaultParams() {
+ const params = {filter: {
+ where: {code: 'paymentsLaw'}
+ }};
+ this.$http.get('/api/DmsTypes/findOne', {params}).then(res => {
+ const dmsType = res.data && res.data;
+ const companyId = window.localStorage.defaultCompanyFk;
+ const warehouseId = window.localStorage.defaultWarehouseFk;
+ const defaultParams = {
+ reference: this.client.id,
+ warehouseId: warehouseId,
+ companyId: companyId,
+ dmsTypeId: dmsType.id,
+ description: this.$translate.instant('ClientFileDescription', {
+ dmsTypeName: dmsType.name,
+ clientId: this.client.id,
+ clientName: this.client.name
+ }).toUpperCase()
+ };
+
+ this.dms = Object.assign(this.dms, defaultParams);
+ });
+ }
+
+ onSubmit() {
+ const query = `/api/clients/${this.client.id}/uploadFile`;
+ const options = {
+ method: 'POST',
+ url: query,
+ params: this.dms,
+ headers: {
+ 'Content-Type': undefined
+ },
+ transformRequest: files => {
+ const formData = new FormData();
+
+ for (let i = 0; i < files.length; i++)
+ formData.append(files[i].name, files[i]);
+
+ return formData;
+ },
+ data: this.dms.files
+ };
+ this.$http(options).then(res => {
+ if (res) {
+ this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
+ this.$.watcher.updateOriginalData();
+ this.$state.go('client.card.dms.index');
+ }
+ });
+ }
+
+ onFileChange(files) {
+ if (files.length > 0) {
+ this.$.$applyAsync(() => {
+ this.dms.hasFile = true;
+ });
+ }
+ }
+}
+
+Controller.$inject = ['$scope', '$http', '$state', '$translate', 'vnApp'];
+
+ngModule.component('vnClientDmsCreate', {
+ template: require('./index.html'),
+ controller: Controller,
+ bindings: {
+ client: '<'
+ }
+});
diff --git a/modules/client/front/dms/create/index.spec.js b/modules/client/front/dms/create/index.spec.js
new file mode 100644
index 000000000..2825d4d25
--- /dev/null
+++ b/modules/client/front/dms/create/index.spec.js
@@ -0,0 +1,60 @@
+import './index';
+
+describe('Client', () => {
+ describe('Component vnClientDmsCreate', () => {
+ let controller;
+ let $scope;
+ let $httpBackend;
+ let $httpParamSerializer;
+
+ beforeEach(ngModule('client'));
+
+ beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => {
+ $scope = $rootScope.$new();
+ $httpBackend = _$httpBackend_;
+ $httpParamSerializer = _$httpParamSerializer_;
+ controller = $componentController('vnClientDmsCreate', {$scope});
+ controller._client = {id: 101, name: 'Bruce wayne'};
+ }));
+
+ describe('client() setter', () => {
+ it('should set the client data and then call setDefaultParams()', () => {
+ spyOn(controller, 'setDefaultParams');
+ controller.client = {
+ id: 15,
+ name: 'Bruce wayne'
+ };
+
+ expect(controller.client).toBeDefined();
+ expect(controller.setDefaultParams).toHaveBeenCalledWith();
+ });
+ });
+
+ describe('setDefaultParams()', () => {
+ it('should perform a GET query and define the dms property on controller', () => {
+ const params = {filter: {
+ where: {code: 'paymentsLaw'}
+ }};
+ let serializedParams = $httpParamSerializer(params);
+ $httpBackend.when('GET', `/api/DmsTypes/findOne?${serializedParams}`).respond({id: 12, code: 'paymentsLaw'});
+ $httpBackend.expect('GET', `/api/DmsTypes/findOne?${serializedParams}`);
+ controller.setDefaultParams();
+ $httpBackend.flush();
+
+ expect(controller.dms).toBeDefined();
+ expect(controller.dms.reference).toEqual(101);
+ expect(controller.dms.dmsTypeId).toEqual(12);
+ });
+ });
+
+ describe('onFileChange()', () => {
+ it('should set dms hasFile property to true if has any files', () => {
+ const files = [{id: 1, name: 'MyFile'}];
+ controller.onFileChange(files);
+ $scope.$apply();
+
+ expect(controller.dms.hasFile).toBeTruthy();
+ });
+ });
+ });
+});
diff --git a/modules/client/front/dms/create/locale/en.yml b/modules/client/front/dms/create/locale/en.yml
new file mode 100644
index 000000000..56f6a658b
--- /dev/null
+++ b/modules/client/front/dms/create/locale/en.yml
@@ -0,0 +1 @@
+ClientFileDescription: "{{dmsTypeName}} from client {{clientName}} id {{clientId}}"
\ No newline at end of file
diff --git a/modules/client/front/dms/create/locale/es.yml b/modules/client/front/dms/create/locale/es.yml
new file mode 100644
index 000000000..2de9e68d3
--- /dev/null
+++ b/modules/client/front/dms/create/locale/es.yml
@@ -0,0 +1,5 @@
+Upload file: Subir fichero
+Upload: Subir
+File: Fichero
+ClientFileDescription: "{{dmsTypeName}} del cliente {{clientName}} id {{clientId}}"
+Attached file: Fichero adjunto
\ No newline at end of file
diff --git a/modules/client/front/dms/create/style.scss b/modules/client/front/dms/create/style.scss
new file mode 100644
index 000000000..b47544b12
--- /dev/null
+++ b/modules/client/front/dms/create/style.scss
@@ -0,0 +1,7 @@
+vn-ticket-request {
+ vn-textfield {
+ margin: 0!important;
+ max-width: 100px;
+ }
+}
+
diff --git a/modules/client/front/dms/index.html b/modules/client/front/dms/index.html
deleted file mode 100644
index df8bcfcb6..000000000
--- a/modules/client/front/dms/index.html
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
- File
- Description
- Reference
- Hard copy
- Worker
- Created
-
-
-
-
-
-
- {{::document.dmsFk}}
-
- {{::document.dms.description}}
-
- {{::document.dms.reference}}
- {{::document.dms.hardCopyNumber | dashIfEmpty}}
-
-
- {{::document.dms.worker.user.nickname | dashIfEmpty}}
-
-
- {{::document.dms.created | dateTime:'dd/MM/yyyy HH:mm'}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/modules/client/front/dms/index/index.html b/modules/client/front/dms/index/index.html
new file mode 100644
index 000000000..40f01c4e0
--- /dev/null
+++ b/modules/client/front/dms/index/index.html
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+ Id
+ Type
+ Reference
+ Description
+ Attached file
+ File
+ Employee
+ Created
+
+
+
+
+
+ {{::document.dmsFk}}
+
+
+ {{::document.dms.dmsType.name}}
+
+
+
+
+ {{::document.dms.reference}}
+
+
+
+
+ {{::document.dms.description}}
+
+
+
+
+
+
+ {{::document.dms.file}}
+
+
+ {{::document.dms.worker.user.nickname | dashIfEmpty}}
+
+
+ {{::document.dms.created | dateTime:'dd/MM/yyyy HH:mm'}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/client/front/dms/index.js b/modules/client/front/dms/index/index.js
similarity index 72%
rename from modules/client/front/dms/index.js
rename to modules/client/front/dms/index/index.js
index 9a3c274b7..2a5da4072 100644
--- a/modules/client/front/dms/index.js
+++ b/modules/client/front/dms/index/index.js
@@ -1,4 +1,4 @@
-import ngModule from '../module';
+import ngModule from '../../module';
import './style.scss';
class Controller {
@@ -13,7 +13,15 @@ class Controller {
include: {
relation: 'dms',
scope: {
- fields: ['dmsTypeFk', 'workerFk', 'file', 'created', 'description', 'reference', 'hardCopyNumber'],
+ fields: [
+ 'dmsTypeFk',
+ 'workerFk',
+ 'reference',
+ 'description',
+ 'hasFile',
+ 'file',
+ 'created',
+ ],
include: [{
relation: 'dmsType',
scope: {
@@ -45,19 +53,18 @@ class Controller {
this.$.workerDescriptor.show();
}
- showDeleteConfirm(dmsId) {
- this.selectedDms = dmsId;
+ showDeleteConfirm(index) {
+ this.dmsIndex = index;
this.$.confirm.show();
}
deleteDms(response) {
if (response === 'ACCEPT') {
- let dmsId = this.selectedDms;
- let query = `/client/api/ClientDms/removes`;
-
- this.$http.post(query, {dmsId}).then(() => {
+ const dmsFk = this.clientDms[this.dmsIndex].dmsFk;
+ const query = `/api/ClientDms/${dmsFk}/removeFile`;
+ this.$http.post(query).then(() => {
+ this.$.model.remove(this.dmsIndex);
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
- this.$.model.refresh();
});
}
}
@@ -65,7 +72,7 @@ class Controller {
Controller.$inject = ['$stateParams', '$scope', 'vnToken', '$http', 'vnApp', '$translate'];
-ngModule.component('vnClientDms', {
+ngModule.component('vnClientDmsIndex', {
template: require('./index.html'),
controller: Controller,
});
diff --git a/modules/client/front/dms/locale/es.yml b/modules/client/front/dms/index/locale/es.yml
similarity index 100%
rename from modules/client/front/dms/locale/es.yml
rename to modules/client/front/dms/index/locale/es.yml
diff --git a/modules/client/front/dms/style.scss b/modules/client/front/dms/index/style.scss
similarity index 100%
rename from modules/client/front/dms/style.scss
rename to modules/client/front/dms/index/style.scss
diff --git a/modules/client/front/index.js b/modules/client/front/index.js
index b124aab7b..f7e4368db 100644
--- a/modules/client/front/index.js
+++ b/modules/client/front/index.js
@@ -35,5 +35,5 @@ import './sample/create';
import './web-payment';
import './log';
import './sms';
-import './dms';
-
+import './dms/index';
+import './dms/create';
diff --git a/modules/client/front/routes.json b/modules/client/front/routes.json
index 8f01f5771..c0e042086 100644
--- a/modules/client/front/routes.json
+++ b/modules/client/front/routes.json
@@ -25,7 +25,7 @@
{"state": "client.card.contact", "icon": "contact_phone"},
{"state": "client.card.sample.index", "icon": "mail"},
{"state": "client.card.webPayment", "icon": "icon-onlinepayment"},
- {"state": "client.card.dms", "icon": "cloud_download"}
+ {"state": "client.card.dms.index", "icon": "cloud_upload"}
]
}
],
@@ -319,8 +319,23 @@
{
"url": "/dms",
"state": "client.card.dms",
- "component": "vn-client-dms",
+ "abstract": true,
+ "component": "ui-view"
+ },
+ {
+ "url": "/index",
+ "state": "client.card.dms.index",
+ "component": "vn-client-dms-index",
"description": "File management"
+ },
+ {
+ "url": "/create",
+ "state": "client.card.dms.create",
+ "component": "vn-client-dms-create",
+ "description": "Upload file",
+ "params": {
+ "client": "$ctrl.client"
+ }
}
]
}
diff --git a/modules/ticket/back/methods/ticket-dms/removeFile.js b/modules/ticket/back/methods/ticket-dms/removeFile.js
new file mode 100644
index 000000000..52a0c524f
--- /dev/null
+++ b/modules/ticket/back/methods/ticket-dms/removeFile.js
@@ -0,0 +1,33 @@
+module.exports = Self => {
+ Self.remoteMethodCtx('removeFile', {
+ description: 'Removes a ticket document',
+ accessType: 'WRITE',
+ accepts: {
+ arg: 'id',
+ type: 'Number',
+ description: 'The document id',
+ http: {source: 'path'}
+ },
+ returns: {
+ type: 'Object',
+ root: true
+ },
+ http: {
+ path: `/:id/removeFile`,
+ verb: 'POST'
+ }
+ });
+
+ Self.removeFile = async(ctx, id) => {
+ const models = Self.app.models;
+ const targetTicketDms = await models.TicketDms.findById(id);
+ const targetDms = await models.Dms.findById(targetTicketDms.dmsFk);
+ const trashDmsType = await models.DmsType.findOne({where: {code: 'trash'}});
+
+ await models.Dms.removeFile(ctx, targetTicketDms.dmsFk);
+ await targetTicketDms.destroy();
+
+ return targetDms.updateAttribute('dmsTypeFk', trashDmsType.id);
+ };
+};
+
diff --git a/modules/ticket/back/methods/ticket-dms/specs/removeFile.spec.js b/modules/ticket/back/methods/ticket-dms/specs/removeFile.spec.js
new file mode 100644
index 000000000..b1cd5686a
--- /dev/null
+++ b/modules/ticket/back/methods/ticket-dms/specs/removeFile.spec.js
@@ -0,0 +1,18 @@
+const app = require('vn-loopback/server/server');
+
+describe('TicketDms removeFile()', () => {
+ const ticketDmsId = 1;
+ it(`should return an error for a user without enough privileges`, async() => {
+ let clientId = 101;
+ let ctx = {req: {accessToken: {userId: clientId}}};
+
+ let error;
+ await app.models.TicketDms.removeFile(ctx, ticketDmsId).catch(e => {
+ error = e;
+ }).finally(() => {
+ expect(error.message).toEqual(`You don't have enough privileges`);
+ });
+
+ expect(error).toBeDefined();
+ });
+});
diff --git a/modules/ticket/back/methods/ticket/specs/uploadFile.spec.js b/modules/ticket/back/methods/ticket/specs/uploadFile.spec.js
new file mode 100644
index 000000000..78b935ab5
--- /dev/null
+++ b/modules/ticket/back/methods/ticket/specs/uploadFile.spec.js
@@ -0,0 +1,19 @@
+const app = require('vn-loopback/server/server');
+
+describe('Ticket uploadFile()', () => {
+ it(`should return an error for a user without enough privileges`, async() => {
+ let ticketId = 15;
+ let currentUserId = 101;
+ let ticketTypeId = 14;
+ let ctx = {req: {accessToken: {userId: currentUserId}}, args: {dmsTypeId: ticketTypeId}};
+
+ let error;
+ await app.models.Ticket.uploadFile(ctx, ticketId).catch(e => {
+ error = e;
+ }).finally(() => {
+ expect(error.message).toEqual(`You don't have enough privileges`);
+ });
+
+ expect(error).toBeDefined();
+ });
+});
diff --git a/modules/ticket/back/methods/ticket/uploadFile.js b/modules/ticket/back/methods/ticket/uploadFile.js
new file mode 100644
index 000000000..6af042c8a
--- /dev/null
+++ b/modules/ticket/back/methods/ticket/uploadFile.js
@@ -0,0 +1,77 @@
+module.exports = Self => {
+ Self.remoteMethodCtx('uploadFile', {
+ description: 'Upload and attach a document',
+ accessType: 'WRITE',
+ accepts: [{
+ arg: 'id',
+ type: 'Number',
+ description: 'The ticket id',
+ http: {source: 'path'}
+ },
+ {
+ arg: 'warehouseId',
+ type: 'Number',
+ description: ''
+ },
+ {
+ arg: 'companyId',
+ type: 'Number',
+ description: ''
+ },
+ {
+ arg: 'dmsTypeId',
+ type: 'Number',
+ description: ''
+ },
+ {
+ arg: 'reference',
+ type: 'String',
+ description: ''
+ },
+ {
+ arg: 'description',
+ type: 'String',
+ description: ''
+ },
+ {
+ arg: 'hasFile',
+ type: 'Boolean',
+ description: ''
+ }],
+ returns: {
+ type: 'Object',
+ root: true
+ },
+ http: {
+ path: `/:id/uploadFile`,
+ verb: 'POST'
+ }
+ });
+
+ Self.uploadFile = async(ctx, id) => {
+ const models = Self.app.models;
+ const transaction = await Self.beginTransaction({});
+ const options = {transaction};
+ const promises = [];
+
+ try {
+ const uploadedFiles = await models.Dms.uploadFile(ctx, options);
+ uploadedFiles.forEach(dms => {
+ const newTicketDms = models.TicketDms.create({
+ ticketFk: id,
+ dmsFk: dms.id
+ }, options);
+
+ promises.push(newTicketDms);
+ });
+ const resolvedPromises = await Promise.all(promises);
+
+ await transaction.commit();
+
+ return resolvedPromises;
+ } catch (err) {
+ await transaction.rollback();
+ throw err;
+ }
+ };
+};
diff --git a/modules/ticket/back/models/ticket-dms.js b/modules/ticket/back/models/ticket-dms.js
new file mode 100644
index 000000000..ddb338632
--- /dev/null
+++ b/modules/ticket/back/models/ticket-dms.js
@@ -0,0 +1,3 @@
+module.exports = Self => {
+ require('../methods/ticket-dms/removeFile')(Self);
+};
diff --git a/modules/ticket/back/models/ticket-dms.json b/modules/ticket/back/models/ticket-dms.json
index b725d47cd..b7b24dce0 100644
--- a/modules/ticket/back/models/ticket-dms.json
+++ b/modules/ticket/back/models/ticket-dms.json
@@ -3,8 +3,7 @@
"base": "Loggable",
"log": {
"model": "TicketLog",
- "relation": "ticket",
- "showField": "dmsFk"
+ "relation": "ticket"
},
"options": {
"mysql": {
@@ -12,11 +11,6 @@
}
},
"properties": {
- "ticketFk": {
- "type": "Number",
- "id": true,
- "required": true
- },
"dmsFk": {
"type": "Number",
"id": true,
diff --git a/modules/ticket/back/models/ticket.js b/modules/ticket/back/models/ticket.js
index 7cae2bc1d..19ee0a9fe 100644
--- a/modules/ticket/back/models/ticket.js
+++ b/modules/ticket/back/models/ticket.js
@@ -23,4 +23,5 @@ module.exports = Self => {
require('../methods/ticket/updateEditableTicket')(Self);
require('../methods/ticket/checkEmptiness')(Self);
require('../methods/ticket/updateDiscount')(Self);
+ require('../methods/ticket/uploadFile')(Self);
};
diff --git a/modules/ticket/front/dms/create/index.html b/modules/ticket/front/dms/create/index.html
new file mode 100644
index 000000000..527c6030d
--- /dev/null
+++ b/modules/ticket/front/dms/create/index.html
@@ -0,0 +1,63 @@
+
+
+
diff --git a/modules/ticket/front/dms/create/index.js b/modules/ticket/front/dms/create/index.js
new file mode 100644
index 000000000..1b28b2417
--- /dev/null
+++ b/modules/ticket/front/dms/create/index.js
@@ -0,0 +1,95 @@
+import ngModule from '../../module';
+import './style.scss';
+
+class Controller {
+ constructor($scope, $http, $state, $translate, vnApp) {
+ this.$ = $scope;
+ this.$http = $http;
+ this.$state = $state;
+ this.$translate = $translate;
+ this.vnApp = vnApp;
+ this.dms = {
+ files: [],
+ hasFile: false
+ };
+ }
+
+ get ticket() {
+ return this._ticket;
+ }
+
+ set ticket(value) {
+ this._ticket = value;
+
+ if (value)
+ this.setDefaultParams();
+ }
+
+ setDefaultParams() {
+ const params = {filter: {
+ where: {code: 'ticket'}
+ }};
+ this.$http.get('/api/DmsTypes/findOne', {params}).then(res => {
+ const dmsTypeId = res.data && res.data.id;
+ const defaultParams = {
+ reference: this.ticket.id,
+ warehouseId: this.ticket.warehouseFk,
+ companyId: this.ticket.companyFk,
+ dmsTypeId: dmsTypeId,
+ description: this.$translate.instant('FileDescription', {
+ ticketId: this.ticket.id,
+ clientId: this.ticket.client.id,
+ clientName: this.ticket.client.name
+ }).toUpperCase()
+ };
+
+ this.dms = Object.assign(this.dms, defaultParams);
+ });
+ }
+
+ onSubmit() {
+ const query = `/api/tickets/${this.ticket.id}/uploadFile`;
+ const options = {
+ method: 'POST',
+ url: query,
+ params: this.dms,
+ headers: {
+ 'Content-Type': undefined
+ },
+ transformRequest: files => {
+ const formData = new FormData();
+
+ for (let i = 0; i < files.length; i++)
+ formData.append(files[i].name, files[i]);
+
+ return formData;
+ },
+ data: this.dms.files
+ };
+ this.$http(options).then(res => {
+ if (res) {
+ this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
+ this.$.watcher.updateOriginalData();
+ this.$state.go('ticket.card.dms.index');
+ }
+ });
+ }
+
+ onFileChange(files) {
+ if (files.length > 0) {
+ this.$.$applyAsync(() => {
+ this.dms.hasFile = true;
+ });
+ }
+ }
+}
+
+Controller.$inject = ['$scope', '$http', '$state', '$translate', 'vnApp'];
+
+ngModule.component('vnTicketDmsCreate', {
+ template: require('./index.html'),
+ controller: Controller,
+ bindings: {
+ ticket: '<'
+ }
+});
diff --git a/modules/ticket/front/dms/create/index.spec.js b/modules/ticket/front/dms/create/index.spec.js
new file mode 100644
index 000000000..1b8cdadfa
--- /dev/null
+++ b/modules/ticket/front/dms/create/index.spec.js
@@ -0,0 +1,65 @@
+import './index';
+
+describe('Ticket', () => {
+ describe('Component vnTicketDmsCreate', () => {
+ let controller;
+ let $scope;
+ let $httpBackend;
+ let $httpParamSerializer;
+
+ beforeEach(ngModule('ticket'));
+
+ beforeEach(angular.mock.inject(($componentController, $rootScope, _$httpBackend_, _$httpParamSerializer_) => {
+ $scope = $rootScope.$new();
+ $httpBackend = _$httpBackend_;
+ $httpParamSerializer = _$httpParamSerializer_;
+ controller = $componentController('vnTicketDmsCreate', {$scope});
+ controller._ticket = {
+ id: 15,
+ client: {id: 101, name: 'Bruce wayne'},
+ warehouseFk: 1,
+ companyFk: 1
+ };
+ }));
+
+ describe('client() setter', () => {
+ it('should set the ticket data and then call setDefaultParams()', () => {
+ spyOn(controller, 'setDefaultParams');
+ controller.ticket = {
+ id: 15,
+ name: 'Bruce wayne'
+ };
+
+ expect(controller.ticket).toBeDefined();
+ expect(controller.setDefaultParams).toHaveBeenCalledWith();
+ });
+ });
+
+ describe('setDefaultParams()', () => {
+ it('should perform a GET query and define the dms property on controller', () => {
+ const params = {filter: {
+ where: {code: 'ticket'}
+ }};
+ let serializedParams = $httpParamSerializer(params);
+ $httpBackend.when('GET', `/api/DmsTypes/findOne?${serializedParams}`).respond({id: 14, code: 'ticket'});
+ $httpBackend.expect('GET', `/api/DmsTypes/findOne?${serializedParams}`);
+ controller.setDefaultParams();
+ $httpBackend.flush();
+
+ expect(controller.dms).toBeDefined();
+ expect(controller.dms.reference).toEqual(15);
+ expect(controller.dms.dmsTypeId).toEqual(14);
+ });
+ });
+
+ describe('onFileChange()', () => {
+ it('should set dms hasFile property to true if has any files', () => {
+ const files = [{id: 1, name: 'MyFile'}];
+ controller.onFileChange(files);
+ $scope.$apply();
+
+ expect(controller.dms.hasFile).toBeTruthy();
+ });
+ });
+ });
+});
diff --git a/modules/ticket/front/dms/create/locale/en.yml b/modules/ticket/front/dms/create/locale/en.yml
new file mode 100644
index 000000000..9f4c026a4
--- /dev/null
+++ b/modules/ticket/front/dms/create/locale/en.yml
@@ -0,0 +1 @@
+FileDescription: Ticket id {{ticketId}} from client {{clientName}} id {{clientId}}
\ No newline at end of file
diff --git a/modules/ticket/front/dms/create/locale/es.yml b/modules/ticket/front/dms/create/locale/es.yml
new file mode 100644
index 000000000..e074da48b
--- /dev/null
+++ b/modules/ticket/front/dms/create/locale/es.yml
@@ -0,0 +1,5 @@
+Upload file: Subir fichero
+Upload: Subir
+File: Fichero
+FileDescription: Ticket id {{ticketId}} del cliente {{clientName}} id {{clientId}}
+Attached file: Fichero adjunto
\ No newline at end of file
diff --git a/modules/ticket/front/dms/create/style.scss b/modules/ticket/front/dms/create/style.scss
new file mode 100644
index 000000000..b47544b12
--- /dev/null
+++ b/modules/ticket/front/dms/create/style.scss
@@ -0,0 +1,7 @@
+vn-ticket-request {
+ vn-textfield {
+ margin: 0!important;
+ max-width: 100px;
+ }
+}
+
diff --git a/modules/ticket/front/dms/index.html b/modules/ticket/front/dms/index.html
deleted file mode 100644
index 878fd3277..000000000
--- a/modules/ticket/front/dms/index.html
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
-
-
-
- Id
- Type
- Employee
- Created
-
-
-
-
-
- {{::document.dmsFk}}
- {{::document.dms.dmsType.name}}
-
-
- {{::document.dms.worker.user.nickname | dashIfEmpty}}
-
- {{::document.dms.created | dateTime:'dd/MM/yyyy HH:mm'}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/modules/ticket/front/dms/index/index.html b/modules/ticket/front/dms/index/index.html
new file mode 100644
index 000000000..9000ca91e
--- /dev/null
+++ b/modules/ticket/front/dms/index/index.html
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+ Id
+ Type
+ Reference
+ Description
+ Attached file
+ File
+ Employee
+ Created
+
+
+
+
+
+ {{::document.dmsFk}}
+
+
+ {{::document.dms.dmsType.name}}
+
+
+
+
+ {{::document.dms.reference}}
+
+
+
+
+ {{::document.dms.description}}
+
+
+
+
+
+
+ {{::document.dms.file}}
+
+
+ {{::document.dms.worker.user.nickname | dashIfEmpty}}
+
+
+ {{::document.dms.created | dateTime:'dd/MM/yyyy HH:mm'}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/ticket/front/dms/index.js b/modules/ticket/front/dms/index/index.js
similarity index 53%
rename from modules/ticket/front/dms/index.js
rename to modules/ticket/front/dms/index/index.js
index ab6456b5f..f2c65be26 100644
--- a/modules/ticket/front/dms/index.js
+++ b/modules/ticket/front/dms/index/index.js
@@ -1,16 +1,27 @@
-import ngModule from '../module';
+import ngModule from '../../module';
import './style.scss';
class Controller {
- constructor($stateParams, $scope, vnToken) {
+ constructor($stateParams, $scope, $http, $translate, vnToken, vnApp) {
this.$stateParams = $stateParams;
this.$ = $scope;
+ this.$http = $http;
+ this.$translate = $translate;
this.accessToken = vnToken.token;
+ this.vnApp = vnApp;
this.filter = {
include: {
relation: 'dms',
scope: {
- fields: ['dmsTypeFk', 'workerFk', 'file', 'created'],
+ fields: [
+ 'dmsTypeFk',
+ 'workerFk',
+ 'reference',
+ 'description',
+ 'hasFile',
+ 'file',
+ 'created',
+ ],
include: [{
relation: 'dmsType',
scope: {
@@ -41,11 +52,27 @@ class Controller {
this.$.workerDescriptor.workerFk = workerFk;
this.$.workerDescriptor.show();
}
+
+ showDeleteConfirm(index) {
+ this.dmsIndex = index;
+ this.$.confirm.show();
+ }
+
+ deleteDms(response) {
+ if (response === 'ACCEPT') {
+ const dmsFk = this.ticketDms[this.dmsIndex].dmsFk;
+ const query = `/api/TicketDms/${dmsFk}/removeFile`;
+ this.$http.post(query).then(() => {
+ this.$.model.remove(this.dmsIndex);
+ this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
+ });
+ }
+ }
}
-Controller.$inject = ['$stateParams', '$scope', 'vnToken'];
+Controller.$inject = ['$stateParams', '$scope', '$http', '$translate', 'vnToken', 'vnApp'];
-ngModule.component('vnTicketDms', {
+ngModule.component('vnTicketDmsIndex', {
template: require('./index.html'),
controller: Controller,
});
diff --git a/modules/ticket/front/dms/index.spec.js b/modules/ticket/front/dms/index/index.spec.js
similarity index 100%
rename from modules/ticket/front/dms/index.spec.js
rename to modules/ticket/front/dms/index/index.spec.js
diff --git a/modules/ticket/front/dms/index/locale/es.yml b/modules/ticket/front/dms/index/locale/es.yml
new file mode 100644
index 000000000..c6742ef3c
--- /dev/null
+++ b/modules/ticket/front/dms/index/locale/es.yml
@@ -0,0 +1,3 @@
+Type: Tipo
+File management: Gestión documental
+Are you sure you want to continue?: ¿Seguro que quieres continuar?
\ No newline at end of file
diff --git a/modules/ticket/front/dms/style.scss b/modules/ticket/front/dms/index/style.scss
similarity index 100%
rename from modules/ticket/front/dms/style.scss
rename to modules/ticket/front/dms/index/style.scss
diff --git a/modules/ticket/front/dms/locale/es.yml b/modules/ticket/front/dms/locale/es.yml
deleted file mode 100644
index 6422279d9..000000000
--- a/modules/ticket/front/dms/locale/es.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-Type: Tipo
-File management: Gestión documental
\ No newline at end of file
diff --git a/modules/ticket/front/index.js b/modules/ticket/front/index.js
index 027cb424b..a9a987b56 100644
--- a/modules/ticket/front/index.js
+++ b/modules/ticket/front/index.js
@@ -31,5 +31,5 @@ import './request/index';
import './request/create';
import './log';
import './weekly';
-import './dms';
-
+import './dms/index';
+import './dms/create';
diff --git a/modules/ticket/front/routes.json b/modules/ticket/front/routes.json
index 5c47901f3..431c4187b 100644
--- a/modules/ticket/front/routes.json
+++ b/modules/ticket/front/routes.json
@@ -19,7 +19,7 @@
{"state": "ticket.card.picture", "icon": "image"},
{"state": "ticket.card.log", "icon": "history"},
{"state": "ticket.card.request.index", "icon": "icon-100"},
- {"state": "ticket.card.dms", "icon": "cloud_download"}
+ {"state": "ticket.card.dms.index", "icon": "cloud_download"}
],
"keybindings": [
{"key": "t", "state": "ticket.index"}
@@ -216,8 +216,23 @@
{
"url": "/dms",
"state": "ticket.card.dms",
- "component": "vn-ticket-dms",
+ "abstract": true,
+ "component": "ui-view"
+ },
+ {
+ "url": "/index",
+ "state": "ticket.card.dms.index",
+ "component": "vn-ticket-dms-index",
"description": "File management"
+ },
+ {
+ "url": "/create",
+ "state": "ticket.card.dms.create",
+ "component": "vn-ticket-dms-create",
+ "description": "Upload file",
+ "params": {
+ "ticket": "$ctrl.ticket"
+ }
}
]
}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 9652653a6..86b5df67c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1243,8 +1243,7 @@
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
- "dev": true
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"ansi-styles": {
"version": "3.2.1",
@@ -1469,6 +1468,15 @@
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
"dev": true
},
+ "ascli": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/ascli/-/ascli-0.3.0.tgz",
+ "integrity": "sha1-XmYjDlIZ/j6JUqTvtPIPrllqgTo=",
+ "requires": {
+ "colour": "^0.7.1",
+ "optjs": "^3.2.2"
+ }
+ },
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
@@ -1600,6 +1608,48 @@
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
"dev": true
},
+ "aws-sdk": {
+ "version": "2.461.0",
+ "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.461.0.tgz",
+ "integrity": "sha512-nqRqlOaM92P6BTx/huq8FuowWNPiRRcpEKHvAQ2XTWTQUADx9HIP9KtbEzLpauxE4Er2reM0UYz9Kbtyke/3EQ==",
+ "requires": {
+ "buffer": "4.9.1",
+ "events": "1.1.1",
+ "ieee754": "1.1.8",
+ "jmespath": "0.15.0",
+ "querystring": "0.2.0",
+ "sax": "1.2.1",
+ "url": "0.10.3",
+ "uuid": "3.3.2",
+ "xml2js": "0.4.19"
+ },
+ "dependencies": {
+ "events": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
+ "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
+ },
+ "ieee754": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
+ "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q="
+ },
+ "sax": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
+ "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o="
+ },
+ "url": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
+ "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=",
+ "requires": {
+ "punycode": "1.3.2",
+ "querystring": "0.2.0"
+ }
+ }
+ }
+ },
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
@@ -1802,6 +1852,96 @@
"integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=",
"dev": true
},
+ "base64url": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/base64url/-/base64url-1.0.6.tgz",
+ "integrity": "sha1-1k03XWinxkDZEuI1jRcNylu1RoE=",
+ "requires": {
+ "concat-stream": "~1.4.7",
+ "meow": "~2.0.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+ "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk="
+ },
+ "camelcase-keys": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz",
+ "integrity": "sha1-vRoRv5sxoc5JNJOpMN4aC69K1+w=",
+ "requires": {
+ "camelcase": "^1.0.1",
+ "map-obj": "^1.0.0"
+ }
+ },
+ "concat-stream": {
+ "version": "1.4.11",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.4.11.tgz",
+ "integrity": "sha512-X3JMh8+4je3U1cQpG87+f9lXHDrqcb2MVLg9L7o8b1UZ0DzhRrUpdn65ttzu10PpJPPI3MQNkis+oha6TSA9Mw==",
+ "requires": {
+ "inherits": "~2.0.1",
+ "readable-stream": "~1.1.9",
+ "typedarray": "~0.0.5"
+ }
+ },
+ "indent-string": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz",
+ "integrity": "sha1-25m8xYPrarux5I3LsZmamGBBy2s=",
+ "requires": {
+ "get-stdin": "^4.0.1",
+ "minimist": "^1.1.0",
+ "repeating": "^1.1.0"
+ }
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+ },
+ "meow": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-2.0.0.tgz",
+ "integrity": "sha1-j1MKjs9dQNP0tN+Tw0cpAPuiqPE=",
+ "requires": {
+ "camelcase-keys": "^1.0.0",
+ "indent-string": "^1.1.0",
+ "minimist": "^1.1.0",
+ "object-assign": "^1.0.0"
+ }
+ },
+ "object-assign": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-1.0.0.tgz",
+ "integrity": "sha1-5l3Idm07R7S4MHRlyDEdoDCwcKY="
+ },
+ "readable-stream": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "repeating": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz",
+ "integrity": "sha1-PUEUIYh3U3SU+X93+Xhfq4EPpKw=",
+ "requires": {
+ "is-finite": "^1.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+ }
+ }
+ },
"batch": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
@@ -2149,7 +2289,6 @@
"version": "4.9.1",
"resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
- "dev": true,
"requires": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4",
@@ -2178,6 +2317,11 @@
"integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=",
"dev": true
},
+ "buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+ },
"buffer-fill": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
@@ -2236,6 +2380,11 @@
}
}
},
+ "bufferview": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/bufferview/-/bufferview-1.0.1.tgz",
+ "integrity": "sha1-ev10pF+Tf6QiodM4wIu/3HbNcl0="
+ },
"builtin-modules": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
@@ -2248,6 +2397,22 @@
"integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
"dev": true
},
+ "bytebuffer": {
+ "version": "3.5.5",
+ "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-3.5.5.tgz",
+ "integrity": "sha1-em+vGhNRSwg/H8+VQcTJv75+f9M=",
+ "requires": {
+ "bufferview": "~1",
+ "long": "~2 >=2.2.3"
+ },
+ "dependencies": {
+ "long": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-2.4.0.tgz",
+ "integrity": "sha1-n6GAux2VAM3CnEFWdmoZleH0Uk8="
+ }
+ }
+ },
"bytes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
@@ -2563,7 +2728,6 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
"integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
- "dev": true,
"requires": {
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
@@ -2574,7 +2738,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
- "dev": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -2583,7 +2746,6 @@
"version": "1.0.2",
"resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
- "dev": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -2678,8 +2840,7 @@
"code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
- "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
- "dev": true
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"coffee-script": {
"version": "1.12.7",
@@ -2734,6 +2895,11 @@
"integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
"dev": true
},
+ "colour": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz",
+ "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g="
+ },
"combined-stream": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
@@ -3316,8 +3482,7 @@
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
- "dev": true
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
"decode-uri-component": {
"version": "0.2.0",
@@ -3564,6 +3729,11 @@
"integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=",
"dev": true
},
+ "diff": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz",
+ "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8="
+ },
"diffie-hellman": {
"version": "5.0.3",
"resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
@@ -3748,7 +3918,6 @@
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz",
"integrity": "sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA==",
- "dev": true,
"requires": {
"end-of-stream": "^1.0.0",
"inherits": "^2.0.1",
@@ -3760,7 +3929,6 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
"integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
- "dev": true,
"requires": {
"once": "^1.4.0"
}
@@ -3794,6 +3962,14 @@
"safer-buffer": "^2.1.0"
}
},
+ "ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"editions": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/editions/-/editions-1.3.3.tgz",
@@ -4071,6 +4247,11 @@
"is-arrayish": "^0.2.1"
}
},
+ "errs": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/errs/-/errs-0.3.2.tgz",
+ "integrity": "sha1-eYCZstvTfKK8dJ5TinwTB9C1BJk="
+ },
"es-abstract": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz",
@@ -4852,6 +5033,14 @@
"schema-utils": "^0.4.5"
}
},
+ "filed": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/filed/-/filed-0.1.0.tgz",
+ "integrity": "sha1-sPYmRyojZtwRlFN6Tup+eonzxzU=",
+ "requires": {
+ "mime": ">= 1.2.6"
+ }
+ },
"fill-range": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
@@ -5072,6 +5261,11 @@
}
}
},
+ "formidable": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz",
+ "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg=="
+ },
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@@ -5181,8 +5375,7 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"aproba": {
"version": "1.2.0",
@@ -5225,8 +5418,7 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"concat-map": {
"version": "0.0.1",
@@ -5237,8 +5429,7 @@
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"core-util-is": {
"version": "1.0.2",
@@ -5355,8 +5546,7 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"ini": {
"version": "1.3.5",
@@ -5368,7 +5558,6 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -5391,14 +5580,12 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -5417,7 +5604,6 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -5511,7 +5697,6 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"wrappy": "1"
}
@@ -5597,8 +5782,7 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -5634,7 +5818,6 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -5654,7 +5837,6 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -5698,14 +5880,12 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
}
}
},
@@ -5747,6 +5927,15 @@
"swagger-client": "^3.3.1"
}
},
+ "gapitoken": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/gapitoken/-/gapitoken-0.1.5.tgz",
+ "integrity": "sha1-NXf8+1Qmvjp7jrrakmcSKdjMgc4=",
+ "requires": {
+ "jws": "~3.0.0",
+ "request": "^2.54.0"
+ }
+ },
"gauge": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
@@ -5785,6 +5974,63 @@
}
}
},
+ "gcloud": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/gcloud/-/gcloud-0.10.0.tgz",
+ "integrity": "sha1-hVoms1Mdx7B5FRP/+4n8ZZIfQ+4=",
+ "requires": {
+ "duplexify": "^3.1.2",
+ "extend": "^1.3.0",
+ "gapitoken": "^0.1.3",
+ "node-uuid": "^1.4.1",
+ "protobufjs": "^3.4.0",
+ "request": "^2.39.0",
+ "stream-events": "^1.0.1",
+ "through2": "^0.6.3"
+ },
+ "dependencies": {
+ "extend": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-1.3.0.tgz",
+ "integrity": "sha1-0VFvsP9WJNLr+RI+odrFoZlABPg="
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+ },
+ "node-uuid": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
+ "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc="
+ },
+ "readable-stream": {
+ "version": "1.0.34",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+ },
+ "through2": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
+ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=",
+ "requires": {
+ "readable-stream": ">=1.0.33-1 <1.1.0-0",
+ "xtend": ">=4.0.0 <4.1.0-0"
+ }
+ }
+ }
+ },
"generate-function": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
@@ -5803,8 +6049,7 @@
"get-stdin": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
- "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
- "dev": true
+ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4="
},
"get-stream": {
"version": "3.0.0",
@@ -6011,6 +6256,510 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
},
+ "growl": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz",
+ "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8="
+ },
+ "grpc": {
+ "version": "1.20.3",
+ "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.20.3.tgz",
+ "integrity": "sha512-GsEsi0NVj6usS/xor8pF/xDbDiwZQR59aZl5NUZ59Sy2bdPQFZ3UePr5wevZjHboirRCIQCKRI1cCgvSWUe2ag==",
+ "requires": {
+ "lodash.camelcase": "^4.3.0",
+ "lodash.clone": "^4.5.0",
+ "nan": "^2.13.2",
+ "node-pre-gyp": "^0.13.0",
+ "protobufjs": "^5.0.3"
+ },
+ "dependencies": {
+ "abbrev": {
+ "version": "1.1.1",
+ "bundled": true
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "bundled": true
+ },
+ "aproba": {
+ "version": "1.2.0",
+ "bundled": true
+ },
+ "are-we-there-yet": {
+ "version": "1.1.5",
+ "bundled": true,
+ "requires": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^2.0.6"
+ }
+ },
+ "ascli": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz",
+ "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=",
+ "requires": {
+ "colour": "~0.7.1",
+ "optjs": "~3.2.2"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "bundled": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "bundled": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "bytebuffer": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz",
+ "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=",
+ "requires": {
+ "long": "~3"
+ }
+ },
+ "camelcase": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+ "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
+ },
+ "chownr": {
+ "version": "1.1.1",
+ "bundled": true
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "bundled": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "bundled": true
+ },
+ "console-control-strings": {
+ "version": "1.1.0",
+ "bundled": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "bundled": true
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "bundled": true
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "bundled": true
+ },
+ "detect-libc": {
+ "version": "1.0.3",
+ "bundled": true
+ },
+ "fs-minipass": {
+ "version": "1.2.5",
+ "bundled": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "bundled": true
+ },
+ "gauge": {
+ "version": "2.7.4",
+ "bundled": true,
+ "requires": {
+ "aproba": "^1.0.3",
+ "console-control-strings": "^1.0.0",
+ "has-unicode": "^2.0.0",
+ "object-assign": "^4.1.0",
+ "signal-exit": "^3.0.0",
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wide-align": "^1.1.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
+ "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "bundled": true
+ },
+ "iconv-lite": {
+ "version": "0.4.23",
+ "bundled": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ignore-walk": {
+ "version": "3.0.1",
+ "bundled": true,
+ "requires": {
+ "minimatch": "^3.0.4"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "bundled": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "bundled": true
+ },
+ "ini": {
+ "version": "1.3.5",
+ "bundled": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "bundled": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "bundled": true
+ },
+ "long": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
+ "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s="
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "bundled": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "bundled": true
+ },
+ "minipass": {
+ "version": "2.3.5",
+ "bundled": true,
+ "requires": {
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.0"
+ }
+ },
+ "minizlib": {
+ "version": "1.2.1",
+ "bundled": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "bundled": true,
+ "requires": {
+ "minimist": "0.0.8"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "bundled": true
+ }
+ }
+ },
+ "nan": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
+ "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
+ },
+ "needle": {
+ "version": "2.3.1",
+ "bundled": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "iconv-lite": "^0.4.4",
+ "sax": "^1.2.4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "bundled": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "bundled": true
+ }
+ }
+ },
+ "node-pre-gyp": {
+ "version": "0.13.0",
+ "bundled": true,
+ "requires": {
+ "detect-libc": "^1.0.2",
+ "mkdirp": "^0.5.1",
+ "needle": "^2.2.1",
+ "nopt": "^4.0.1",
+ "npm-packlist": "^1.1.6",
+ "npmlog": "^4.0.2",
+ "rc": "^1.2.7",
+ "rimraf": "^2.6.1",
+ "semver": "^5.3.0",
+ "tar": "^4"
+ }
+ },
+ "nopt": {
+ "version": "4.0.1",
+ "bundled": true,
+ "requires": {
+ "abbrev": "1",
+ "osenv": "^0.1.4"
+ }
+ },
+ "npm-bundled": {
+ "version": "1.0.6",
+ "bundled": true
+ },
+ "npm-packlist": {
+ "version": "1.4.1",
+ "bundled": true,
+ "requires": {
+ "ignore-walk": "^3.0.1",
+ "npm-bundled": "^1.0.1"
+ }
+ },
+ "npmlog": {
+ "version": "4.1.2",
+ "bundled": true,
+ "requires": {
+ "are-we-there-yet": "~1.1.2",
+ "console-control-strings": "~1.1.0",
+ "gauge": "~2.7.3",
+ "set-blocking": "~2.0.0"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "bundled": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "bundled": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "bundled": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "bundled": true
+ },
+ "os-locale": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
+ "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
+ "requires": {
+ "lcid": "^1.0.0"
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "bundled": true
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "bundled": true,
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "bundled": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "bundled": true
+ },
+ "protobufjs": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz",
+ "integrity": "sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==",
+ "requires": {
+ "ascli": "~1",
+ "bytebuffer": "~5",
+ "glob": "^7.0.5",
+ "yargs": "^3.10.0"
+ }
+ },
+ "rc": {
+ "version": "1.2.8",
+ "bundled": true,
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "bundled": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "rimraf": {
+ "version": "2.6.3",
+ "bundled": true,
+ "requires": {
+ "glob": "^7.1.3"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.3",
+ "bundled": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "bundled": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "bundled": true
+ },
+ "sax": {
+ "version": "1.2.4",
+ "bundled": true
+ },
+ "semver": {
+ "version": "5.7.0",
+ "bundled": true
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "bundled": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "bundled": true
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "bundled": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "bundled": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "bundled": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "bundled": true
+ },
+ "tar": {
+ "version": "4.4.8",
+ "bundled": true,
+ "requires": {
+ "chownr": "^1.1.1",
+ "fs-minipass": "^1.2.5",
+ "minipass": "^2.3.4",
+ "minizlib": "^1.1.1",
+ "mkdirp": "^0.5.0",
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.2"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "bundled": true
+ },
+ "wide-align": {
+ "version": "1.1.3",
+ "bundled": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "bundled": true
+ },
+ "yallist": {
+ "version": "3.0.3",
+ "bundled": true
+ },
+ "yargs": {
+ "version": "3.32.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz",
+ "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=",
+ "requires": {
+ "camelcase": "^2.0.1",
+ "cliui": "^3.0.3",
+ "decamelize": "^1.1.1",
+ "os-locale": "^1.4.0",
+ "string-width": "^1.0.1",
+ "window-size": "^0.1.4",
+ "y18n": "^3.2.0"
+ }
+ }
+ }
+ },
"gulp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.0.tgz",
@@ -7214,8 +7963,7 @@
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
- "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
- "dev": true
+ "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
},
"ip-regex": {
"version": "2.1.0",
@@ -7363,7 +8111,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
"integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
- "dev": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -7599,6 +8346,27 @@
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
+ "jade": {
+ "version": "0.26.3",
+ "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz",
+ "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=",
+ "requires": {
+ "commander": "0.6.1",
+ "mkdirp": "0.3.0"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz",
+ "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY="
+ },
+ "mkdirp": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz",
+ "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4="
+ }
+ }
+ },
"jasmine": {
"version": "2.99.0",
"resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.99.0.tgz",
@@ -7684,6 +8452,11 @@
"uuid": "^3.2.1"
}
},
+ "jmespath": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
+ "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
+ },
"js-base64": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.0.tgz",
@@ -7814,6 +8587,32 @@
"integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=",
"dev": true
},
+ "jwa": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.0.2.tgz",
+ "integrity": "sha1-/Xlgnx53Limdzo3bdtAGWd2DUR8=",
+ "requires": {
+ "base64url": "~0.0.4",
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "^1.0.0"
+ },
+ "dependencies": {
+ "base64url": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/base64url/-/base64url-0.0.6.tgz",
+ "integrity": "sha1-lZezazMNscQkdzIuqH6oAnSZuCs="
+ }
+ }
+ },
+ "jws": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.0.0.tgz",
+ "integrity": "sha1-2l8meJfdTpz4E3l52zP8VKPAVBg=",
+ "requires": {
+ "base64url": "~1.0.4",
+ "jwa": "~1.0.0"
+ }
+ },
"karma": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/karma/-/karma-4.0.1.tgz",
@@ -8052,6 +8851,15 @@
"type-check": "~0.3.2"
}
},
+ "liboneandone": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/liboneandone/-/liboneandone-1.2.0.tgz",
+ "integrity": "sha512-EB6Ak9qw+U4HAOnKqPtatxQ9pLclvtsBsggrvOuD4zclJ5xOeEASojsLKEC3O8KJ1Q4obE2JHhOeDuqWXvkoUQ==",
+ "requires": {
+ "mocha": "^2.5.3",
+ "request": "^2.74.0"
+ }
+ },
"liftoff": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.5.0.tgz",
@@ -8186,6 +8994,16 @@
"integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=",
"dev": true
},
+ "lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
+ },
+ "lodash.clone": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz",
+ "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y="
+ },
"lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
@@ -8688,6 +9506,152 @@
}
}
},
+ "loopback-component-storage": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/loopback-component-storage/-/loopback-component-storage-3.6.0.tgz",
+ "integrity": "sha512-BC6iUhgxeDXrBqp4x7Y4Im4WKds1JSN++wRczyXqoq87ceMDFZ9zqHlElZ9lcO9rklqIaixACwEUWI3HiAMfYg==",
+ "requires": {
+ "async": "^2.6.1",
+ "debug": "^3.1.0",
+ "formidable": "^1.2.1",
+ "pkgcloud": "^1.5.0",
+ "strong-globalize": "^4.1.1",
+ "uuid": "^3.2.1"
+ },
+ "dependencies": {
+ "async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
+ "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
+ "requires": {
+ "lodash": "^4.17.11"
+ }
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "execa": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "requires": {
+ "cross-spawn": "^6.0.0",
+ "get-stream": "^4.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "globalize": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/globalize/-/globalize-1.4.2.tgz",
+ "integrity": "sha512-IfKeYI5mAITBmT5EnH8kSQB5uGson4Fkj2XtTpyEbIS7IHNfLHoeTyLJ6tfjiKC6cJXng3IhVurDk5C7ORqFhQ==",
+ "requires": {
+ "cldrjs": "^0.5.0"
+ }
+ },
+ "invert-kv": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
+ "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA=="
+ },
+ "lcid": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
+ "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
+ "requires": {
+ "invert-kv": "^2.0.0"
+ }
+ },
+ "mem": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
+ "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
+ "requires": {
+ "map-age-cleaner": "^0.1.1",
+ "mimic-fn": "^2.0.0",
+ "p-is-promise": "^2.0.0"
+ }
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+ },
+ "os-locale": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
+ "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
+ "requires": {
+ "execa": "^1.0.0",
+ "lcid": "^2.0.0",
+ "mem": "^4.0.0"
+ }
+ },
+ "p-is-promise": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
+ "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg=="
+ },
+ "strong-globalize": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/strong-globalize/-/strong-globalize-4.1.3.tgz",
+ "integrity": "sha512-SJegV7w5D4AodEspZJtJ7rls3fmi+Zc0PdyJCqBsg4RN9B8TC80/uAI2fikC+s1Jp9FLvr2vDX8f0Fqc62M4OA==",
+ "requires": {
+ "accept-language": "^3.0.18",
+ "debug": "^4.1.1",
+ "globalize": "^1.4.2",
+ "lodash": "^4.17.4",
+ "md5": "^2.2.1",
+ "mkdirp": "^0.5.1",
+ "os-locale": "^3.1.0",
+ "yamljs": "^0.3.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ }
+ }
+ },
"loopback-connector": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/loopback-connector/-/loopback-connector-4.5.1.tgz",
@@ -9475,8 +10439,7 @@
"map-obj": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
- "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
- "dev": true
+ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0="
},
"map-stream": {
"version": "0.0.7",
@@ -9800,6 +10763,76 @@
}
}
},
+ "mocha": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz",
+ "integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=",
+ "requires": {
+ "commander": "2.3.0",
+ "debug": "2.2.0",
+ "diff": "1.4.0",
+ "escape-string-regexp": "1.0.2",
+ "glob": "3.2.11",
+ "growl": "1.9.2",
+ "jade": "0.26.3",
+ "mkdirp": "0.5.1",
+ "supports-color": "1.2.0",
+ "to-iso-string": "0.0.2"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz",
+ "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM="
+ },
+ "debug": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+ "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+ "requires": {
+ "ms": "0.7.1"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz",
+ "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE="
+ },
+ "glob": {
+ "version": "3.2.11",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
+ "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=",
+ "requires": {
+ "inherits": "2",
+ "minimatch": "0.3"
+ }
+ },
+ "lru-cache": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
+ "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI="
+ },
+ "minimatch": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
+ "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=",
+ "requires": {
+ "lru-cache": "2",
+ "sigmund": "~1.0.0"
+ }
+ },
+ "ms": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+ "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg="
+ },
+ "supports-color": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz",
+ "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4="
+ }
+ }
+ },
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
@@ -10848,8 +11881,7 @@
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"core-util-is": {
"version": "1.0.2",
@@ -11009,7 +12041,6 @@
"version": "2.3.5",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@@ -11028,7 +12059,6 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -11122,7 +12152,6 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"wrappy": "1"
}
@@ -11208,8 +12237,7 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -11245,7 +12273,6 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
- "optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -11309,14 +12336,12 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
- "dev": true,
- "optional": true
+ "dev": true
}
}
},
@@ -11434,8 +12459,7 @@
"number-is-nan": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
- "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
- "dev": true
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
"oauth-sign": {
"version": "0.9.0",
@@ -11651,6 +12675,11 @@
"resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz",
"integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8="
},
+ "optjs": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz",
+ "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4="
+ },
"ordered-read-streams": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
@@ -12022,6 +13051,93 @@
"find-up": "^2.1.0"
}
},
+ "pkgcloud": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/pkgcloud/-/pkgcloud-1.7.0.tgz",
+ "integrity": "sha512-F4zfjozUAPeKgUKxuVvdGHjFMXEU971tiqtxGNgjQBuNtBNIOwUE0yWpZ/MgwAZEjI8xeQaIcT8JMxYkcHM3VA==",
+ "requires": {
+ "async": "^2.6.1",
+ "aws-sdk": "^2.382.0",
+ "errs": "^0.3.2",
+ "eventemitter2": "^5.0.1",
+ "fast-json-patch": "0.5.x",
+ "filed": "^0.1.0",
+ "gcloud": "^0.10.0",
+ "grpc": "^1.14.1",
+ "ip": "^1.1.5",
+ "liboneandone": "^1.2.0",
+ "lodash": "^4.17.10",
+ "mime": "1.4.1",
+ "qs": "^6.5.2",
+ "request": "^2.88.0",
+ "through2": "0.6.x",
+ "url-join": "0.0.x",
+ "xml2js": "0.1.x"
+ },
+ "dependencies": {
+ "async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
+ "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
+ "requires": {
+ "lodash": "^4.17.11"
+ }
+ },
+ "fast-json-patch": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-0.5.7.tgz",
+ "integrity": "sha1-taj0nSWWJFlu+YuHLz/aiVtNhmU="
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+ },
+ "qs": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+ },
+ "readable-stream": {
+ "version": "1.0.34",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+ },
+ "through2": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
+ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=",
+ "requires": {
+ "readable-stream": ">=1.0.33-1 <1.1.0-0",
+ "xtend": ">=4.0.0 <4.1.0-0"
+ }
+ },
+ "url-join": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/url-join/-/url-join-0.0.1.tgz",
+ "integrity": "sha1-HbSK1CLTQCRpqH99l73r/k+x48g="
+ },
+ "xml2js": {
+ "version": "0.1.14",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.1.14.tgz",
+ "integrity": "sha1-UnTmf1pkxfkpdM2FE54DMq3GuQw=",
+ "requires": {
+ "sax": ">=0.1.1"
+ }
+ }
+ }
+ },
"platform": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz",
@@ -12285,6 +13401,15 @@
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
"dev": true
},
+ "protobufjs": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-3.8.2.tgz",
+ "integrity": "sha1-vIJuNMOvRpfo0K96Zp5NYSrtzRc=",
+ "requires": {
+ "ascli": "~0.3",
+ "bytebuffer": "~3 >=3.5"
+ }
+ },
"proxy-addr": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
@@ -13502,6 +14627,11 @@
"nanoid": "^2.0.0"
}
},
+ "sigmund": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
+ "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
+ },
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
@@ -14152,6 +15282,14 @@
"stream-shift": "^1.0.0"
}
},
+ "stream-events": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
+ "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
+ "requires": {
+ "stubs": "^3.0.0"
+ }
+ },
"stream-exhaust": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz",
@@ -14211,8 +15349,7 @@
"stream-shift": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
- "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
- "dev": true
+ "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
},
"streamroller": {
"version": "1.0.3",
@@ -14303,7 +15440,6 @@
"version": "3.0.1",
"resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
- "dev": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -14571,6 +15707,11 @@
}
}
},
+ "stubs": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
+ "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls="
+ },
"style-loader": {
"version": "0.23.1",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz",
@@ -15015,6 +16156,11 @@
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
"dev": true
},
+ "to-iso-string": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz",
+ "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE="
+ },
"to-object-path": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
@@ -16622,6 +17768,11 @@
"string-width": "^2.1.1"
}
},
+ "window-size": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz",
+ "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY="
+ },
"word-count": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/word-count/-/word-count-0.2.2.tgz",
@@ -16646,7 +17797,6 @@
"version": "2.1.0",
"resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
- "dev": true,
"requires": {
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1"
@@ -16656,7 +17806,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
- "dev": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -16665,7 +17814,6 @@
"version": "1.0.2",
"resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
- "dev": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -16786,8 +17934,7 @@
"y18n": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
- "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
- "dev": true
+ "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
},
"yallist": {
"version": "2.1.2",
diff --git a/package.json b/package.json
index fa63013f7..ed5f51057 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"loopback": "^3.25.0",
"loopback-boot": "^2.27.1",
"loopback-component-explorer": "^6.3.1",
+ "loopback-component-storage": "^3.6.0",
"loopback-connector-mysql": "^5.3.1",
"loopback-connector-remote": "^3.4.1",
"loopback-context": "^3.4.0",