diff --git a/CHANGELOG.md b/CHANGELOG.md index b478bb3..5a98fc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## node-red-contrib-letsfiware-NGSI v0.4.2 + +- Fix shared buffer in entities node (#30) + ## node-red-contrib-letsfiware-NGSI v0.4.0-next - Update copyright date (#28) @@ -30,4 +34,4 @@ ## node-red-contrib-NGSI-LD v0.0.1 - 13 April, 2023 -- Initial release +- Initial release \ No newline at end of file diff --git a/package.json b/package.json index c532f90..ca2a8fc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-contrib-ngsi-ld", - "version": "0.4.1", + "version": "0.4.2", "description": "Node-RED implementation for NGSI-LD", "scripts": { "lint": "./node_modules/eslint/bin/eslint.js --ext .json,.js src test", diff --git a/src/nodes/NGSI-LD/entities/entities.js b/src/nodes/NGSI-LD/entities/entities.js index 40b2944..0e72ab9 100644 --- a/src/nodes/NGSI-LD/entities/entities.js +++ b/src/nodes/NGSI-LD/entities/entities.js @@ -79,52 +79,6 @@ const getEntities = async function (msg, param) { param.buffer.close(); }; -const nobuffering = { - node: null, - msg: null, - open: function (node, msg) { - this.node = node; - this.msg = msg; - return this; - }, - send: function (entities) { - const message = Object.assign({}, this.msg); - message.payload = entities; - message.statusCode = 200; - this.node.send(message); - }, - close: function () {}, - out: function (entities) { - const message = Object.assign({}, this.msg); - message.payload = entities; - message.statusCode = 200; - this.node.send(message); - } -}; - -const buffering = { - node: null, - msg: null, - entities: [], - open: function (node, msg) { - this.node = node; - this.msg = msg; - this.entities = []; - return this; - }, - send: function (entities) { - this.entities = this.entities.concat(entities); - }, - close: function () { - this.msg.payload = this.entities; - this.msg.statusCode = 200; - this.node.send(this.msg); - }, - out: function (entities) { - this.entities = this.entities.concat(entities); - } -}; - const validateConfig = function (msg, config) { const items = [ 'atContext', @@ -242,6 +196,52 @@ const createParam = function (msg, config, brokerConfig) { return null; } + const buffering = { + node: null, + msg: null, + entities: [], + open: function (node, msg) { + this.node = node; + this.msg = msg; + this.entities = []; + return this; + }, + send: function (entities) { + this.entities = this.entities.concat(entities); + }, + close: function () { + this.msg.payload = this.entities; + this.msg.statusCode = 200; + this.node.send(this.msg); + }, + out: function (entities) { + this.entities = this.entities.concat(entities); + } + }; + + const nobuffering = { + node: null, + msg: null, + open: function (node, msg) { + this.node = node; + this.msg = msg; + return this; + }, + send: function (entities) { + const message = Object.assign({}, this.msg); + message.payload = entities; + message.statusCode = 200; + this.node.send(message); + }, + close: function () {}, + out: function (entities) { + const message = Object.assign({}, this.msg); + message.payload = entities; + message.statusCode = 200; + this.node.send(message); + } + }; + param.buffer = param.config.buffering ? buffering.open(this, msg) : nobuffering.open(this, msg); return param; diff --git a/test/unit/entities_spec.js b/test/unit/entities_spec.js index cb945d7..4a3e87f 100644 --- a/test/unit/entities_spec.js +++ b/test/unit/entities_spec.js @@ -1,22 +1,22 @@ /* MIT License - + Copyright 2023-2024 Kazuhito Suda - + This file is part of node-red-contrib-NGSI-LD - + https://github.com/lets-fiware/node-red-contrib-NGSI-LD - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -39,6 +39,52 @@ const { assert } = require('chai'); const entitiesNode = require('../../src/nodes/NGSI-LD/entities/entities.js'); const MockRed = require('./helpers/mockred.js'); +const buffering = { + node: null, + msg: null, + entities: [], + open: function (node, msg) { + this.node = node; + this.msg = msg; + this.entities = []; + return this; + }, + send: function (entities) { + this.entities = this.entities.concat(entities); + }, + close: function () { + this.msg.payload = this.entities; + this.msg.statusCode = 200; + this.node.send(this.msg); + }, + out: function (entities) { + this.entities = this.entities.concat(entities); + } +}; + +const nobuffering = { + node: null, + msg: null, + open: function (node, msg) { + this.node = node; + this.msg = msg; + return this; + }, + send: function (entities) { + const message = Object.assign({}, this.msg); + message.payload = entities; + message.statusCode = 200; + this.node.send(message); + }, + close: function () {}, + out: function (entities) { + const message = Object.assign({}, this.msg); + message.payload = entities; + message.statusCode = 200; + this.node.send(message); + } +}; + describe('entities.js', () => { describe('getEntities', () => { afterEach(() => { @@ -81,7 +127,6 @@ describe('entities.js', () => { }); const getEntities = entitiesNode.__get__('getEntities'); - const nobuffering = entitiesNode.__get__('nobuffering'); let actual; const param = { @@ -166,7 +211,6 @@ describe('entities.js', () => { }); const getEntities = entitiesNode.__get__('getEntities'); - const buffering = entitiesNode.__get__('buffering'); let actual = []; const param = { @@ -255,7 +299,6 @@ describe('entities.js', () => { }); const getEntities = entitiesNode.__get__('getEntities'); - const nobuffering = entitiesNode.__get__('nobuffering'); let actual; const param = { @@ -294,7 +337,6 @@ describe('entities.js', () => { }); const getEntities = entitiesNode.__get__('getEntities'); - const nobuffering = entitiesNode.__get__('nobuffering'); let actual; const param = { @@ -331,7 +373,6 @@ describe('entities.js', () => { buildParams: () => new URLSearchParams() }); const getEntities = entitiesNode.__get__('getEntities'); - const nobuffering = entitiesNode.__get__('nobuffering'); const param = { host: 'http://orion-ld:1026', @@ -377,7 +418,6 @@ describe('entities.js', () => { decodeNGSI: (data) => data }); const getEntities = entitiesNode.__get__('getEntities'); - const nobuffering = entitiesNode.__get__('nobuffering'); const param = { host: 'http://orion-ld:1026', @@ -418,7 +458,6 @@ describe('entities.js', () => { decodeNGSI: (data) => data }); const getEntities = entitiesNode.__get__('getEntities'); - const nobuffering = entitiesNode.__get__('nobuffering'); const param = { host: 'http://orion-ld:1026', @@ -452,8 +491,46 @@ describe('entities.js', () => { }); }); describe('nobuffering', () => { + let nobuffering; + + before(() => { + // Create a param + const createParam = entitiesNode.__get__('createParam'); + const msg = { payload: { idPattern: '.*' } }; + const config = { + representation: 'normalized', + entityId: '', + entityType: '', + idPattern: '', + attrs: '', + sysAttrs: 'false', + query: '', + csf: '', + georel: '', + geometry: '', + coordinates: '', + geoproperty: '', + geometryProperty: '', + lang: '', + accept: 'application/ld+json', + atContext: '', + buffering: 'off' + }; + const brokerConfig = { + apiEndpoint: 'http://orion-ld:1026', + mintaka: '', + getToken: () => {}, + tenant: 'openiot', + atContext: 'https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld' + }; + + const node = { msg: '' }; + const param = createParam.call(node, msg, config, brokerConfig); + + nobuffering = param.buffer; + }); + it('should have a entity', () => { - const nobuffering = entitiesNode.__get__('nobuffering'); const errmsg = {}; const actual = []; @@ -471,7 +548,6 @@ describe('entities.js', () => { assert.deepEqual(actual, [{ payload: [{ id: 'E1', type: 'T' }], statusCode: 200 }]); }); it('should have entities', () => { - const nobuffering = entitiesNode.__get__('nobuffering'); const errmsg = {}; const actual = []; @@ -493,7 +569,6 @@ describe('entities.js', () => { ]); }); it('should be empty', () => { - const nobuffering = entitiesNode.__get__('nobuffering'); const errmsg = {}; const actual = []; @@ -510,7 +585,6 @@ describe('entities.js', () => { assert.deepEqual(actual, []); }); it('should have a entity', () => { - const nobuffering = entitiesNode.__get__('nobuffering'); const errmsg = {}; const actual = []; @@ -528,9 +602,48 @@ describe('entities.js', () => { assert.deepEqual(actual, [{ payload: [{ id: 'E1', type: 'T' }], statusCode: 200 }]); }); }); + describe('buffering', () => { + let buffering; + + before(() => { + // Create a param + const createParam = entitiesNode.__get__('createParam'); + const msg = { payload: { idPattern: '.*' } }; + const config = { + representation: 'normalized', + entityId: '', + entityType: '', + idPattern: '', + attrs: '', + sysAttrs: 'false', + query: '', + csf: '', + georel: '', + geometry: '', + coordinates: '', + geoproperty: '', + geometryProperty: '', + lang: '', + accept: 'application/ld+json', + atContext: '', + buffering: 'on' + }; + const brokerConfig = { + apiEndpoint: 'http://orion-ld:1026', + mintaka: '', + getToken: () => {}, + tenant: 'openiot', + atContext: 'https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld' + }; + + const node = { msg: '' }; + const param = createParam.call(node, msg, config, brokerConfig); + + buffering = param.buffer; + }); + it('should have a entity', () => { - const buffering = entitiesNode.__get__('buffering'); const errmsg = {}; const actual = []; @@ -548,7 +661,6 @@ describe('entities.js', () => { assert.deepEqual(actual, [{ payload: [{ id: 'E1', type: 'T' }], statusCode: 200 }]); }); it('should have a entities', () => { - const buffering = entitiesNode.__get__('buffering'); const errmsg = {}; const actual = []; @@ -575,7 +687,6 @@ describe('entities.js', () => { ]); }); it('should be empty', () => { - const buffering = entitiesNode.__get__('buffering'); const errmsg = {}; const actual = []; @@ -592,7 +703,6 @@ describe('entities.js', () => { assert.deepEqual(actual, [{ payload: [], statusCode: 200 }]); }); it('should have a entity', () => { - const buffering = entitiesNode.__get__('buffering'); const errmsg = {}; const actual = []; @@ -1178,7 +1288,86 @@ describe('entities.js', () => { assert.equal(actual, expected); assert.deepEqual(msg, { payload: { error: 'lang not string' } }); }); + + describe('buffering', () => { + it('should create a buffering object', () => { + const createParam = entitiesNode.__get__('createParam'); + const msg = { payload: { idPattern: '.*' } }; + const config = { + representation: 'normalized', + entityId: '', + entityType: '', + idPattern: '', + attrs: '', + sysAttrs: 'false', + query: '', + csf: '', + georel: '', + geometry: '', + coordinates: '', + geoproperty: '', + geometryProperty: '', + lang: '', + accept: 'application/ld+json', + atContext: '', + buffering: 'on' + }; + const brokerConfig = { + apiEndpoint: 'http://orion-ld:1026', + mintaka: '', + getToken: () => {}, + tenant: 'openiot', + atContext: 'https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld' + }; + + const node = { msg: '' }; + const param1 = createParam.call(node, msg, config, brokerConfig); + const param2 = createParam.call(node, msg, config, brokerConfig); + + // Check that params are not the same instance + assert.notEqual(param1, param2); + }); + + it('should create a nobuffering object', () => { + const createParam = entitiesNode.__get__('createParam'); + const msg = { payload: { idPattern: '.*' } }; + const config = { + representation: 'normalized', + entityId: '', + entityType: '', + idPattern: '', + attrs: '', + sysAttrs: 'false', + query: '', + csf: '', + georel: '', + geometry: '', + coordinates: '', + geoproperty: '', + geometryProperty: '', + lang: '', + accept: 'application/ld+json', + atContext: '', + buffering: 'off' + }; + const brokerConfig = { + apiEndpoint: 'http://orion-ld:1026', + mintaka: '', + getToken: () => {}, + tenant: 'openiot', + atContext: 'https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.6.jsonld' + }; + + const node = { msg: '' }; + const param1 = createParam.call(node, msg, config, brokerConfig); + const param2 = createParam.call(node, msg, config, brokerConfig); + + // Check that params are not the same instance + assert.notEqual(param1, param2); + }); + }); }); + describe('NGSI-LD entities node', () => { afterEach(() => { entitiesNode.__ResetDependency__('lib');