• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lets-fiware / node-red-contrib-letsfiware-NGSI / 4208224491

pending completion
4208224491

Pull #88

github

GitHub
Merge ecd42c905 into b3bda1f8a
Pull Request #88: Improve NGSI Batch update custom node

650 of 650 branches covered (100.0%)

Branch coverage included in aggregate %.

1106 of 1106 relevant lines covered (100.0%)

5.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/src/nodes/NGSI/attribute-value/attribute-value.js
1
/*
2
   MIT License
3

4
   Copyright 2022-2023 Kazuhito Suda
5

6
   This file is part of node-red-contrib-letsfiware-NGSI
7

8
   https://github.com/lets-fiware/node-red-contrib-letsfiware-NGSI
9

10
   Permission is hereby granted, free of charge, to any person obtaining a copy
11
   of this software and associated documentation files (the "Software"), to deal
12
   in the Software without restriction, including without limitation the rights
13
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
   copies of the Software, and to permit persons to whom the Software is
15
   furnished to do so, subject to the following conditions:
16

17
   The above copyright notice and this permission notice shall be included in all
18
   copies or substantial portions of the Software.
19

20
   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
   SOFTWARE.
27
 */
28

29
'use strict';
30

31
const lib = require('../../../lib.js');
1✔
32

33
const typeConversion = function (value) {
1✔
34
  if (typeof value === 'object') {
8✔
35
    return value;
2✔
36
  }
37
  if (typeof value === 'boolean') {
6✔
38
    return value;
2✔
39
  }
40
  if (value === 'null') {
4✔
41
    return null;
1✔
42
  }
43
  if (!isNaN(value)) {
3✔
44
    return Number(value);
2✔
45
  }
46

47
  return value;
1✔
48
};
49

50
const attrValue = async function (param) {
1✔
51

52
  const options = {
4✔
53
    method: param.method,
54
    baseURL: param.host,
55
    url: param.pathname,
56
    headers: await lib.buildHTTPHeader(param),
57
    params: lib.buildParams(param.config),
58
  };
59

60
  if (typeof param.config.value !== 'undefined') {
4✔
61
    options.data = param.config.value;
1✔
62
  }
63

64
  try {
4✔
65
    const res = await lib.http(options);
4✔
66
    if (res.status === 200 && param.config.actionType === 'read') {
3✔
67
      return typeConversion(res.data);
1✔
68
    } else if (res.status === 204 && param.config.actionType === 'update') {
2✔
69
      return Number(res.status);
1✔
70
    } else {
71
      this.error(`Error while managing attribute value: ${res.status} ${res.statusText}`);
1✔
72
      return null;
1✔
73
    }
74
  } catch (error) {
75
    this.error(`Exception while managing attribute value: ${error}`);
1✔
76
    return null;
1✔
77
  }
78
};
79

