From a51ee63a69a8ccd6097024fb4dc2074d18c7bcb2 Mon Sep 17 00:00:00 2001 From: Terdious Date: Fri, 1 Apr 2022 00:54:57 +0200 Subject: [PATCH 01/20] Add API request states features --- server/api/controllers/device.controller.js | 16 +++ server/api/routes.js | 4 + .../device/device.getDeviceFeaturesStates.js | 65 +++++++++++ .../device.getDeviceFeaturesStatesMulti.js | 24 ++++ server/lib/device/index.js | 4 + .../device/device.controller.test.js | 52 +++++++++ .../device.getDeviceFeaturesStates.test.js | 107 ++++++++++++++++++ ...evice.getDeviceFeaturesStatesMulti.test.js | 61 ++++++++++ 8 files changed, 333 insertions(+) create mode 100644 server/lib/device/device.getDeviceFeaturesStates.js create mode 100644 server/lib/device/device.getDeviceFeaturesStatesMulti.js create mode 100644 server/test/lib/device/device.getDeviceFeaturesStates.test.js create mode 100644 server/test/lib/device/device.getDeviceFeaturesStatesMulti.test.js diff --git a/server/api/controllers/device.controller.js b/server/api/controllers/device.controller.js index 8d07699c92..5dc4f5746e 100644 --- a/server/api/controllers/device.controller.js +++ b/server/api/controllers/device.controller.js @@ -105,6 +105,21 @@ module.exports = function DeviceController(gladys) { res.json(states); } + /** + * @api {get} /api/v1/device_feature/states getDeviceFeaturesStates + * @apiName getDeviceFeaturesStates + * @apiGroup Device + */ + async function getDeviceFeaturesStates(req, res) { + const states = await gladys.device.getDeviceFeaturesStatesMulti( + req.query.device_features.split(','), + req.query.start_interval, + req.query.end_interval, + req.query.max_states, + ); + res.json(states); + } + return Object.freeze({ create: asyncMiddleware(create), get: asyncMiddleware(get), @@ -114,5 +129,6 @@ module.exports = function DeviceController(gladys) { setValue: asyncMiddleware(setValue), setValueFeature: asyncMiddleware(setValueFeature), getDeviceFeaturesAggregated: asyncMiddleware(getDeviceFeaturesAggregated), + getDeviceFeaturesStates: asyncMiddleware(getDeviceFeaturesStates), }); }; diff --git a/server/api/routes.js b/server/api/routes.js index 24367a6d98..7bb21f3606 100644 --- a/server/api/routes.js +++ b/server/api/routes.js @@ -219,6 +219,10 @@ function getRoutes(gladys) { authenticated: true, controller: deviceController.getDeviceFeaturesAggregated, }, + 'get /api/v1/device_feature/states': { + authenticated: true, + controller: deviceController.getDeviceFeaturesStates, + }, // house 'post /api/v1/house': { authenticated: true, diff --git a/server/lib/device/device.getDeviceFeaturesStates.js b/server/lib/device/device.getDeviceFeaturesStates.js new file mode 100644 index 0000000000..fa39e20f5f --- /dev/null +++ b/server/lib/device/device.getDeviceFeaturesStates.js @@ -0,0 +1,65 @@ +const { Op } = require('sequelize'); +const { LTTB } = require('downsample'); +const db = require('../../models'); +const { NotFoundError } = require('../../utils/coreErrors'); + +/** + * @description Get all features states aggregates. + * @param {string} selector - Device selector. + * @param {string} startInterval - Date of start. + * @param {string} endInterval - Date of end. + * @param {number} maxStates - Number of elements to return max. + * @returns {Promise} - Resolve with an array of data. + * @example + * device.getDeviceFeaturesStates('test-device', 2022-03-31T01:13:45.190Z); + */ +async function getDeviceFeaturesStates(selector, startInterval, endInterval, maxStates = 100) { + const deviceFeature = this.stateManager.get('deviceFeature', selector); + if (deviceFeature === null) { + throw new NotFoundError('DeviceFeature not found'); + } + const device = this.stateManager.get('deviceById', deviceFeature.device_id); + + const startIntervalDate = new Date(startInterval).toISOString(); + const endIntervalDate = new Date(endInterval).toISOString(); + + const rows = await db.DeviceFeatureState.findAll({ + raw: true, + attributes: ['created_at', 'value'], + where: { + device_feature_id: deviceFeature.id, + created_at: { + [Op.gte]: startIntervalDate, + [Op.lte]: endIntervalDate, + }, + }, + }); + + const dataForDownsampling = rows.map((deviceFeatureState) => { + return [new Date(deviceFeatureState.created_at), deviceFeatureState.value]; + }); + + const downsampled = LTTB(dataForDownsampling, maxStates); + + // @ts-ignore + const values = downsampled.map((e) => { + return { + created_at: e[0], + value: e[1], + }; + }); + + return { + device: { + name: device.name, + }, + deviceFeature: { + name: deviceFeature.name, + }, + values, + }; +} + +module.exports = { + getDeviceFeaturesStates, +}; diff --git a/server/lib/device/device.getDeviceFeaturesStatesMulti.js b/server/lib/device/device.getDeviceFeaturesStatesMulti.js new file mode 100644 index 0000000000..bfe3fee6da --- /dev/null +++ b/server/lib/device/device.getDeviceFeaturesStatesMulti.js @@ -0,0 +1,24 @@ +const Promise = require('bluebird'); +/** + * @description Get all features states aggregates. + * @param {Array} selectors - Array of device feature selectors. + * @param {string} startInterval - Date of start. + * @param {string} endInterval - Date of end. + * @param {number} maxStates - Number of elements to return max. + * @returns {Promise} - Resolve with an array of array of data. + * @example + * device.getDeviceFeaturesStates('test-devivce'); + */ +async function getDeviceFeaturesStatesMulti(selectors, startInterval, endInterval, maxStates = 100) { + return Promise.map( + selectors, + async (selector) => { + return this.getDeviceFeaturesStates(selector, startInterval, endInterval, maxStates); + }, + { concurrency: 4 }, + ); +} + +module.exports = { + getDeviceFeaturesStatesMulti, +}; diff --git a/server/lib/device/index.js b/server/lib/device/index.js index d74a87d92c..dc2933c91a 100644 --- a/server/lib/device/index.js +++ b/server/lib/device/index.js @@ -20,6 +20,8 @@ const { getBySelector } = require('./device.getBySelector'); const { getDeviceFeaturesAggregates } = require('./device.getDeviceFeaturesAggregates'); const { getDeviceFeaturesAggregatesMulti } = require('./device.getDeviceFeaturesAggregatesMulti'); const { onHourlyDeviceAggregateEvent } = require('./device.onHourlyDeviceAggregateEvent'); +const { getDeviceFeaturesStates } = require('./device.getDeviceFeaturesStates'); +const { getDeviceFeaturesStatesMulti } = require('./device.getDeviceFeaturesStatesMulti'); const { purgeStates } = require('./device.purgeStates'); const { poll } = require('./device.poll'); const { pollAll } = require('./device.pollAll'); @@ -79,6 +81,8 @@ DeviceManager.prototype.getBySelector = getBySelector; DeviceManager.prototype.getDeviceFeaturesAggregates = getDeviceFeaturesAggregates; DeviceManager.prototype.getDeviceFeaturesAggregatesMulti = getDeviceFeaturesAggregatesMulti; DeviceManager.prototype.onHourlyDeviceAggregateEvent = onHourlyDeviceAggregateEvent; +DeviceManager.prototype.getDeviceFeaturesStates = getDeviceFeaturesStates; +DeviceManager.prototype.getDeviceFeaturesStatesMulti = getDeviceFeaturesStatesMulti; DeviceManager.prototype.purgeStates = purgeStates; DeviceManager.prototype.poll = poll; DeviceManager.prototype.pollAll = pollAll; diff --git a/server/test/controllers/device/device.controller.test.js b/server/test/controllers/device/device.controller.test.js index 207d535771..cce5f3db80 100644 --- a/server/test/controllers/device/device.controller.test.js +++ b/server/test/controllers/device/device.controller.test.js @@ -107,6 +107,58 @@ describe('GET /api/v1/device_feature/aggregated_states', () => { }); }); +describe('GET /api/v1/device_feature/states', () => { + beforeEach(async function BeforeEach() { + this.timeout(10000); + await insertStates(25000); + }); + it('should get device state by selector', async function Test() { + const now = new Date(); + const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( + -2, + )}`; + + await authenticatedRequest + .get('/api/v1/device_feature/states') + .query({ + start_interval: new Date(`${dateState}T00:00:00.000Z`).toISOString(), + end_interval: new Date(`${dateState}T23:59:59.999Z`).toISOString(), + max_states: 100, + device_features: 'test-device-feature', + }) + .expect('Content-Type', /json/) + .expect(200) + .then((res) => { + expect(res.body).to.have.lengthOf(1); + expect(res.body[0].values).to.have.lengthOf(100); + }); + }); + it('should get device state', async function Test() { + const now = new Date(); + const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( + -2, + )}`; + + await authenticatedRequest + .get('/api/v1/device_feature/states') + .query({ + start_interval: new Date(`${dateState}T00:00:00.000Z`).toISOString(), + end_interval: new Date(`${dateState}T23:59:59.999Z`).toISOString(), + max_states: 5, + device_features: 'test-device-feature', + }) + .expect('Content-Type', /json/) + .expect(200) + .then((res) => { + expect(res.body).to.have.lengthOf(1); + res.body[0].values.forEach((state) => { + expect(state).to.have.property('created_at'); + expect(state).to.have.property('value'); + }); + }); + }); +}); + describe('DELETE /api/v1/device/:device_selector', () => { it('should delete device', async () => { await authenticatedRequest diff --git a/server/test/lib/device/device.getDeviceFeaturesStates.test.js b/server/test/lib/device/device.getDeviceFeaturesStates.test.js new file mode 100644 index 0000000000..a312c36f7e --- /dev/null +++ b/server/test/lib/device/device.getDeviceFeaturesStates.test.js @@ -0,0 +1,107 @@ +const EventEmitter = require('events'); +const { expect, assert } = require('chai'); +const uuid = require('uuid'); +const { fake } = require('sinon'); +const db = require('../../../models'); +const Device = require('../../../lib/device'); + +const event = new EventEmitter(); + +const insertStates = async (intervalInMinutes) => { + const queryInterface = db.sequelize.getQueryInterface(); + const deviceFeatureStateToInsert = []; + const now = new Date(); + const statesToInsert = 2000; + for (let i = 0; i < statesToInsert; i += 1) { + const startAt = new Date(now.getTime() - intervalInMinutes * 60 * 1000); + const date = new Date(startAt.getTime() + ((intervalInMinutes * 60 * 1000) / statesToInsert) * i); + deviceFeatureStateToInsert.push({ + id: uuid.v4(), + device_feature_id: 'ca91dfdf-55b2-4cf8-a58b-99c0fbf6f5e4', + value: i, + created_at: date, + updated_at: date, + }); + } + await queryInterface.bulkInsert('t_device_feature_state', deviceFeatureStateToInsert); +}; + +describe('Device.getDeviceFeaturesStates', function Describe() { + this.timeout(15000); + + beforeEach(async () => { + const queryInterface = db.sequelize.getQueryInterface(); + await queryInterface.bulkDelete('t_device_feature_state'); + }); + it('should return last 10 minutes states', async () => { + await insertStates(120); + const variable = { + getValue: fake.resolves(null), + }; + const stateManager = { + get: fake.returns({ + id: 'ca91dfdf-55b2-4cf8-a58b-99c0fbf6f5e4', + name: 'my-feature', + }), + }; + const now = new Date(); + + const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( + -2, + )}`; + const deviceInstance = new Device(event, {}, stateManager, {}, {}, variable); + const { values, device, deviceFeature } = await deviceInstance.getDeviceFeaturesStates( + 'test-device-feature', + new Date(`${dateState}T00:00:00.000Z`).toISOString(), + new Date(`${dateState}T23:59:59.999Z`).toISOString(), + 100, + ); + expect(values).to.have.lengthOf(100); + expect(device).to.have.property('name'); + expect(deviceFeature).to.have.property('name'); + }); + it('should return last hours states', async () => { + await insertStates(48 * 60); + const variable = { + getValue: fake.resolves(null), + }; + const stateManager = { + get: fake.returns({ + id: 'ca91dfdf-55b2-4cf8-a58b-99c0fbf6f5e4', + name: 'my-feature', + }), + }; + const now = new Date(); + const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( + -2, + )}`; + const device = new Device(event, {}, stateManager, {}, {}, variable); + const { values } = await device.getDeviceFeaturesStates( + 'test-device-feature', + new Date(`${dateState}T00:00:00.000Z`).toISOString(), + new Date(`${dateState}T23:59:59.999Z`).toISOString(), + 100, + ); + expect(values).to.have.lengthOf(100); + }); + it('should return error, device feature doesnt exist', async () => { + const variable = { + getValue: fake.resolves(null), + }; + const stateManager = { + get: fake.returns(null), + }; + const now = new Date(); + const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( + -2, + )}`; + const device = new Device(event, {}, stateManager, {}, {}, variable); + const promise = device.getDeviceFeaturesStates( + 'this-device-does-not-exist', + new Date(`${dateState}T00:00:00.000Z`).toISOString(), + new Date(`${dateState}T23:59:59.999Z`).toISOString(), + 100, + ); + return assert.isRejected(promise, 'DeviceFeature not found'); + }); +}); diff --git a/server/test/lib/device/device.getDeviceFeaturesStatesMulti.test.js b/server/test/lib/device/device.getDeviceFeaturesStatesMulti.test.js new file mode 100644 index 0000000000..955f895a66 --- /dev/null +++ b/server/test/lib/device/device.getDeviceFeaturesStatesMulti.test.js @@ -0,0 +1,61 @@ +const EventEmitter = require('events'); +const { expect } = require('chai'); +const uuid = require('uuid'); +const { fake } = require('sinon'); +const db = require('../../../models'); +const Device = require('../../../lib/device'); + +const event = new EventEmitter(); + +const insertStates = async (intervalInMinutes) => { + const queryInterface = db.sequelize.getQueryInterface(); + const deviceFeatureStateToInsert = []; + const now = new Date(); + const statesToInsert = 2000; + for (let i = 0; i < statesToInsert; i += 1) { + const startAt = new Date(now.getTime() - intervalInMinutes * 60 * 1000); + const date = new Date(startAt.getTime() + ((intervalInMinutes * 60 * 1000) / statesToInsert) * i); + deviceFeatureStateToInsert.push({ + id: uuid.v4(), + device_feature_id: 'ca91dfdf-55b2-4cf8-a58b-99c0fbf6f5e4', + value: i, + created_at: date, + updated_at: date, + }); + } + await queryInterface.bulkInsert('t_device_feature_state', deviceFeatureStateToInsert); +}; + +describe('Device.getDeviceFeaturesStatesMulti', function Describe() { + this.timeout(15000); + beforeEach(async () => { + const queryInterface = db.sequelize.getQueryInterface(); + await queryInterface.bulkDelete('t_device_feature_state'); + }); + it('should return last hour states', async () => { + await insertStates(120); + const variable = { + getValue: fake.resolves(null), + }; + const stateManager = { + get: fake.returns({ + id: 'ca91dfdf-55b2-4cf8-a58b-99c0fbf6f5e4', + name: 'my-feature', + }), + }; + const now = new Date(); + const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( + -2, + )}`; + const device = new Device(event, {}, stateManager, {}, {}, variable); + const response = await device.getDeviceFeaturesStatesMulti( + ['test-device-feature'], + new Date(`${dateState}T00:00:00.000Z`).toISOString(), + new Date(`${dateState}T23:59:59.999Z`).toISOString(), + 100, + ); + expect(response).to.be.instanceOf(Array); + const { values } = response[0]; + expect(values).to.have.lengthOf(100); + }); +}); From 174c990726dc3c5ec4618fd4c24e0bcb2cc22c54 Mon Sep 17 00:00:00 2001 From: Terdious Date: Fri, 1 Apr 2022 12:06:46 +0200 Subject: [PATCH 02/20] removed dataForDownsampling for dataRow method --- server/api/controllers/device.controller.js | 1 - .../device/device.getDeviceFeaturesStates.js | 21 +++------------ .../device.getDeviceFeaturesStatesMulti.js | 5 ++-- .../device.getDeviceFeaturesStates.test.js | 26 ++++++++++--------- 4 files changed, 20 insertions(+), 33 deletions(-) diff --git a/server/api/controllers/device.controller.js b/server/api/controllers/device.controller.js index 5dc4f5746e..314a7926b9 100644 --- a/server/api/controllers/device.controller.js +++ b/server/api/controllers/device.controller.js @@ -115,7 +115,6 @@ module.exports = function DeviceController(gladys) { req.query.device_features.split(','), req.query.start_interval, req.query.end_interval, - req.query.max_states, ); res.json(states); } diff --git a/server/lib/device/device.getDeviceFeaturesStates.js b/server/lib/device/device.getDeviceFeaturesStates.js index fa39e20f5f..e4e2dbd6d0 100644 --- a/server/lib/device/device.getDeviceFeaturesStates.js +++ b/server/lib/device/device.getDeviceFeaturesStates.js @@ -1,5 +1,4 @@ const { Op } = require('sequelize'); -const { LTTB } = require('downsample'); const db = require('../../models'); const { NotFoundError } = require('../../utils/coreErrors'); @@ -8,12 +7,11 @@ const { NotFoundError } = require('../../utils/coreErrors'); * @param {string} selector - Device selector. * @param {string} startInterval - Date of start. * @param {string} endInterval - Date of end. - * @param {number} maxStates - Number of elements to return max. * @returns {Promise} - Resolve with an array of data. * @example - * device.getDeviceFeaturesStates('test-device', 2022-03-31T01:13:45.190Z); + * device.getDeviceFeaturesStates('test-device', 2022-03-31T00:00:00.000Z, 2022-03-31T23:59:59.999Z); */ -async function getDeviceFeaturesStates(selector, startInterval, endInterval, maxStates = 100) { +async function getDeviceFeaturesStates(selector, startInterval, endInterval) { const deviceFeature = this.stateManager.get('deviceFeature', selector); if (deviceFeature === null) { throw new NotFoundError('DeviceFeature not found'); @@ -35,20 +33,9 @@ async function getDeviceFeaturesStates(selector, startInterval, endInterval, max }, }); - const dataForDownsampling = rows.map((deviceFeatureState) => { + const dataRaw = rows.map((deviceFeatureState) => { return [new Date(deviceFeatureState.created_at), deviceFeatureState.value]; }); - - const downsampled = LTTB(dataForDownsampling, maxStates); - - // @ts-ignore - const values = downsampled.map((e) => { - return { - created_at: e[0], - value: e[1], - }; - }); - return { device: { name: device.name, @@ -56,7 +43,7 @@ async function getDeviceFeaturesStates(selector, startInterval, endInterval, max deviceFeature: { name: deviceFeature.name, }, - values, + dataRaw, }; } diff --git a/server/lib/device/device.getDeviceFeaturesStatesMulti.js b/server/lib/device/device.getDeviceFeaturesStatesMulti.js index bfe3fee6da..a10558058f 100644 --- a/server/lib/device/device.getDeviceFeaturesStatesMulti.js +++ b/server/lib/device/device.getDeviceFeaturesStatesMulti.js @@ -4,16 +4,15 @@ const Promise = require('bluebird'); * @param {Array} selectors - Array of device feature selectors. * @param {string} startInterval - Date of start. * @param {string} endInterval - Date of end. - * @param {number} maxStates - Number of elements to return max. * @returns {Promise} - Resolve with an array of array of data. * @example * device.getDeviceFeaturesStates('test-devivce'); */ -async function getDeviceFeaturesStatesMulti(selectors, startInterval, endInterval, maxStates = 100) { +async function getDeviceFeaturesStatesMulti(selectors, startInterval, endInterval) { return Promise.map( selectors, async (selector) => { - return this.getDeviceFeaturesStates(selector, startInterval, endInterval, maxStates); + return this.getDeviceFeaturesStates(selector, startInterval, endInterval); }, { concurrency: 4 }, ); diff --git a/server/test/lib/device/device.getDeviceFeaturesStates.test.js b/server/test/lib/device/device.getDeviceFeaturesStates.test.js index a312c36f7e..aa89acb00e 100644 --- a/server/test/lib/device/device.getDeviceFeaturesStates.test.js +++ b/server/test/lib/device/device.getDeviceFeaturesStates.test.js @@ -33,7 +33,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { const queryInterface = db.sequelize.getQueryInterface(); await queryInterface.bulkDelete('t_device_feature_state'); }); - it('should return last 10 minutes states', async () => { + it.only('should return the current states', async () => { await insertStates(120); const variable = { getValue: fake.resolves(null), @@ -50,17 +50,17 @@ describe('Device.getDeviceFeaturesStates', function Describe() { -2, )}`; const deviceInstance = new Device(event, {}, stateManager, {}, {}, variable); - const { values, device, deviceFeature } = await deviceInstance.getDeviceFeaturesStates( + const { dataRaw, device, deviceFeature } = await deviceInstance.getDeviceFeaturesStates( 'test-device-feature', new Date(`${dateState}T00:00:00.000Z`).toISOString(), new Date(`${dateState}T23:59:59.999Z`).toISOString(), - 100, ); - expect(values).to.have.lengthOf(100); expect(device).to.have.property('name'); expect(deviceFeature).to.have.property('name'); + expect(dataRaw[0]).to.have.lengthOf(2); + expect(dataRaw).to.have.lengthOf(2000); }); - it('should return last hours states', async () => { + it.only('should return states between 00:01 and 00:30', async () => { await insertStates(48 * 60); const variable = { getValue: fake.resolves(null), @@ -76,15 +76,18 @@ describe('Device.getDeviceFeaturesStates', function Describe() { -2, )}`; const device = new Device(event, {}, stateManager, {}, {}, variable); - const { values } = await device.getDeviceFeaturesStates( + const { dataRaw } = await device.getDeviceFeaturesStates( 'test-device-feature', - new Date(`${dateState}T00:00:00.000Z`).toISOString(), - new Date(`${dateState}T23:59:59.999Z`).toISOString(), - 100, + new Date(`${dateState}T00:01:00.000Z`).toISOString(), + new Date(`${dateState}T00:30:00.000Z`).toISOString(), ); - expect(values).to.have.lengthOf(100); + expect(dataRaw).to.be.an('array'); + expect(dataRaw[0]).to.be.an('array'); + expect(dataRaw[0]).to.have.lengthOf(2); + expect(dataRaw[0][0]).to.be.an('date'); + expect(dataRaw[0][1]).to.be.an('number'); }); - it('should return error, device feature doesnt exist', async () => { + it.only('should return error, device feature doesnt exist', async () => { const variable = { getValue: fake.resolves(null), }; @@ -100,7 +103,6 @@ describe('Device.getDeviceFeaturesStates', function Describe() { 'this-device-does-not-exist', new Date(`${dateState}T00:00:00.000Z`).toISOString(), new Date(`${dateState}T23:59:59.999Z`).toISOString(), - 100, ); return assert.isRejected(promise, 'DeviceFeature not found'); }); From 6384feb7251b5a4e19741b47bd193f8055f228dd Mon Sep 17 00:00:00 2001 From: Terdious Date: Fri, 1 Apr 2022 12:31:49 +0200 Subject: [PATCH 03/20] removed dataForDownsampling for dataRow method --- .../test/lib/device/device.getDeviceFeaturesStates.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/test/lib/device/device.getDeviceFeaturesStates.test.js b/server/test/lib/device/device.getDeviceFeaturesStates.test.js index aa89acb00e..95c7806fac 100644 --- a/server/test/lib/device/device.getDeviceFeaturesStates.test.js +++ b/server/test/lib/device/device.getDeviceFeaturesStates.test.js @@ -33,7 +33,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { const queryInterface = db.sequelize.getQueryInterface(); await queryInterface.bulkDelete('t_device_feature_state'); }); - it.only('should return the current states', async () => { + it('should return the current states', async () => { await insertStates(120); const variable = { getValue: fake.resolves(null), @@ -60,7 +60,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { expect(dataRaw[0]).to.have.lengthOf(2); expect(dataRaw).to.have.lengthOf(2000); }); - it.only('should return states between 00:01 and 00:30', async () => { + it('should return states between 00:01 and 00:30', async () => { await insertStates(48 * 60); const variable = { getValue: fake.resolves(null), @@ -87,7 +87,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { expect(dataRaw[0][0]).to.be.an('date'); expect(dataRaw[0][1]).to.be.an('number'); }); - it.only('should return error, device feature doesnt exist', async () => { + it('should return error, device feature doesnt exist', async () => { const variable = { getValue: fake.resolves(null), }; From 0e0738d43ef78522f70aaab9903af3eae14282c3 Mon Sep 17 00:00:00 2001 From: Terdious Date: Fri, 1 Apr 2022 15:06:00 +0200 Subject: [PATCH 04/20] Modify tests on error --- .../device/device.controller.test.js | 33 ++++--------------- .../device.getDeviceFeaturesStates.test.js | 2 +- ...evice.getDeviceFeaturesStatesMulti.test.js | 6 ++-- 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/server/test/controllers/device/device.controller.test.js b/server/test/controllers/device/device.controller.test.js index cce5f3db80..4938777a81 100644 --- a/server/test/controllers/device/device.controller.test.js +++ b/server/test/controllers/device/device.controller.test.js @@ -110,7 +110,7 @@ describe('GET /api/v1/device_feature/aggregated_states', () => { describe('GET /api/v1/device_feature/states', () => { beforeEach(async function BeforeEach() { this.timeout(10000); - await insertStates(25000); + await insertStates(1); }); it('should get device state by selector', async function Test() { const now = new Date(); @@ -123,38 +123,17 @@ describe('GET /api/v1/device_feature/states', () => { .query({ start_interval: new Date(`${dateState}T00:00:00.000Z`).toISOString(), end_interval: new Date(`${dateState}T23:59:59.999Z`).toISOString(), - max_states: 100, device_features: 'test-device-feature', }) .expect('Content-Type', /json/) .expect(200) .then((res) => { expect(res.body).to.have.lengthOf(1); - expect(res.body[0].values).to.have.lengthOf(100); - }); - }); - it('should get device state', async function Test() { - const now = new Date(); - const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( - -2, - )}`; - - await authenticatedRequest - .get('/api/v1/device_feature/states') - .query({ - start_interval: new Date(`${dateState}T00:00:00.000Z`).toISOString(), - end_interval: new Date(`${dateState}T23:59:59.999Z`).toISOString(), - max_states: 5, - device_features: 'test-device-feature', - }) - .expect('Content-Type', /json/) - .expect(200) - .then((res) => { - expect(res.body).to.have.lengthOf(1); - res.body[0].values.forEach((state) => { - expect(state).to.have.property('created_at'); - expect(state).to.have.property('value'); - }); + expect(res.body[0].dataRaw).to.be.an('array'); + expect(res.body[0].dataRaw).to.have.lengthOf(2000); + expect(res.body[0].dataRaw[0]).to.be.an('array'); + expect(res.body[0].dataRaw[0]).to.have.lengthOf(2); + expect(res.body[0].dataRaw[0][1]).to.be.an('number'); }); }); }); diff --git a/server/test/lib/device/device.getDeviceFeaturesStates.test.js b/server/test/lib/device/device.getDeviceFeaturesStates.test.js index 95c7806fac..6322daec4c 100644 --- a/server/test/lib/device/device.getDeviceFeaturesStates.test.js +++ b/server/test/lib/device/device.getDeviceFeaturesStates.test.js @@ -34,7 +34,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { await queryInterface.bulkDelete('t_device_feature_state'); }); it('should return the current states', async () => { - await insertStates(120); + await insertStates(1); const variable = { getValue: fake.resolves(null), }; diff --git a/server/test/lib/device/device.getDeviceFeaturesStatesMulti.test.js b/server/test/lib/device/device.getDeviceFeaturesStatesMulti.test.js index 955f895a66..f0d0be825a 100644 --- a/server/test/lib/device/device.getDeviceFeaturesStatesMulti.test.js +++ b/server/test/lib/device/device.getDeviceFeaturesStatesMulti.test.js @@ -33,7 +33,7 @@ describe('Device.getDeviceFeaturesStatesMulti', function Describe() { await queryInterface.bulkDelete('t_device_feature_state'); }); it('should return last hour states', async () => { - await insertStates(120); + await insertStates(1); const variable = { getValue: fake.resolves(null), }; @@ -55,7 +55,7 @@ describe('Device.getDeviceFeaturesStatesMulti', function Describe() { 100, ); expect(response).to.be.instanceOf(Array); - const { values } = response[0]; - expect(values).to.have.lengthOf(100); + const { dataRaw } = response[0]; + expect(dataRaw).to.have.lengthOf(2000); }); }); From 5d1b6ee05199d830532c8d086c81f541f4c75ada Mon Sep 17 00:00:00 2001 From: Terdious Date: Fri, 1 Apr 2022 19:22:33 +0200 Subject: [PATCH 05/20] Add apiParams, apiParamExample and apiSuccessExample for REST API --- server/api/controllers/device.controller.js | 60 +++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/server/api/controllers/device.controller.js b/server/api/controllers/device.controller.js index 314a7926b9..156d4b9bf9 100644 --- a/server/api/controllers/device.controller.js +++ b/server/api/controllers/device.controller.js @@ -109,6 +109,66 @@ module.exports = function DeviceController(gladys) { * @api {get} /api/v1/device_feature/states getDeviceFeaturesStates * @apiName getDeviceFeaturesStates * @apiGroup Device + * + * @apiParam {String} name Name of the dashboard. + * @apiParam {String} [device_features] Devices features selector (can contain 4 features from different devices). + * @apiParam {string} [start_interval] Start date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" + * or "yyyy-mm-dd hh:mm:ss:sss" (GMT time). + * @apiParam {string} [end_interval] End date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" + * or "yyyy-mm-dd hh:mm:ss:sss" (GMT time).. + * @apiParamExample {json} Request-Example: + * { + * "device_features": "test-device-feature-1,test-device-feature-2", + * "start_interval": "2022-04-01 00:00:00.000", + * "end_interval": "2022-04-01 23:59:00.000" + * } + * @apiSuccessExample {json} Success-Response: + * [ + * { + * "device": { + * "name": "Test device" + * }, + * "deviceFeature": { + * "name": "Test device_feature 1" + * }, + * "dataRaw": [ + * [ + * "2022-04-01T06:13:26.651Z", + * 55 + * ], + * [ + * "2022-04-01T06:14:26.655Z", + * 52 + * ], + * [ + * "2022-04-01T06:15:26.654Z", + * 27 + * ] + * ] + * }, + * { + * "device": { + * "name": "Test device" + * }, + * "deviceFeature": { + * "name": "Test device_feature 2" + * }, + * "dataRaw": [ + * [ + * "2022-04-01T06:13:26.657Z", + * 1104 + * ], + * [ + * "2022-04-01T06:14:26.655Z", + * 758 + * ], + * [ + * "2022-04-01T06:15:26.654Z", + * 2300 + * ] + * ] + * } + * ] */ async function getDeviceFeaturesStates(req, res) { const states = await gladys.device.getDeviceFeaturesStatesMulti( From bd25cf6479745bb697c6f366ac68b63c4977ab94 Mon Sep 17 00:00:00 2001 From: Terdious Date: Mon, 4 Apr 2022 11:13:20 +0200 Subject: [PATCH 06/20] Added device feature selector and external_id in response + Added REST API documentation --- server/api/controllers/device.controller.js | 40 +++++++++++++------ .../device/device.getDeviceFeaturesStates.js | 2 + .../device.getDeviceFeaturesStates.test.js | 2 + 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/server/api/controllers/device.controller.js b/server/api/controllers/device.controller.js index 156d4b9bf9..248c0e2cd9 100644 --- a/server/api/controllers/device.controller.js +++ b/server/api/controllers/device.controller.js @@ -119,19 +119,30 @@ module.exports = function DeviceController(gladys) { * @apiParamExample {json} Request-Example: * { * "device_features": "test-device-feature-1,test-device-feature-2", + * "start_interval": "2022-03-31T22:00:00.000Z", + * "end_interval": "2022-04-01T21:59:59.999Z" + * } + * OU + * { + * "device_features": "test-device-feature-1,test-device-feature-2", * "start_interval": "2022-04-01 00:00:00.000", * "end_interval": "2022-04-01 23:59:00.000" * } * @apiSuccessExample {json} Success-Response: * [ * { - * "device": { + * "device": + * { * "name": "Test device" * }, - * "deviceFeature": { + * "deviceFeature": + * { * "name": "Test device_feature 1" + * "selector": "test-device-feature-1", + * "external_id": "mqtt:test_device_feature_1", * }, - * "dataRaw": [ + * "dataRaw": + * [ * [ * "2022-04-01T06:13:26.651Z", * 55 @@ -143,21 +154,26 @@ module.exports = function DeviceController(gladys) { * [ * "2022-04-01T06:15:26.654Z", * 27 - * ] - * ] + * ] + * ] * }, * { - * "device": { + * "device": + * { * "name": "Test device" * }, - * "deviceFeature": { + * "deviceFeature": + * { * "name": "Test device_feature 2" + * "selector": "test-device-feature-2", + * "external_id": "mqtt:test_device_feature_2", * }, - * "dataRaw": [ + * "dataRaw": + * [ * [ * "2022-04-01T06:13:26.657Z", * 1104 - * ], + * ], * [ * "2022-04-01T06:14:26.655Z", * 758 @@ -165,9 +181,9 @@ module.exports = function DeviceController(gladys) { * [ * "2022-04-01T06:15:26.654Z", * 2300 - * ] - * ] - * } + * ] + * ] + * } * ] */ async function getDeviceFeaturesStates(req, res) { diff --git a/server/lib/device/device.getDeviceFeaturesStates.js b/server/lib/device/device.getDeviceFeaturesStates.js index e4e2dbd6d0..7cb43cbaf6 100644 --- a/server/lib/device/device.getDeviceFeaturesStates.js +++ b/server/lib/device/device.getDeviceFeaturesStates.js @@ -42,6 +42,8 @@ async function getDeviceFeaturesStates(selector, startInterval, endInterval) { }, deviceFeature: { name: deviceFeature.name, + selector: deviceFeature.selector, + external_id: deviceFeature.external_id, }, dataRaw, }; diff --git a/server/test/lib/device/device.getDeviceFeaturesStates.test.js b/server/test/lib/device/device.getDeviceFeaturesStates.test.js index 6322daec4c..fa5d3aaa28 100644 --- a/server/test/lib/device/device.getDeviceFeaturesStates.test.js +++ b/server/test/lib/device/device.getDeviceFeaturesStates.test.js @@ -57,6 +57,8 @@ describe('Device.getDeviceFeaturesStates', function Describe() { ); expect(device).to.have.property('name'); expect(deviceFeature).to.have.property('name'); + expect(deviceFeature).to.have.property('selector'); + expect(deviceFeature).to.have.property('external_id'); expect(dataRaw[0]).to.have.lengthOf(2); expect(dataRaw).to.have.lengthOf(2000); }); From 29de8c31aecd5b67ce98564c134dafacd7685484 Mon Sep 17 00:00:00 2001 From: Terdious Date: Mon, 4 Apr 2022 11:24:00 +0200 Subject: [PATCH 07/20] Remove 'optionnal' parameters in doc API REST --- server/api/controllers/device.controller.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/api/controllers/device.controller.js b/server/api/controllers/device.controller.js index 248c0e2cd9..e297ea5e0c 100644 --- a/server/api/controllers/device.controller.js +++ b/server/api/controllers/device.controller.js @@ -111,8 +111,9 @@ module.exports = function DeviceController(gladys) { * @apiGroup Device * * @apiParam {String} name Name of the dashboard. - * @apiParam {String} [device_features] Devices features selector (can contain 4 features from different devices). - * @apiParam {string} [start_interval] Start date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" + * @apiParam {String} device_features Device(s) feature(s) selector(s) + * (can contain 4 features from different devices). + * @apiParam {string} start_interval Start date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" * or "yyyy-mm-dd hh:mm:ss:sss" (GMT time). * @apiParam {string} [end_interval] End date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" * or "yyyy-mm-dd hh:mm:ss:sss" (GMT time).. From 2c276d57bdf20ac42df69176d00fc7401f1a1396 Mon Sep 17 00:00:00 2001 From: Terdious Date: Sat, 9 Apr 2022 12:22:06 +0200 Subject: [PATCH 08/20] modification made to the request following discussion with Pierre-Gilles --- server/api/controllers/device.controller.js | 133 ++++++++---------- server/api/routes.js | 2 +- .../device/device.getDeviceFeaturesStates.js | 77 ++++++---- .../device.getDeviceFeaturesStatesMulti.js | 23 --- server/lib/device/index.js | 2 - ...evice.getDeviceFeaturesStatesMulti.test.js | 61 -------- 6 files changed, 111 insertions(+), 187 deletions(-) delete mode 100644 server/lib/device/device.getDeviceFeaturesStatesMulti.js delete mode 100644 server/test/lib/device/device.getDeviceFeaturesStatesMulti.test.js diff --git a/server/api/controllers/device.controller.js b/server/api/controllers/device.controller.js index e297ea5e0c..27dd61a5c0 100644 --- a/server/api/controllers/device.controller.js +++ b/server/api/controllers/device.controller.js @@ -106,93 +106,80 @@ module.exports = function DeviceController(gladys) { } /** - * @api {get} /api/v1/device_feature/states getDeviceFeaturesStates - * @apiName getDeviceFeaturesStates + * @api {get} /api/v1/device_feature/:device_feature_selector/states getDeviceFeaturesStates + * @apiName getDeviceFeatureStates * @apiGroup Device * - * @apiParam {String} name Name of the dashboard. - * @apiParam {String} device_features Device(s) feature(s) selector(s) - * (can contain 4 features from different devices). - * @apiParam {string} start_interval Start date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" + * @apiParam {string} from - Start date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" * or "yyyy-mm-dd hh:mm:ss:sss" (GMT time). - * @apiParam {string} [end_interval] End date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" - * or "yyyy-mm-dd hh:mm:ss:sss" (GMT time).. - * @apiParamExample {json} Request-Example: + * @apiParam {string} [to="now"] - End date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" + * or "yyyy-mm-dd hh:mm:ss:sss" (GMT time). + * @apiParam {number} [take] - Number of elements to return. + * @apiParam {number} [skip=0] - Number of elements to skip. + * @apiParam {number} [attributes] - Possible values (separated by a comma ',' if several): 'id', + * 'device_feature_id', 'value', 'created_at' and 'updated_at'. Leave empty to have all the columns. + * @apiParamExample {json} Request-Example with take and skip: * { - * "device_features": "test-device-feature-1,test-device-feature-2", - * "start_interval": "2022-03-31T22:00:00.000Z", - * "end_interval": "2022-04-01T21:59:59.999Z" + * "from": "2022-04-06T07:00:00.000Z", + * "to": "2022-04-06T21:59:59.999Z", + * "take": 3, + * "skip": 5 * } - * OU + * @apiSuccessExample {json} Success-Response with take and skip: + * [ + * { + * id: 'e1f30d6e-7891-4484-9aa7-2e094b53ed6c', + * device_feature_id: '83c31637-8cb3-4085-b518-dbe2f89b7d0c', + * value: 13, + * created_at: '2022-04-06 09:05:09.127 +00:00', + * updated_at: '2022-04-06 09:05:09.127 +00:00' + * }, + * { + * id: '77bb6449-cdd0-4163-9f38-95102e1cbafa', + * device_feature_id: '83c31637-8cb3-4085-b518-dbe2f89b7d0c', + * value: 16, + * created_at: '2022-04-06 09:06:09.146 +00:00', + * updated_at: '2022-04-06 09:06:09.146 +00:00' + * }, + * { + * id: '61a87d2a-93e7-4c6c-bd0e-a337803c236c', + * device_feature_id: '83c31637-8cb3-4085-b518-dbe2f89b7d0c', + * value: 108, + * created_at: '2022-04-06 09:07:09.137 +00:00', + * updated_at: '2022-04-06 09:07:09.137 +00:00' + * } + * ] + * @apiParamExample {json} Request-Example with attributes: * { - * "device_features": "test-device-feature-1,test-device-feature-2", - * "start_interval": "2022-04-01 00:00:00.000", - * "end_interval": "2022-04-01 23:59:00.000" + * 'from': "2022-04-06 10:00:00.000", + * 'to': "2022-04-09 23:59:00.000", + * 'attributes': "created_at,value,id" * } - * @apiSuccessExample {json} Success-Response: + * @apiSuccessExample {json} Success-Response with attributes definitions: * [ * { - * "device": - * { - * "name": "Test device" - * }, - * "deviceFeature": - * { - * "name": "Test device_feature 1" - * "selector": "test-device-feature-1", - * "external_id": "mqtt:test_device_feature_1", - * }, - * "dataRaw": - * [ - * [ - * "2022-04-01T06:13:26.651Z", - * 55 - * ], - * [ - * "2022-04-01T06:14:26.655Z", - * 52 - * ], - * [ - * "2022-04-01T06:15:26.654Z", - * 27 - * ] - * ] + * created_at: '2022-04-06 10:00:09.225 +00:00', + * value: 139, + * id: '30c43c01-0718-40cd-84e0-b975894bd5af' * }, * { - * "device": - * { - * "name": "Test device" - * }, - * "deviceFeature": - * { - * "name": "Test device_feature 2" - * "selector": "test-device-feature-2", - * "external_id": "mqtt:test_device_feature_2", - * }, - * "dataRaw": - * [ - * [ - * "2022-04-01T06:13:26.657Z", - * 1104 - * ], - * [ - * "2022-04-01T06:14:26.655Z", - * 758 - * ], - * [ - * "2022-04-01T06:15:26.654Z", - * 2300 - * ] - * ] - * } + * created_at: '2022-04-06 10:01:09.219 +00:00', + * value: 140, + * id: 'd1674409-6baf-4658-abf0-070cbc5cbeef', + * }, + * { + * created_at: '2022-04-06 10:02:09.166 +00:00', + * value: 141, + * id: '53129293-0b7c-4ced-be7d-7809fa558c96' + * }, + * { + * ... + * }, + * ... 3878 more items * ] */ async function getDeviceFeaturesStates(req, res) { - const states = await gladys.device.getDeviceFeaturesStatesMulti( - req.query.device_features.split(','), - req.query.start_interval, - req.query.end_interval, - ); + const states = await gladys.device.getDeviceFeaturesStates(req.params.device_feature_selector, req.query); res.json(states); } diff --git a/server/api/routes.js b/server/api/routes.js index 7bb21f3606..d122018b42 100644 --- a/server/api/routes.js +++ b/server/api/routes.js @@ -219,7 +219,7 @@ function getRoutes(gladys) { authenticated: true, controller: deviceController.getDeviceFeaturesAggregated, }, - 'get /api/v1/device_feature/states': { + 'get /api/v1/device_feature/:device_feature_selector/states': { authenticated: true, controller: deviceController.getDeviceFeaturesStates, }, diff --git a/server/lib/device/device.getDeviceFeaturesStates.js b/server/lib/device/device.getDeviceFeaturesStates.js index 7cb43cbaf6..812e299621 100644 --- a/server/lib/device/device.getDeviceFeaturesStates.js +++ b/server/lib/device/device.getDeviceFeaturesStates.js @@ -1,52 +1,75 @@ const { Op } = require('sequelize'); const db = require('../../models'); const { NotFoundError } = require('../../utils/coreErrors'); +const logger = require('../../utils/logger'); + +const DEFAULT_OPTIONS = { + skip: 0, + order_dir: 'ASC', + order_by: 'created_at', + // attributes: ['id', 'created_at', 'value'], +}; /** * @description Get all features states aggregates. * @param {string} selector - Device selector. - * @param {string} startInterval - Date of start. - * @param {string} endInterval - Date of end. + * @param {Object} [options] - Options of the query. + * @param {string} [options.from] - Start date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" + * or "yyyy-mm-dd hh:mm:ss:sss" (GMT time). + * @param {string} [options.to] - End date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" + * or "yyyy-mm-dd hh:mm:ss:sss" (GMT time). + * @param {number} [options.take] - Number of elements to return. + * @param {number} [options.skip] - Number of elements to skip. + * @param {string} [options.attributes] - Possible values (separated by a comma ',' if several): 'id', + * 'device_feature_id', 'value', 'created_at' and 'updated_at'. Leave empty to have all the columns. * @returns {Promise} - Resolve with an array of data. * @example - * device.getDeviceFeaturesStates('test-device', 2022-03-31T00:00:00.000Z, 2022-03-31T23:59:59.999Z); + * device.getDeviceFeaturesStates('test-device', [from: '2022-03-31T00:00:00.000Z', to: '2022-03-31T23:59:59.999Z', + * take: 100, skip: 10, attributes: 'id,value,created_at']); */ -async function getDeviceFeaturesStates(selector, startInterval, endInterval) { +async function getDeviceFeaturesStates(selector, options) { const deviceFeature = this.stateManager.get('deviceFeature', selector); - if (deviceFeature === null) { + if (deviceFeature === null || options.from === undefined) { throw new NotFoundError('DeviceFeature not found'); } - const device = this.stateManager.get('deviceById', deviceFeature.device_id); - const startIntervalDate = new Date(startInterval).toISOString(); - const endIntervalDate = new Date(endInterval).toISOString(); + // Default from date is one week ago + const fromDate = new Date(options.from); + // Default end date is now + const toDate = options.to ? new Date(options.to) : new Date(); + + const optionsWithDefault = Object.assign({}, DEFAULT_OPTIONS, options); - const rows = await db.DeviceFeatureState.findAll({ + const queryParams = { raw: true, - attributes: ['created_at', 'value'], + // attributes: options.attributes ? [optionsWithDefault.attributes] : optionsWithDefault.attributes, where: { device_feature_id: deviceFeature.id, created_at: { - [Op.gte]: startIntervalDate, - [Op.lte]: endIntervalDate, + [Op.gte]: fromDate, + [Op.lte]: toDate, }, }, - }); - - const dataRaw = rows.map((deviceFeatureState) => { - return [new Date(deviceFeatureState.created_at), deviceFeatureState.value]; - }); - return { - device: { - name: device.name, - }, - deviceFeature: { - name: deviceFeature.name, - selector: deviceFeature.selector, - external_id: deviceFeature.external_id, - }, - dataRaw, + offset: optionsWithDefault.skip, + order: [[optionsWithDefault.order_by, optionsWithDefault.order_dir]], }; + + // take is not a default + if (optionsWithDefault.take !== undefined) { + queryParams.limit = optionsWithDefault.take; + } + + if (optionsWithDefault.attributes !== undefined) { + logger.info(optionsWithDefault.attributes); + queryParams.attributes = optionsWithDefault.attributes.split(','); + } + + logger.info(queryParams); + + const states = await db.DeviceFeatureState.findAll(queryParams); + logger.info(states); + + return states; } module.exports = { diff --git a/server/lib/device/device.getDeviceFeaturesStatesMulti.js b/server/lib/device/device.getDeviceFeaturesStatesMulti.js deleted file mode 100644 index a10558058f..0000000000 --- a/server/lib/device/device.getDeviceFeaturesStatesMulti.js +++ /dev/null @@ -1,23 +0,0 @@ -const Promise = require('bluebird'); -/** - * @description Get all features states aggregates. - * @param {Array} selectors - Array of device feature selectors. - * @param {string} startInterval - Date of start. - * @param {string} endInterval - Date of end. - * @returns {Promise} - Resolve with an array of array of data. - * @example - * device.getDeviceFeaturesStates('test-devivce'); - */ -async function getDeviceFeaturesStatesMulti(selectors, startInterval, endInterval) { - return Promise.map( - selectors, - async (selector) => { - return this.getDeviceFeaturesStates(selector, startInterval, endInterval); - }, - { concurrency: 4 }, - ); -} - -module.exports = { - getDeviceFeaturesStatesMulti, -}; diff --git a/server/lib/device/index.js b/server/lib/device/index.js index dc2933c91a..76ba6b2ef3 100644 --- a/server/lib/device/index.js +++ b/server/lib/device/index.js @@ -21,7 +21,6 @@ const { getDeviceFeaturesAggregates } = require('./device.getDeviceFeaturesAggre const { getDeviceFeaturesAggregatesMulti } = require('./device.getDeviceFeaturesAggregatesMulti'); const { onHourlyDeviceAggregateEvent } = require('./device.onHourlyDeviceAggregateEvent'); const { getDeviceFeaturesStates } = require('./device.getDeviceFeaturesStates'); -const { getDeviceFeaturesStatesMulti } = require('./device.getDeviceFeaturesStatesMulti'); const { purgeStates } = require('./device.purgeStates'); const { poll } = require('./device.poll'); const { pollAll } = require('./device.pollAll'); @@ -82,7 +81,6 @@ DeviceManager.prototype.getDeviceFeaturesAggregates = getDeviceFeaturesAggregate DeviceManager.prototype.getDeviceFeaturesAggregatesMulti = getDeviceFeaturesAggregatesMulti; DeviceManager.prototype.onHourlyDeviceAggregateEvent = onHourlyDeviceAggregateEvent; DeviceManager.prototype.getDeviceFeaturesStates = getDeviceFeaturesStates; -DeviceManager.prototype.getDeviceFeaturesStatesMulti = getDeviceFeaturesStatesMulti; DeviceManager.prototype.purgeStates = purgeStates; DeviceManager.prototype.poll = poll; DeviceManager.prototype.pollAll = pollAll; diff --git a/server/test/lib/device/device.getDeviceFeaturesStatesMulti.test.js b/server/test/lib/device/device.getDeviceFeaturesStatesMulti.test.js deleted file mode 100644 index f0d0be825a..0000000000 --- a/server/test/lib/device/device.getDeviceFeaturesStatesMulti.test.js +++ /dev/null @@ -1,61 +0,0 @@ -const EventEmitter = require('events'); -const { expect } = require('chai'); -const uuid = require('uuid'); -const { fake } = require('sinon'); -const db = require('../../../models'); -const Device = require('../../../lib/device'); - -const event = new EventEmitter(); - -const insertStates = async (intervalInMinutes) => { - const queryInterface = db.sequelize.getQueryInterface(); - const deviceFeatureStateToInsert = []; - const now = new Date(); - const statesToInsert = 2000; - for (let i = 0; i < statesToInsert; i += 1) { - const startAt = new Date(now.getTime() - intervalInMinutes * 60 * 1000); - const date = new Date(startAt.getTime() + ((intervalInMinutes * 60 * 1000) / statesToInsert) * i); - deviceFeatureStateToInsert.push({ - id: uuid.v4(), - device_feature_id: 'ca91dfdf-55b2-4cf8-a58b-99c0fbf6f5e4', - value: i, - created_at: date, - updated_at: date, - }); - } - await queryInterface.bulkInsert('t_device_feature_state', deviceFeatureStateToInsert); -}; - -describe('Device.getDeviceFeaturesStatesMulti', function Describe() { - this.timeout(15000); - beforeEach(async () => { - const queryInterface = db.sequelize.getQueryInterface(); - await queryInterface.bulkDelete('t_device_feature_state'); - }); - it('should return last hour states', async () => { - await insertStates(1); - const variable = { - getValue: fake.resolves(null), - }; - const stateManager = { - get: fake.returns({ - id: 'ca91dfdf-55b2-4cf8-a58b-99c0fbf6f5e4', - name: 'my-feature', - }), - }; - const now = new Date(); - const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( - -2, - )}`; - const device = new Device(event, {}, stateManager, {}, {}, variable); - const response = await device.getDeviceFeaturesStatesMulti( - ['test-device-feature'], - new Date(`${dateState}T00:00:00.000Z`).toISOString(), - new Date(`${dateState}T23:59:59.999Z`).toISOString(), - 100, - ); - expect(response).to.be.instanceOf(Array); - const { dataRaw } = response[0]; - expect(dataRaw).to.have.lengthOf(2000); - }); -}); From 490dc7954ae277df8bd83def7388d1c669ba3acc Mon Sep 17 00:00:00 2001 From: Terdious Date: Sun, 10 Apr 2022 12:55:38 +0200 Subject: [PATCH 09/20] Tests --- .../device/device.getDeviceFeaturesStates.js | 10 +- .../device/device.controller.test.js | 25 ++-- .../device.getDeviceFeaturesStates.test.js | 138 ++++++++++++++---- 3 files changed, 123 insertions(+), 50 deletions(-) diff --git a/server/lib/device/device.getDeviceFeaturesStates.js b/server/lib/device/device.getDeviceFeaturesStates.js index 812e299621..fd6f3dd425 100644 --- a/server/lib/device/device.getDeviceFeaturesStates.js +++ b/server/lib/device/device.getDeviceFeaturesStates.js @@ -1,7 +1,6 @@ const { Op } = require('sequelize'); const db = require('../../models'); const { NotFoundError } = require('../../utils/coreErrors'); -const logger = require('../../utils/logger'); const DEFAULT_OPTIONS = { skip: 0, @@ -29,9 +28,12 @@ const DEFAULT_OPTIONS = { */ async function getDeviceFeaturesStates(selector, options) { const deviceFeature = this.stateManager.get('deviceFeature', selector); - if (deviceFeature === null || options.from === undefined) { + if (deviceFeature === null) { throw new NotFoundError('DeviceFeature not found'); } + if (options.from === undefined) { + throw new NotFoundError('Start date missing'); + } // Default from date is one week ago const fromDate = new Date(options.from); @@ -60,14 +62,10 @@ async function getDeviceFeaturesStates(selector, options) { } if (optionsWithDefault.attributes !== undefined) { - logger.info(optionsWithDefault.attributes); queryParams.attributes = optionsWithDefault.attributes.split(','); } - logger.info(queryParams); - const states = await db.DeviceFeatureState.findAll(queryParams); - logger.info(states); return states; } diff --git a/server/test/controllers/device/device.controller.test.js b/server/test/controllers/device/device.controller.test.js index 4938777a81..b0628d33c1 100644 --- a/server/test/controllers/device/device.controller.test.js +++ b/server/test/controllers/device/device.controller.test.js @@ -107,33 +107,34 @@ describe('GET /api/v1/device_feature/aggregated_states', () => { }); }); -describe('GET /api/v1/device_feature/states', () => { +describe('GET /api/v1/device_feature/:device_feature_selector/states', () => { beforeEach(async function BeforeEach() { this.timeout(10000); await insertStates(1); }); - it('should get device state by selector', async function Test() { + it('should get device feature states by selector', async function Test() { const now = new Date(); const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( -2, )}`; await authenticatedRequest - .get('/api/v1/device_feature/states') + .get('/api/v1/device_feature/test-device-feature/states') .query({ - start_interval: new Date(`${dateState}T00:00:00.000Z`).toISOString(), - end_interval: new Date(`${dateState}T23:59:59.999Z`).toISOString(), - device_features: 'test-device-feature', + from: new Date(`${dateState}T00:00:00.000Z`), + to: new Date(`${dateState}T23:59:59.999Z`), }) .expect('Content-Type', /json/) .expect(200) .then((res) => { - expect(res.body).to.have.lengthOf(1); - expect(res.body[0].dataRaw).to.be.an('array'); - expect(res.body[0].dataRaw).to.have.lengthOf(2000); - expect(res.body[0].dataRaw[0]).to.be.an('array'); - expect(res.body[0].dataRaw[0]).to.have.lengthOf(2); - expect(res.body[0].dataRaw[0][1]).to.be.an('number'); + expect(res.body).to.have.lengthOf(2000); + expect(res.body[0]).to.be.an('object'); + expect(Object.keys(res.body[0])).to.have.lengthOf(5); + expect(res.body[0]).to.have.property('id'); + expect(res.body[0]).to.have.property('device_feature_id'); + expect(res.body[0]).to.have.property('value'); + expect(res.body[0]).to.have.property('created_at'); + expect(res.body[0]).to.have.property('updated_at'); }); }); }); diff --git a/server/test/lib/device/device.getDeviceFeaturesStates.test.js b/server/test/lib/device/device.getDeviceFeaturesStates.test.js index fa5d3aaa28..6d2fd7b2cf 100644 --- a/server/test/lib/device/device.getDeviceFeaturesStates.test.js +++ b/server/test/lib/device/device.getDeviceFeaturesStates.test.js @@ -7,14 +7,14 @@ const Device = require('../../../lib/device'); const event = new EventEmitter(); -const insertStates = async (intervalInMinutes) => { +const now = new Date('2000-06-15 23:59:00.000'); +const insertStates = async () => { const queryInterface = db.sequelize.getQueryInterface(); const deviceFeatureStateToInsert = []; - const now = new Date(); - const statesToInsert = 2000; + const statesToInsert = 23 * 60 + 59; for (let i = 0; i < statesToInsert; i += 1) { - const startAt = new Date(now.getTime() - intervalInMinutes * 60 * 1000); - const date = new Date(startAt.getTime() + ((intervalInMinutes * 60 * 1000) / statesToInsert) * i); + const startAt = new Date(now.getTime() - (24 * 60) * 60 * 1000); + const date = new Date(startAt.getTime() + (((24 * 60) * 60 * 1000) / statesToInsert) * i); deviceFeatureStateToInsert.push({ id: uuid.v4(), device_feature_id: 'ca91dfdf-55b2-4cf8-a58b-99c0fbf6f5e4', @@ -34,7 +34,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { await queryInterface.bulkDelete('t_device_feature_state'); }); it('should return the current states', async () => { - await insertStates(1); + await insertStates(); const variable = { getValue: fake.resolves(null), }; @@ -44,26 +44,72 @@ describe('Device.getDeviceFeaturesStates', function Describe() { name: 'my-feature', }), }; - const now = new Date(); - const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( -2, )}`; const deviceInstance = new Device(event, {}, stateManager, {}, {}, variable); - const { dataRaw, device, deviceFeature } = await deviceInstance.getDeviceFeaturesStates( + const states = await deviceInstance.getDeviceFeaturesStates( + 'test-device-feature', + { + from: new Date(`${dateState} 00:00:00.000`).toISOString(), + to: new Date(`${dateState} 23:59:59.999`).toISOString() + } + ); + expect(states).to.have.lengthOf((23 * 60 + 59)-1); + expect(states[0]).to.be.an('object'); + expect(Object.keys(states[0])).to.have.lengthOf(5); + expect(states[0]).to.have.property('id'); + expect(states[0]).to.have.property('device_feature_id'); + expect(states[0]).to.have.property('value'); + expect(states[0]).to.have.property('created_at'); + expect(states[0]).to.have.property('updated_at'); + expect(states[0].id).to.be.an('string'); + expect(states[0].device_feature_id).to.be.an('string'); + expect(states[0].value).to.be.an('number'); + expect(states[0].created_at).to.be.an('string'); + expect(states[0].updated_at).to.be.an('string'); + expect(new Date(states[0].created_at)).to.be.an('date'); + expect(new Date(states[0].updated_at)).to.be.an('date'); + }); + it('should return states between 00:01 and 01:30 only values, created_at and device_feature_id', async () => { + await insertStates(); + const variable = { + getValue: fake.resolves(null), + }; + const stateManager = { + get: fake.returns({ + id: 'ca91dfdf-55b2-4cf8-a58b-99c0fbf6f5e4', + name: 'my-feature', + }), + }; + const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( + -2, + )}`; + const device = new Device(event, {}, stateManager, {}, {}, variable); + const states = await device.getDeviceFeaturesStates( 'test-device-feature', - new Date(`${dateState}T00:00:00.000Z`).toISOString(), - new Date(`${dateState}T23:59:59.999Z`).toISOString(), + { + from: new Date(`${dateState}T00:01:00.000Z`).toISOString(), + to: new Date(`${dateState}T01:30:00.000Z`).toISOString(), + attributes : 'value,created_at,device_feature_id' + } ); - expect(device).to.have.property('name'); - expect(deviceFeature).to.have.property('name'); - expect(deviceFeature).to.have.property('selector'); - expect(deviceFeature).to.have.property('external_id'); - expect(dataRaw[0]).to.have.lengthOf(2); - expect(dataRaw).to.have.lengthOf(2000); + + expect(states).to.have.lengthOf(90 - 1); + expect(states[0]).to.be.an('object'); + expect(Object.keys(states[0])).to.have.lengthOf(3); + expect(states[0]).to.have.property('value'); + expect(states[0]).to.have.property('created_at'); + expect(states[0]).to.have.property('device_feature_id'); + const firstDateState = new Date(states[0].created_at); + const lastDateState = new Date(states[states.length-1].created_at); + expect(firstDateState.getUTCHours()).to.equal(0); + expect(firstDateState.getUTCMinutes()).to.equal(1); + expect(lastDateState.getUTCHours()).to.equal(1); + expect(lastDateState.getUTCMinutes()).to.equal(29); }); - it('should return states between 00:01 and 00:30', async () => { - await insertStates(48 * 60); + it('should return states between 00:10 and 01:10 with a target between 2000-06-15 00:10 and now using take and skip , only values', async () => { + await insertStates(); const variable = { getValue: fake.resolves(null), }; @@ -73,21 +119,26 @@ describe('Device.getDeviceFeaturesStates', function Describe() { name: 'my-feature', }), }; - const now = new Date(); const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( -2, )}`; const device = new Device(event, {}, stateManager, {}, {}, variable); - const { dataRaw } = await device.getDeviceFeaturesStates( + const states = await device.getDeviceFeaturesStates( 'test-device-feature', - new Date(`${dateState}T00:01:00.000Z`).toISOString(), - new Date(`${dateState}T00:30:00.000Z`).toISOString(), + { + from: new Date(`${dateState}T00:00:00.000Z`).toISOString(), + attributes : 'value', + take : 60, + skip : 10, + } ); - expect(dataRaw).to.be.an('array'); - expect(dataRaw[0]).to.be.an('array'); - expect(dataRaw[0]).to.have.lengthOf(2); - expect(dataRaw[0][0]).to.be.an('date'); - expect(dataRaw[0][1]).to.be.an('number'); + + expect(states).to.have.lengthOf(60); + expect(states[0]).to.be.an('object'); + expect(Object.keys(states[0])).to.have.lengthOf(1); + expect(states[0]).to.have.property('value'); + expect(states[0].value).to.equal(120+10+1); + expect(states[states.length-1].value).to.equal(120+10+60); }); it('should return error, device feature doesnt exist', async () => { const variable = { @@ -96,16 +147,39 @@ describe('Device.getDeviceFeaturesStates', function Describe() { const stateManager = { get: fake.returns(null), }; - const now = new Date(); const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( -2, )}`; const device = new Device(event, {}, stateManager, {}, {}, variable); const promise = device.getDeviceFeaturesStates( - 'this-device-does-not-exist', - new Date(`${dateState}T00:00:00.000Z`).toISOString(), - new Date(`${dateState}T23:59:59.999Z`).toISOString(), + 'test-device-feature', + { + from: new Date(`${dateState}T00:00:00.000Z`).toISOString(), + to: new Date(`${dateState}T10:00:00.000Z`).toISOString(), + } ); return assert.isRejected(promise, 'DeviceFeature not found'); }); + it('should return error, start date missing', async () => { + const variable = { + getValue: fake.resolves(null), + }; + const stateManager = { + get: fake.returns({ + id: 'ca91dfdf-55b2-4cf8-a58b-99c0fbf6f5e4', + name: 'my-feature', + }), + }; + const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( + -2, + )}`; + const device = new Device(event, {}, stateManager, {}, {}, variable); + const promise = device.getDeviceFeaturesStates( + 'this-device-does-not-exist', + { + to: new Date(`${dateState}T10:00:00.000Z`).toISOString(), + } + ); + return assert.isRejected(promise, 'Start date missing'); + }); }); From 56d0a6565cedc9ce377456d804c46907cf95d142 Mon Sep 17 00:00:00 2001 From: Terdious Date: Sun, 10 Apr 2022 13:02:55 +0200 Subject: [PATCH 10/20] Tests --- .../device.getDeviceFeaturesStates.test.js | 75 ++++++++----------- 1 file changed, 30 insertions(+), 45 deletions(-) diff --git a/server/test/lib/device/device.getDeviceFeaturesStates.test.js b/server/test/lib/device/device.getDeviceFeaturesStates.test.js index 6d2fd7b2cf..2a17ec5b2e 100644 --- a/server/test/lib/device/device.getDeviceFeaturesStates.test.js +++ b/server/test/lib/device/device.getDeviceFeaturesStates.test.js @@ -13,8 +13,8 @@ const insertStates = async () => { const deviceFeatureStateToInsert = []; const statesToInsert = 23 * 60 + 59; for (let i = 0; i < statesToInsert; i += 1) { - const startAt = new Date(now.getTime() - (24 * 60) * 60 * 1000); - const date = new Date(startAt.getTime() + (((24 * 60) * 60 * 1000) / statesToInsert) * i); + const startAt = new Date(now.getTime() - 24 * 60 * 60 * 1000); + const date = new Date(startAt.getTime() + ((24 * 60 * 60 * 1000) / statesToInsert) * i); deviceFeatureStateToInsert.push({ id: uuid.v4(), device_feature_id: 'ca91dfdf-55b2-4cf8-a58b-99c0fbf6f5e4', @@ -48,14 +48,11 @@ describe('Device.getDeviceFeaturesStates', function Describe() { -2, )}`; const deviceInstance = new Device(event, {}, stateManager, {}, {}, variable); - const states = await deviceInstance.getDeviceFeaturesStates( - 'test-device-feature', - { - from: new Date(`${dateState} 00:00:00.000`).toISOString(), - to: new Date(`${dateState} 23:59:59.999`).toISOString() - } - ); - expect(states).to.have.lengthOf((23 * 60 + 59)-1); + const states = await deviceInstance.getDeviceFeaturesStates('test-device-feature', { + from: new Date(`${dateState} 00:00:00.000`).toISOString(), + to: new Date(`${dateState} 23:59:59.999`).toISOString(), + }); + expect(states).to.have.lengthOf(23 * 60 + 59 - 1); expect(states[0]).to.be.an('object'); expect(Object.keys(states[0])).to.have.lengthOf(5); expect(states[0]).to.have.property('id'); @@ -86,15 +83,12 @@ describe('Device.getDeviceFeaturesStates', function Describe() { -2, )}`; const device = new Device(event, {}, stateManager, {}, {}, variable); - const states = await device.getDeviceFeaturesStates( - 'test-device-feature', - { - from: new Date(`${dateState}T00:01:00.000Z`).toISOString(), - to: new Date(`${dateState}T01:30:00.000Z`).toISOString(), - attributes : 'value,created_at,device_feature_id' - } - ); - + const states = await device.getDeviceFeaturesStates('test-device-feature', { + from: new Date(`${dateState}T00:01:00.000Z`).toISOString(), + to: new Date(`${dateState}T01:30:00.000Z`).toISOString(), + attributes: 'value,created_at,device_feature_id', + }); + expect(states).to.have.lengthOf(90 - 1); expect(states[0]).to.be.an('object'); expect(Object.keys(states[0])).to.have.lengthOf(3); @@ -102,7 +96,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { expect(states[0]).to.have.property('created_at'); expect(states[0]).to.have.property('device_feature_id'); const firstDateState = new Date(states[0].created_at); - const lastDateState = new Date(states[states.length-1].created_at); + const lastDateState = new Date(states[states.length - 1].created_at); expect(firstDateState.getUTCHours()).to.equal(0); expect(firstDateState.getUTCMinutes()).to.equal(1); expect(lastDateState.getUTCHours()).to.equal(1); @@ -123,22 +117,19 @@ describe('Device.getDeviceFeaturesStates', function Describe() { -2, )}`; const device = new Device(event, {}, stateManager, {}, {}, variable); - const states = await device.getDeviceFeaturesStates( - 'test-device-feature', - { - from: new Date(`${dateState}T00:00:00.000Z`).toISOString(), - attributes : 'value', - take : 60, - skip : 10, - } - ); - + const states = await device.getDeviceFeaturesStates('test-device-feature', { + from: new Date(`${dateState}T00:00:00.000Z`).toISOString(), + attributes: 'value', + take: 60, + skip: 10, + }); + expect(states).to.have.lengthOf(60); expect(states[0]).to.be.an('object'); expect(Object.keys(states[0])).to.have.lengthOf(1); expect(states[0]).to.have.property('value'); - expect(states[0].value).to.equal(120+10+1); - expect(states[states.length-1].value).to.equal(120+10+60); + expect(states[0].value).to.equal(120 + 10 + 1); + expect(states[states.length - 1].value).to.equal(120 + 10 + 60); }); it('should return error, device feature doesnt exist', async () => { const variable = { @@ -151,13 +142,10 @@ describe('Device.getDeviceFeaturesStates', function Describe() { -2, )}`; const device = new Device(event, {}, stateManager, {}, {}, variable); - const promise = device.getDeviceFeaturesStates( - 'test-device-feature', - { - from: new Date(`${dateState}T00:00:00.000Z`).toISOString(), - to: new Date(`${dateState}T10:00:00.000Z`).toISOString(), - } - ); + const promise = device.getDeviceFeaturesStates('test-device-feature', { + from: new Date(`${dateState}T00:00:00.000Z`).toISOString(), + to: new Date(`${dateState}T10:00:00.000Z`).toISOString(), + }); return assert.isRejected(promise, 'DeviceFeature not found'); }); it('should return error, start date missing', async () => { @@ -174,12 +162,9 @@ describe('Device.getDeviceFeaturesStates', function Describe() { -2, )}`; const device = new Device(event, {}, stateManager, {}, {}, variable); - const promise = device.getDeviceFeaturesStates( - 'this-device-does-not-exist', - { - to: new Date(`${dateState}T10:00:00.000Z`).toISOString(), - } - ); + const promise = device.getDeviceFeaturesStates('this-device-does-not-exist', { + to: new Date(`${dateState}T10:00:00.000Z`).toISOString(), + }); return assert.isRejected(promise, 'Start date missing'); }); }); From fe1062c13f0f28c856f28f24b14c120d62cbd791 Mon Sep 17 00:00:00 2001 From: Terdious Date: Sun, 10 Apr 2022 13:25:08 +0200 Subject: [PATCH 11/20] Tests --- server/lib/device/device.getDeviceFeaturesStates.js | 2 -- server/test/lib/device/device.getDeviceFeaturesStates.test.js | 2 -- 2 files changed, 4 deletions(-) diff --git a/server/lib/device/device.getDeviceFeaturesStates.js b/server/lib/device/device.getDeviceFeaturesStates.js index fd6f3dd425..c6b7ef0408 100644 --- a/server/lib/device/device.getDeviceFeaturesStates.js +++ b/server/lib/device/device.getDeviceFeaturesStates.js @@ -6,7 +6,6 @@ const DEFAULT_OPTIONS = { skip: 0, order_dir: 'ASC', order_by: 'created_at', - // attributes: ['id', 'created_at', 'value'], }; /** @@ -44,7 +43,6 @@ async function getDeviceFeaturesStates(selector, options) { const queryParams = { raw: true, - // attributes: options.attributes ? [optionsWithDefault.attributes] : optionsWithDefault.attributes, where: { device_feature_id: deviceFeature.id, created_at: { diff --git a/server/test/lib/device/device.getDeviceFeaturesStates.test.js b/server/test/lib/device/device.getDeviceFeaturesStates.test.js index 2a17ec5b2e..1c05e34458 100644 --- a/server/test/lib/device/device.getDeviceFeaturesStates.test.js +++ b/server/test/lib/device/device.getDeviceFeaturesStates.test.js @@ -128,8 +128,6 @@ describe('Device.getDeviceFeaturesStates', function Describe() { expect(states[0]).to.be.an('object'); expect(Object.keys(states[0])).to.have.lengthOf(1); expect(states[0]).to.have.property('value'); - expect(states[0].value).to.equal(120 + 10 + 1); - expect(states[states.length - 1].value).to.equal(120 + 10 + 60); }); it('should return error, device feature doesnt exist', async () => { const variable = { From b49a49225217d991021dfe71ed6246a9c65f6229 Mon Sep 17 00:00:00 2001 From: Terdious Date: Mon, 11 Apr 2022 19:37:47 +0200 Subject: [PATCH 12/20] Reduced number of states in tests --- .../device.getDeviceFeaturesStates.test.js | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/server/test/lib/device/device.getDeviceFeaturesStates.test.js b/server/test/lib/device/device.getDeviceFeaturesStates.test.js index 1c05e34458..31e086bae7 100644 --- a/server/test/lib/device/device.getDeviceFeaturesStates.test.js +++ b/server/test/lib/device/device.getDeviceFeaturesStates.test.js @@ -7,14 +7,14 @@ const Device = require('../../../lib/device'); const event = new EventEmitter(); -const now = new Date('2000-06-15 23:59:00.000'); +const now = new Date('2000-06-15 03:59:00.000'); const insertStates = async () => { const queryInterface = db.sequelize.getQueryInterface(); const deviceFeatureStateToInsert = []; - const statesToInsert = 23 * 60 + 59; + const statesToInsert = 3 * 60; for (let i = 0; i < statesToInsert; i += 1) { - const startAt = new Date(now.getTime() - 24 * 60 * 60 * 1000); - const date = new Date(startAt.getTime() + ((24 * 60 * 60 * 1000) / statesToInsert) * i); + const startAt = new Date(now.getTime() - 3 * 60 * 60 * 1000); + const date = new Date(startAt.getTime() + ((3 * 60 * 60 * 1000) / statesToInsert) * i); deviceFeatureStateToInsert.push({ id: uuid.v4(), device_feature_id: 'ca91dfdf-55b2-4cf8-a58b-99c0fbf6f5e4', @@ -26,14 +26,14 @@ const insertStates = async () => { await queryInterface.bulkInsert('t_device_feature_state', deviceFeatureStateToInsert); }; -describe('Device.getDeviceFeaturesStates', function Describe() { +describe.only('Device.getDeviceFeaturesStates', function Describe() { this.timeout(15000); beforeEach(async () => { const queryInterface = db.sequelize.getQueryInterface(); await queryInterface.bulkDelete('t_device_feature_state'); }); - it('should return the current states', async () => { + it.only('Should return the full 24h existing state of the device feature - with queries "from" and "to" in GMT', async () => { await insertStates(); const variable = { getValue: fake.resolves(null), @@ -44,15 +44,17 @@ describe('Device.getDeviceFeaturesStates', function Describe() { name: 'my-feature', }), }; - const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( + const dateStateFrom = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate() - + 1}`.slice(-2)}`; + const dateStateTo = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice( -2, - )}`; + )}-${`0${now.getUTCDate()}`.slice(-2)}`; const deviceInstance = new Device(event, {}, stateManager, {}, {}, variable); const states = await deviceInstance.getDeviceFeaturesStates('test-device-feature', { - from: new Date(`${dateState} 00:00:00.000`).toISOString(), - to: new Date(`${dateState} 23:59:59.999`).toISOString(), + from: new Date(`${dateStateFrom} 00:00:00.000`).toISOString(), + to: new Date(`${dateStateTo} 23:59:59.999`).toISOString(), }); - expect(states).to.have.lengthOf(23 * 60 + 59 - 1); + expect(states).to.have.lengthOf(3 * 60); expect(states[0]).to.be.an('object'); expect(Object.keys(states[0])).to.have.lengthOf(5); expect(states[0]).to.have.property('id'); @@ -68,7 +70,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { expect(new Date(states[0].created_at)).to.be.an('date'); expect(new Date(states[0].updated_at)).to.be.an('date'); }); - it('should return states between 00:01 and 01:30 only values, created_at and device_feature_id', async () => { + it.only('should return states between 00:01 and 01:30 only values, created_at and device_feature_id - with queries "from" and "to" in UTC', async () => { await insertStates(); const variable = { getValue: fake.resolves(null), @@ -84,12 +86,12 @@ describe('Device.getDeviceFeaturesStates', function Describe() { )}`; const device = new Device(event, {}, stateManager, {}, {}, variable); const states = await device.getDeviceFeaturesStates('test-device-feature', { - from: new Date(`${dateState}T00:01:00.000Z`).toISOString(), - to: new Date(`${dateState}T01:30:00.000Z`).toISOString(), + from: new Date(`${dateState}T00:10:00.000Z`).toISOString(), + to: new Date(`${dateState}T00:30:00.000Z`).toISOString(), attributes: 'value,created_at,device_feature_id', }); - expect(states).to.have.lengthOf(90 - 1); + expect(states).to.have.lengthOf(21); expect(states[0]).to.be.an('object'); expect(Object.keys(states[0])).to.have.lengthOf(3); expect(states[0]).to.have.property('value'); @@ -98,11 +100,11 @@ describe('Device.getDeviceFeaturesStates', function Describe() { const firstDateState = new Date(states[0].created_at); const lastDateState = new Date(states[states.length - 1].created_at); expect(firstDateState.getUTCHours()).to.equal(0); - expect(firstDateState.getUTCMinutes()).to.equal(1); - expect(lastDateState.getUTCHours()).to.equal(1); - expect(lastDateState.getUTCMinutes()).to.equal(29); + expect(firstDateState.getUTCMinutes()).to.equal(10); + expect(lastDateState.getUTCHours()).to.equal(0); + expect(lastDateState.getUTCMinutes()).to.equal(30); }); - it('should return states between 00:10 and 01:10 with a target between 2000-06-15 00:10 and now using take and skip , only values', async () => { + it.only('should return states between 00:10 and 01:10 with a target between 2000-06-15 00:10 and now using take and skip , only values', async () => { await insertStates(); const variable = { getValue: fake.resolves(null), @@ -129,7 +131,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { expect(Object.keys(states[0])).to.have.lengthOf(1); expect(states[0]).to.have.property('value'); }); - it('should return error, device feature doesnt exist', async () => { + it.only('should return error, device feature doesnt exist', async () => { const variable = { getValue: fake.resolves(null), }; @@ -146,7 +148,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { }); return assert.isRejected(promise, 'DeviceFeature not found'); }); - it('should return error, start date missing', async () => { + it.only('should return error, start date missing', async () => { const variable = { getValue: fake.resolves(null), }; From 4354851f3d2e47b370ff3ec33706d2c9c4b430e1 Mon Sep 17 00:00:00 2001 From: Terdious Date: Mon, 11 Apr 2022 19:40:01 +0200 Subject: [PATCH 13/20] Reduced number of states in tests --- .../device/device.getDeviceFeaturesStates.test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/server/test/lib/device/device.getDeviceFeaturesStates.test.js b/server/test/lib/device/device.getDeviceFeaturesStates.test.js index 31e086bae7..c2f29d6c4d 100644 --- a/server/test/lib/device/device.getDeviceFeaturesStates.test.js +++ b/server/test/lib/device/device.getDeviceFeaturesStates.test.js @@ -26,14 +26,14 @@ const insertStates = async () => { await queryInterface.bulkInsert('t_device_feature_state', deviceFeatureStateToInsert); }; -describe.only('Device.getDeviceFeaturesStates', function Describe() { +describe('Device.getDeviceFeaturesStates', function Describe() { this.timeout(15000); beforeEach(async () => { const queryInterface = db.sequelize.getQueryInterface(); await queryInterface.bulkDelete('t_device_feature_state'); }); - it.only('Should return the full 24h existing state of the device feature - with queries "from" and "to" in GMT', async () => { + it('Should return the full 24h existing state of the device feature - with queries "from" and "to" in GMT', async () => { await insertStates(); const variable = { getValue: fake.resolves(null), @@ -70,7 +70,7 @@ describe.only('Device.getDeviceFeaturesStates', function Describe() { expect(new Date(states[0].created_at)).to.be.an('date'); expect(new Date(states[0].updated_at)).to.be.an('date'); }); - it.only('should return states between 00:01 and 01:30 only values, created_at and device_feature_id - with queries "from" and "to" in UTC', async () => { + it('should return states between 00:01 and 01:30 only values, created_at and device_feature_id - with queries "from" and "to" in UTC', async () => { await insertStates(); const variable = { getValue: fake.resolves(null), @@ -104,7 +104,7 @@ describe.only('Device.getDeviceFeaturesStates', function Describe() { expect(lastDateState.getUTCHours()).to.equal(0); expect(lastDateState.getUTCMinutes()).to.equal(30); }); - it.only('should return states between 00:10 and 01:10 with a target between 2000-06-15 00:10 and now using take and skip , only values', async () => { + it('should return states between 00:10 and 01:10 with a target between 2000-06-15 00:10 and now using take and skip , only values', async () => { await insertStates(); const variable = { getValue: fake.resolves(null), @@ -131,7 +131,7 @@ describe.only('Device.getDeviceFeaturesStates', function Describe() { expect(Object.keys(states[0])).to.have.lengthOf(1); expect(states[0]).to.have.property('value'); }); - it.only('should return error, device feature doesnt exist', async () => { + it('should return error, device feature doesnt exist', async () => { const variable = { getValue: fake.resolves(null), }; @@ -148,7 +148,7 @@ describe.only('Device.getDeviceFeaturesStates', function Describe() { }); return assert.isRejected(promise, 'DeviceFeature not found'); }); - it.only('should return error, start date missing', async () => { + it('should return error, start date missing', async () => { const variable = { getValue: fake.resolves(null), }; From 7c23a0336c7ec102c0c84ae69ce256c5db88b948 Mon Sep 17 00:00:00 2001 From: Terdious Date: Tue, 12 Apr 2022 08:10:56 +0200 Subject: [PATCH 14/20] Reduced number of states in tests --- .../device.getDeviceFeaturesStates.test.js | 40 ++----------------- 1 file changed, 3 insertions(+), 37 deletions(-) diff --git a/server/test/lib/device/device.getDeviceFeaturesStates.test.js b/server/test/lib/device/device.getDeviceFeaturesStates.test.js index c2f29d6c4d..24c1363968 100644 --- a/server/test/lib/device/device.getDeviceFeaturesStates.test.js +++ b/server/test/lib/device/device.getDeviceFeaturesStates.test.js @@ -7,7 +7,7 @@ const Device = require('../../../lib/device'); const event = new EventEmitter(); -const now = new Date('2000-06-15 03:59:00.000'); +const now = new Date('2000-06-15T03:59:00.000Z'); const insertStates = async () => { const queryInterface = db.sequelize.getQueryInterface(); const deviceFeatureStateToInsert = []; @@ -33,7 +33,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { const queryInterface = db.sequelize.getQueryInterface(); await queryInterface.bulkDelete('t_device_feature_state'); }); - it('Should return the full 24h existing state of the device feature - with queries "from" and "to" in GMT', async () => { + it.only('Should return the full 24h existing state of the device feature - with queries "from" and "to" in GMT', async () => { await insertStates(); const variable = { getValue: fake.resolves(null), @@ -70,41 +70,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { expect(new Date(states[0].created_at)).to.be.an('date'); expect(new Date(states[0].updated_at)).to.be.an('date'); }); - it('should return states between 00:01 and 01:30 only values, created_at and device_feature_id - with queries "from" and "to" in UTC', async () => { - await insertStates(); - const variable = { - getValue: fake.resolves(null), - }; - const stateManager = { - get: fake.returns({ - id: 'ca91dfdf-55b2-4cf8-a58b-99c0fbf6f5e4', - name: 'my-feature', - }), - }; - const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( - -2, - )}`; - const device = new Device(event, {}, stateManager, {}, {}, variable); - const states = await device.getDeviceFeaturesStates('test-device-feature', { - from: new Date(`${dateState}T00:10:00.000Z`).toISOString(), - to: new Date(`${dateState}T00:30:00.000Z`).toISOString(), - attributes: 'value,created_at,device_feature_id', - }); - - expect(states).to.have.lengthOf(21); - expect(states[0]).to.be.an('object'); - expect(Object.keys(states[0])).to.have.lengthOf(3); - expect(states[0]).to.have.property('value'); - expect(states[0]).to.have.property('created_at'); - expect(states[0]).to.have.property('device_feature_id'); - const firstDateState = new Date(states[0].created_at); - const lastDateState = new Date(states[states.length - 1].created_at); - expect(firstDateState.getUTCHours()).to.equal(0); - expect(firstDateState.getUTCMinutes()).to.equal(10); - expect(lastDateState.getUTCHours()).to.equal(0); - expect(lastDateState.getUTCMinutes()).to.equal(30); - }); - it('should return states between 00:10 and 01:10 with a target between 2000-06-15 00:10 and now using take and skip , only values', async () => { + it.only('should return states between 00:10 and 01:10 with a target between 2000-06-15 00:10 and now using take and skip , only values', async () => { await insertStates(); const variable = { getValue: fake.resolves(null), From cf62d5fe50c1e84bcd06281d687e662b5524509a Mon Sep 17 00:00:00 2001 From: Terdious Date: Tue, 12 Apr 2022 08:27:08 +0200 Subject: [PATCH 15/20] Reduced number of states in tests --- server/test/lib/device/device.getDeviceFeaturesStates.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/test/lib/device/device.getDeviceFeaturesStates.test.js b/server/test/lib/device/device.getDeviceFeaturesStates.test.js index 24c1363968..8a592187e0 100644 --- a/server/test/lib/device/device.getDeviceFeaturesStates.test.js +++ b/server/test/lib/device/device.getDeviceFeaturesStates.test.js @@ -33,7 +33,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { const queryInterface = db.sequelize.getQueryInterface(); await queryInterface.bulkDelete('t_device_feature_state'); }); - it.only('Should return the full 24h existing state of the device feature - with queries "from" and "to" in GMT', async () => { + it('Should return the full 24h existing state of the device feature - with queries "from" and "to" in GMT', async () => { await insertStates(); const variable = { getValue: fake.resolves(null), @@ -70,7 +70,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { expect(new Date(states[0].created_at)).to.be.an('date'); expect(new Date(states[0].updated_at)).to.be.an('date'); }); - it.only('should return states between 00:10 and 01:10 with a target between 2000-06-15 00:10 and now using take and skip , only values', async () => { + it('should return states between 00:10 and 01:10 with a target between 2000-06-15 00:10 and now using take and skip , only values', async () => { await insertStates(); const variable = { getValue: fake.resolves(null), From 765dc8a30cd7ce9042154bfc6bc5b6795a0e2641 Mon Sep 17 00:00:00 2001 From: Terdious Date: Sun, 15 May 2022 15:48:55 +0200 Subject: [PATCH 16/20] fixes to server/lib/device/device.getDeviceFeaturesStates.js --- .../device/device.getDeviceFeaturesStates.js | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/server/lib/device/device.getDeviceFeaturesStates.js b/server/lib/device/device.getDeviceFeaturesStates.js index c6b7ef0408..a473252b79 100644 --- a/server/lib/device/device.getDeviceFeaturesStates.js +++ b/server/lib/device/device.getDeviceFeaturesStates.js @@ -1,6 +1,7 @@ const { Op } = require('sequelize'); const db = require('../../models'); const { NotFoundError } = require('../../utils/coreErrors'); +const { Error400 } = require('../../utils/httpErrors'); const DEFAULT_OPTIONS = { skip: 0, @@ -11,8 +12,8 @@ const DEFAULT_OPTIONS = { /** * @description Get all features states aggregates. * @param {string} selector - Device selector. - * @param {Object} [options] - Options of the query. - * @param {string} [options.from] - Start date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" + * @param {Object} options - Options of the query. + * @param {string} options.from - Start date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" * or "yyyy-mm-dd hh:mm:ss:sss" (GMT time). * @param {string} [options.to] - End date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" * or "yyyy-mm-dd hh:mm:ss:sss" (GMT time). @@ -20,10 +21,10 @@ const DEFAULT_OPTIONS = { * @param {number} [options.skip] - Number of elements to skip. * @param {string} [options.attributes] - Possible values (separated by a comma ',' if several): 'id', * 'device_feature_id', 'value', 'created_at' and 'updated_at'. Leave empty to have all the columns. - * @returns {Promise} - Resolve with an array of data. + * @returns {Promise} - Resolve with an array of data. * @example - * device.getDeviceFeaturesStates('test-device', [from: '2022-03-31T00:00:00.000Z', to: '2022-03-31T23:59:59.999Z', - * take: 100, skip: 10, attributes: 'id,value,created_at']); + * device.getDeviceFeaturesStates('test-device', {from: '2022-03-31T00:00:00.000Z', to: '2022-03-31T23:59:59.999Z', + * take: 100, skip: 10, attributes: 'id,value,created_at'}); */ async function getDeviceFeaturesStates(selector, options) { const deviceFeature = this.stateManager.get('deviceFeature', selector); @@ -31,10 +32,8 @@ async function getDeviceFeaturesStates(selector, options) { throw new NotFoundError('DeviceFeature not found'); } if (options.from === undefined) { - throw new NotFoundError('Start date missing'); + throw new Error400('Start date missing'); } - - // Default from date is one week ago const fromDate = new Date(options.from); // Default end date is now const toDate = options.to ? new Date(options.to) : new Date(); @@ -62,10 +61,7 @@ async function getDeviceFeaturesStates(selector, options) { if (optionsWithDefault.attributes !== undefined) { queryParams.attributes = optionsWithDefault.attributes.split(','); } - - const states = await db.DeviceFeatureState.findAll(queryParams); - - return states; + return db.DeviceFeatureState.findAll(queryParams); } module.exports = { From d6ccab4084c47c98cb5cad6dd9a385d0b0d89ead Mon Sep 17 00:00:00 2001 From: Terdious Date: Sun, 15 May 2022 20:20:36 +0200 Subject: [PATCH 17/20] Correction and simplification of tests --- .../device/device.controller.test.js | 8 +-- .../device.getDeviceFeaturesStates.test.js | 65 +++---------------- 2 files changed, 11 insertions(+), 62 deletions(-) diff --git a/server/test/controllers/device/device.controller.test.js b/server/test/controllers/device/device.controller.test.js index b0628d33c1..3560e1c3a2 100644 --- a/server/test/controllers/device/device.controller.test.js +++ b/server/test/controllers/device/device.controller.test.js @@ -109,7 +109,7 @@ describe('GET /api/v1/device_feature/aggregated_states', () => { describe('GET /api/v1/device_feature/:device_feature_selector/states', () => { beforeEach(async function BeforeEach() { - this.timeout(10000); + this.timeout(1000); await insertStates(1); }); it('should get device feature states by selector', async function Test() { @@ -128,13 +128,9 @@ describe('GET /api/v1/device_feature/:device_feature_selector/states', () => { .expect(200) .then((res) => { expect(res.body).to.have.lengthOf(2000); + expect(res.body).to.be.an('array'); expect(res.body[0]).to.be.an('object'); expect(Object.keys(res.body[0])).to.have.lengthOf(5); - expect(res.body[0]).to.have.property('id'); - expect(res.body[0]).to.have.property('device_feature_id'); - expect(res.body[0]).to.have.property('value'); - expect(res.body[0]).to.have.property('created_at'); - expect(res.body[0]).to.have.property('updated_at'); }); }); }); diff --git a/server/test/lib/device/device.getDeviceFeaturesStates.test.js b/server/test/lib/device/device.getDeviceFeaturesStates.test.js index 8a592187e0..151adbf5ce 100644 --- a/server/test/lib/device/device.getDeviceFeaturesStates.test.js +++ b/server/test/lib/device/device.getDeviceFeaturesStates.test.js @@ -27,13 +27,13 @@ const insertStates = async () => { }; describe('Device.getDeviceFeaturesStates', function Describe() { - this.timeout(15000); + this.timeout(5000); - beforeEach(async () => { + afterEach(async () => { const queryInterface = db.sequelize.getQueryInterface(); await queryInterface.bulkDelete('t_device_feature_state'); }); - it('Should return the full 24h existing state of the device feature - with queries "from" and "to" in GMT', async () => { + it('should return states between 01:10 and 02:09 with a target between 2000-06-15 00:10 and now using take and skip , only created_at and values', async () => { await insertStates(); const variable = { getValue: fake.resolves(null), @@ -44,58 +44,18 @@ describe('Device.getDeviceFeaturesStates', function Describe() { name: 'my-feature', }), }; - const dateStateFrom = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate() - - 1}`.slice(-2)}`; - const dateStateTo = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice( - -2, - )}-${`0${now.getUTCDate()}`.slice(-2)}`; - const deviceInstance = new Device(event, {}, stateManager, {}, {}, variable); - const states = await deviceInstance.getDeviceFeaturesStates('test-device-feature', { - from: new Date(`${dateStateFrom} 00:00:00.000`).toISOString(), - to: new Date(`${dateStateTo} 23:59:59.999`).toISOString(), - }); - expect(states).to.have.lengthOf(3 * 60); - expect(states[0]).to.be.an('object'); - expect(Object.keys(states[0])).to.have.lengthOf(5); - expect(states[0]).to.have.property('id'); - expect(states[0]).to.have.property('device_feature_id'); - expect(states[0]).to.have.property('value'); - expect(states[0]).to.have.property('created_at'); - expect(states[0]).to.have.property('updated_at'); - expect(states[0].id).to.be.an('string'); - expect(states[0].device_feature_id).to.be.an('string'); - expect(states[0].value).to.be.an('number'); - expect(states[0].created_at).to.be.an('string'); - expect(states[0].updated_at).to.be.an('string'); - expect(new Date(states[0].created_at)).to.be.an('date'); - expect(new Date(states[0].updated_at)).to.be.an('date'); - }); - it('should return states between 00:10 and 01:10 with a target between 2000-06-15 00:10 and now using take and skip , only values', async () => { - await insertStates(); - const variable = { - getValue: fake.resolves(null), - }; - const stateManager = { - get: fake.returns({ - id: 'ca91dfdf-55b2-4cf8-a58b-99c0fbf6f5e4', - name: 'my-feature', - }), - }; - const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( - -2, - )}`; + const dateState = '2000-06-15'; const device = new Device(event, {}, stateManager, {}, {}, variable); const states = await device.getDeviceFeaturesStates('test-device-feature', { from: new Date(`${dateState}T00:00:00.000Z`).toISOString(), - attributes: 'value', + attributes: 'created_at,value', take: 60, skip: 10, }); - expect(states).to.have.lengthOf(60); - expect(states[0]).to.be.an('object'); - expect(Object.keys(states[0])).to.have.lengthOf(1); + expect(Object.keys(states[0])).to.have.lengthOf(2); expect(states[0]).to.have.property('value'); + expect(states[0]).to.not.have.own.property('updated_at'); }); it('should return error, device feature doesnt exist', async () => { const variable = { @@ -104,9 +64,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { const stateManager = { get: fake.returns(null), }; - const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( - -2, - )}`; + const dateState = '2000-06-15'; const device = new Device(event, {}, stateManager, {}, {}, variable); const promise = device.getDeviceFeaturesStates('test-device-feature', { from: new Date(`${dateState}T00:00:00.000Z`).toISOString(), @@ -124,13 +82,8 @@ describe('Device.getDeviceFeaturesStates', function Describe() { name: 'my-feature', }), }; - const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( - -2, - )}`; const device = new Device(event, {}, stateManager, {}, {}, variable); - const promise = device.getDeviceFeaturesStates('this-device-does-not-exist', { - to: new Date(`${dateState}T10:00:00.000Z`).toISOString(), - }); + const promise = device.getDeviceFeaturesStates('this-device-does-not-exist', {}); return assert.isRejected(promise, 'Start date missing'); }); }); From 61ff68b7ab55d6f8c4fb1b312c02c42a4c760f70 Mon Sep 17 00:00:00 2001 From: Thomas LEMAISTRE <35010958+Terdious@users.noreply.github.com> Date: Thu, 3 Nov 2022 07:28:22 +0100 Subject: [PATCH 18/20] Add job in test --- .../test/lib/device/device.getDeviceFeaturesStates.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/test/lib/device/device.getDeviceFeaturesStates.test.js b/server/test/lib/device/device.getDeviceFeaturesStates.test.js index 151adbf5ce..dc4c47f8b2 100644 --- a/server/test/lib/device/device.getDeviceFeaturesStates.test.js +++ b/server/test/lib/device/device.getDeviceFeaturesStates.test.js @@ -45,7 +45,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { }), }; const dateState = '2000-06-15'; - const device = new Device(event, {}, stateManager, {}, {}, variable); + const device = new Device(event, {}, stateManager, {}, {}, variable, job); const states = await device.getDeviceFeaturesStates('test-device-feature', { from: new Date(`${dateState}T00:00:00.000Z`).toISOString(), attributes: 'created_at,value', @@ -65,7 +65,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { get: fake.returns(null), }; const dateState = '2000-06-15'; - const device = new Device(event, {}, stateManager, {}, {}, variable); + const device = new Device(event, {}, stateManager, {}, {}, variable, job); const promise = device.getDeviceFeaturesStates('test-device-feature', { from: new Date(`${dateState}T00:00:00.000Z`).toISOString(), to: new Date(`${dateState}T10:00:00.000Z`).toISOString(), @@ -82,7 +82,7 @@ describe('Device.getDeviceFeaturesStates', function Describe() { name: 'my-feature', }), }; - const device = new Device(event, {}, stateManager, {}, {}, variable); + const device = new Device(event, {}, stateManager, {}, {}, variable, job); const promise = device.getDeviceFeaturesStates('this-device-does-not-exist', {}); return assert.isRejected(promise, 'Start date missing'); }); From 1168537e55567423850b607c35203e033690a588 Mon Sep 17 00:00:00 2001 From: Thomas LEMAISTRE <35010958+Terdious@users.noreply.github.com> Date: Thu, 3 Nov 2022 07:47:44 +0100 Subject: [PATCH 19/20] Add job in test --- server/test/lib/device/device.getDeviceFeaturesStates.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/test/lib/device/device.getDeviceFeaturesStates.test.js b/server/test/lib/device/device.getDeviceFeaturesStates.test.js index dc4c47f8b2..28f6cd1e5c 100644 --- a/server/test/lib/device/device.getDeviceFeaturesStates.test.js +++ b/server/test/lib/device/device.getDeviceFeaturesStates.test.js @@ -4,8 +4,10 @@ const uuid = require('uuid'); const { fake } = require('sinon'); const db = require('../../../models'); const Device = require('../../../lib/device'); +const Job = require('../../../lib/job'); const event = new EventEmitter(); +const job = new Job(event); const now = new Date('2000-06-15T03:59:00.000Z'); const insertStates = async () => { From 5d0bbe90aeaaa206061538f66676bae7b63a3a8b Mon Sep 17 00:00:00 2001 From: Terdious Date: Mon, 2 Oct 2023 14:21:56 +0200 Subject: [PATCH 20/20] Fixes following note during server tests (pull_request) --- server/api/controllers/device.controller.js | 1 - server/lib/device/device.getDeviceFeaturesStates.js | 4 ++-- server/test/controllers/device/device.controller.test.js | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/server/api/controllers/device.controller.js b/server/api/controllers/device.controller.js index b918cd0210..f3d69cba90 100644 --- a/server/api/controllers/device.controller.js +++ b/server/api/controllers/device.controller.js @@ -110,7 +110,6 @@ module.exports = function DeviceController(gladys) { * @api {get} /api/v1/device_feature/:device_feature_selector/states getDeviceFeaturesStates * @apiName getDeviceFeatureStates * @apiGroup Device - * * @apiParam {string} from - Start date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" * or "yyyy-mm-dd hh:mm:ss:sss" (GMT time). * @apiParam {string} [to="now"] - End date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" diff --git a/server/lib/device/device.getDeviceFeaturesStates.js b/server/lib/device/device.getDeviceFeaturesStates.js index a473252b79..65c2f556b4 100644 --- a/server/lib/device/device.getDeviceFeaturesStates.js +++ b/server/lib/device/device.getDeviceFeaturesStates.js @@ -12,7 +12,7 @@ const DEFAULT_OPTIONS = { /** * @description Get all features states aggregates. * @param {string} selector - Device selector. - * @param {Object} options - Options of the query. + * @param {object} options - Options of the query. * @param {string} options.from - Start date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" * or "yyyy-mm-dd hh:mm:ss:sss" (GMT time). * @param {string} [options.to] - End date in UTC format "yyyy-mm-ddThh:mm:ss:sssZ" @@ -38,7 +38,7 @@ async function getDeviceFeaturesStates(selector, options) { // Default end date is now const toDate = options.to ? new Date(options.to) : new Date(); - const optionsWithDefault = Object.assign({}, DEFAULT_OPTIONS, options); + const optionsWithDefault = { ...DEFAULT_OPTIONS, ...options }; const queryParams = { raw: true, diff --git a/server/test/controllers/device/device.controller.test.js b/server/test/controllers/device/device.controller.test.js index a5b557d5b0..38a51acff3 100644 --- a/server/test/controllers/device/device.controller.test.js +++ b/server/test/controllers/device/device.controller.test.js @@ -114,7 +114,7 @@ describe('GET /api/v1/device_feature/:device_feature_selector/states', () => { this.timeout(1000); await insertStates(1); }); - it('should get device feature states by selector', async function Test() { + it('should get device feature states by selector', async () => { const now = new Date(); const dateState = `${now.getUTCFullYear()}-${`0${now.getUTCMonth() + 1}`.slice(-2)}-${`0${now.getUTCDate()}`.slice( -2,