80
function createParam(msg, defaultConfig, openAPIsConfig) {
81
  if (defaultConfig.actionType === 'payload') {
15✔
82
    if (msg.payload !== null && typeof msg.payload === 'object' && !Array.isArray(msg.payload) && 'actionType' in msg.payload) {
3✔
83
      defaultConfig = Object.assign(defaultConfig, msg.payload);
2✔
84
    } else {
85
      this.error('actionType not found');
1✔
86
      return;
1✔
87
    }
88
  } else {
89
    if (defaultConfig.actionType === 'read') {
12✔
90
      ['id', 'type', 'attrName', 'skipForwarding'].forEach(e => {
5✔
91
        if (e in msg.payload) {
20✔
92
          defaultConfig[e] = msg.payload[e];
4✔
93
        }
94
      });
95
    } else {
96
      defaultConfig.value = msg.payload;
7✔
97
    }
98
  }
99

100
  if (defaultConfig.id === '') {
14✔
101
    this.error('Entity id not found');
1✔
102
    return null;
1✔
103
  }
104

105
  if (defaultConfig.attrName === '') {
13✔
106
    this.error('Attribute name not found');
1✔
107
    return null;
1✔
108
  }
109

110
  const options = ['skipForwarding', 'forcedUpdate', 'flowControl'];
12✔
111

112
  for (let i = 0; i < options.length; i++) {
12✔
113
    if (typeof defaultConfig[options[i]] !== 'boolean') {
34✔
114
      this.error(options[i] + ' not boolean');
1✔
115
      return null;
1✔
116
    }
117
  }
118

119
  const param = {
11✔
120
    host: openAPIsConfig.apiEndpoint,
121
    pathname: '/v2/entities/' + defaultConfig.id + '/attrs/' + defaultConfig.attrName + '/value',
122
    getToken: openAPIsConfig.getToken === null ? null : openAPIsConfig.getToken.bind(openAPIsConfig),
11✔
123
  };
124

125
  switch (defaultConfig.actionType) {
11✔
126
    case 'read':
127
      param.method = 'get';
3✔
128
      delete defaultConfig.value;
3✔
129
      delete defaultConfig.forcedUpdate;
3✔
130
      delete defaultConfig.flowControl;
3✔
131
      break;
3✔
132
    case 'update':
133
      param.method = 'put';
7✔
134
      if (!('value' in defaultConfig)) {
7✔
135
        this.error('Attribute value not found');
1✔
136
        return null;
1✔
137
      }
138
      param.contentType = typeof defaultConfig.value === 'object' || Array.isArray(defaultConfig.value) ? 'application/json' : 'text/plain';
6✔
139
      switch (typeof defaultConfig.value) {
6✔
140
        case 'boolean':
141
        case 'number':
142
          defaultConfig.value = '' + defaultConfig.value;
2✔
143
          break;
2✔
144
        case 'string':
145
          defaultConfig.value = '"' + defaultConfig.value + '"';
1✔
146
          break;
1✔
147
        case 'object':
148
          if (defaultConfig.value === null) {
3✔
149
            defaultConfig.value = '' + defaultConfig.value;
2✔
150
            param.contentType = 'text/plain';
2✔
151
          }
152
      }
153
      delete defaultConfig.skipForwarding;
6✔
154
      break;
6✔
155
    default:
156
      this.error('ActionType error: ' + defaultConfig.actionType);
1✔
157
      return null;
1✔
158
  }
159

160
  param.config = defaultConfig;
9✔
161

162
  [param.config.service, param.config.servicepath] = lib.getServiceAndServicePath(msg, openAPIsConfig.service.trim(), defaultConfig.servicepath);
9✔
163

164
  return param;
9✔
165
}
166

167
module.exports = function (RED) {
1✔
168
  function NGSIAttributeValue(config) {
169
    RED.nodes.createNode(this, config);
4✔
170
    const node = this;
4✔
171

172
    const openAPIsConfig = RED.nodes.getNode(config.openapis);
4✔
173

174
    node.on('input', async function (msg) {
4✔
175
      if (openAPIsConfig.geType !== 'orion') {
4✔
176
        node.error('FIWARE GE type not Orion');
1✔
177
        return;
1✔
178
      }
179

180
      const defaultConfig = {
3✔
181
        service: openAPIsConfig.service.trim(),
182
        servicepath: config.servicepath.trim(),
183
        actionType: config.actionType,
184
        id: config.entityId.trim(),
185
        type: config.entityType.trim(),
186
        attrName: config.attrName.trim(),
187
        skipForwarding: config.skipForwarding === 'true',
188
        forcedUpdate: config.forcedUpdate === 'true',
189
        flowControl: config.flowControl === 'true',
190
      };
191

192
      const param = createParam.call(node, msg, defaultConfig, openAPIsConfig);
3✔
193

194
      if (param) {
3✔
195
        const result = await attrValue.call(node, param);
2✔
196
        msg.payload = result;
2✔
197
        node.send(msg);
2✔
198
      }
199
    });
200
  }
201
  RED.nodes.registerType('NGSI Attribute value', NGSIAttributeValue);
4✔
202
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc