Skip to content

Commit 2918c88

Browse files
committed
Refactor common code
1 parent bc09893 commit 2918c88

6 files changed

Lines changed: 126 additions & 33 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ and migrated off watson-developer-cloud to ibm-watson as a npm dependancy.
1515
Migrated nodes will not be compatible with pre 1.0.0 versions of node-red.
1616
During the migration there will be a dependancy on both modules.
1717
- Bump dependancy on node to >=10.0.0
18-
- Bump dependancy on cfenv, request
18+
- Bump dependancy on cfenv, request, file-type
1919
- Remove dependancy on ibm-cloud-sdk-core.
2020
- Node-RED & IBM-Watson & Use of promises on API invokation & IAM URL construct migration & Removal of default endpoint of
2121
- Tone Analyzer node.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"dependencies": {
66
"async": "^1.5.2",
77
"cfenv": "~1.2.2",
8-
"file-type": "^10.9.0",
8+
"file-type": "^12.4.2",
99
"request": "~2.88.0",
1010
"temp": "^0.9.0",
1111
"qs": "6.x",

services/visual_recognition/v3.js

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ module.exports = function(RED) {
3939
fileType = require('file-type'),
4040
fs = require('fs'),
4141
async = require('async'),
42-
toArray = require('stream-to-array'),
4342
sAPIKey = null,
4443
apikey = '',
4544
service = null,
@@ -123,31 +122,6 @@ module.exports = function(RED) {
123122
}
124123

125124

126-
// Even though Visual Reconition SDK can accept a filestream as input
127-
// it can't handle the one on msg.payload, so read it into a buffer
128-
function checkForStream(msg) {
129-
var p = new Promise(function resolver(resolve, reject) {
130-
if (payloadutils.isReadableStream(msg.payload)) {
131-
//msg.payload.resume();
132-
toArray(msg.payload)
133-
.then(function(parts) {
134-
var buffers = [], part = null;
135-
136-
for (var i = 0; i < parts.length; ++i) {
137-
part = parts[i];
138-
buffers.push((part instanceof Buffer) ? part : new Buffer(part));
139-
}
140-
msg.payload = Buffer.concat(buffers);
141-
resolve();
142-
});
143-
} else {
144-
resolve();
145-
}
146-
});
147-
return p;
148-
149-
}
150-
151125
function verifyServiceCredentials(node, msg) {
152126
// If it is present the newly provided user entered key
153127
// takes precedence over the existing one.
@@ -530,7 +504,7 @@ module.exports = function(RED) {
530504
})
531505
.then(function(f) {
532506
feature = f;
533-
return checkForStream(msg);
507+
return payloadutils.checkForStream(msg);
534508
})
535509
.then(function() {
536510
return verifyInputs(feature, msg);

services/visual_recognition/v4-collection-utils.html

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
<option value="updateCollection">Update name or description of a collection</option>
4444
<option value="deleteCollection">Delete a collection</option>
4545
<option value="deleteAllCollections">Delete all collections</option>
46+
<option disabled>______________</option>
47+
<option value="addImages">Add image to collection</option>
4648
</select>
4749
</div>
4850
</script>
@@ -115,6 +117,7 @@
115117
<li><b>Update collection</b></li>
116118
<li><b>Delete collection</b></li>
117119
<li><b>Delete all collections</b></li>
120+
<li><b>Add images to collection</b></li>
118121
</ul>
119122

120123
<p>All Results will made available at <code>msg.payload</code></p>
@@ -166,6 +169,17 @@
166169

167170
<p><b>Delete all collections</b></p>
168171
<p>No input parameters are required : </p>
169-
172+
173+
<p><b>Add images to collection</b></p>
174+
<p>this feature should be provided in input : </p>
175+
<ul>
176+
<li><code>msg.payload</code> : Either a zip of image files or an array of URLs of image files (Required)</li>
177+
<li><code>msg.params["collectionId"]</code> : The identifier of the collection (Required)</li>
178+
<li><code>msg.params["trainingData"]</code> : Training data if a single image is provided locating objects in image as a JSON object (Optional)</li>
179+
</ul>
180+
<p>More information on this
181+
<a href="https://cloud.ibm.com/apidocs/visual-recognition/visual-recognition-v4?code=node#add-images">
182+
API documentation</a>.</p>
183+
170184

171185
</script>

services/visual_recognition/v4-collection-utils.js

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,17 @@ module.exports = function(RED) {
2323
'createCollection': ['name', 'description'],
2424
'getCollection': ['collectionId'],
2525
'updateCollection': ['collectionId'],
26-
'deleteCollection': ['collectionId']
26+
'deleteCollection': ['collectionId'],
27+
'addImages': ['collectionId']
2728
};
2829

2930
var pkg = require('../../package.json'),
3031
serviceutils = require('../../utilities/service-utils'),
3132
payloadutils = require('../../utilities/payload-utils'),
3233
temp = require('temp'),
34+
fileType = require('file-type'),
35+
fs = require('fs'),
36+
fsp = require('fs').promises,
3337
sAPIKey = null,
3438
apikey = '',
3539
service = null,
@@ -238,6 +242,7 @@ module.exports = function(RED) {
238242
case 'getCollection':
239243
case 'updateCollection':
240244
case 'deleteCollection':
245+
case 'addImages':
241246
theMissing = paramCheckFor(REQUIRED_PARAMS[feature], msg);
242247
if (theMissing.length === 0) {
243248
return Promise.resolve();
@@ -257,6 +262,72 @@ module.exports = function(RED) {
257262
}
258263
}
259264

265+
function bufferCheck(data) {
266+
return data instanceof Buffer;
267+
}
268+
269+
function zipCheck(msg) {
270+
return new Promise(function resolver(resolve, reject) {
271+
if (!bufferCheck(msg.payload)) {
272+
reject('msg.payload is neither an array or urls or a zip');
273+
} else {
274+
let ft = fileType(msg.payload);
275+
if (!ft || !ft.ext || 'zip' != ft.ext) {
276+
reject('msg.payload is not a zip');
277+
} else {
278+
resolve();
279+
}
280+
}
281+
});
282+
}
283+
284+
function setImagesFileParam(msg) {
285+
return new Promise(function resolver(resolve, reject) {
286+
temp.open({
287+
suffix: '.' + 'zip'
288+
}, function(err, info) {
289+
if (err) {
290+
reject('Node has been unable to open the zip stream');
291+
}
292+
fsp.writeFile(info.path, msg.payload)
293+
.then(() => {
294+
msg.params['imagesFile'] = fs.createReadStream(info.path);
295+
resolve();
296+
})
297+
.catch((err) => {
298+
reject(err);
299+
})
300+
});
301+
});
302+
}
303+
304+
function processPayload(node, msg) {
305+
return new Promise(function resolver(resolve, reject) {
306+
if ('addImages' !== node.config[FEATURE]) {
307+
resolve();
308+
} else if (Array.isArray(msg.payload)) {
309+
// Payload can be either an array of urls for images
310+
msg.params['imageUrl'] = msg.payload;
311+
resolve();
312+
} else {
313+
// or a zip of images
314+
payloadutils.checkForStream(msg)
315+
.then(() => {
316+
return zipCheck(msg);
317+
})
318+
.then(() => {
319+
return setImagesFileParam(msg);
320+
})
321+
.then(() => {
322+
resolve();
323+
})
324+
.catch((err) => {
325+
reject(err);
326+
});
327+
}
328+
});
329+
}
330+
260331
function verifyPayload(node, msg) {
261332
switch (node.config[FEATURE]) {
262333
case 'createCollection':
@@ -266,8 +337,13 @@ module.exports = function(RED) {
266337
case 'deleteCollection':
267338
case 'deleteAllCollections':
268339
return Promise.resolve();
340+
case 'addImages':
341+
if (!msg.payload) {
342+
return Promise.reject('Missing property: msg.payload');
343+
}
344+
return Promise.resolve();
269345
default:
270-
return Promise.reject('Missing property: msg.payload');
346+
return Promise.reject('Unknown mode has been specified');
271347
}
272348
}
273349

@@ -294,6 +370,9 @@ module.exports = function(RED) {
294370
.then(() => {
295371
return verifyParams(node, msg);
296372
})
373+
.then(() => {
374+
return processPayload(node, msg);
375+
})
297376
.then(() => {
298377
return determineEndpoint(node.config);
299378
})

utilities/payload-utils.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ var url = require('url'),
1818
fileType = require('file-type'),
1919
request = require('request'),
2020
path = require('path'),
21+
toArray = require('stream-to-array'),
2122
stream = require('stream');
2223

2324
function PayloadUtils() {}
@@ -99,6 +100,31 @@ PayloadUtils.prototype = {
99100
request(url).pipe(wstream);
100101
},
101102

103+
// Convert filestream in msg.payload to a buffer
104+
checkForStream: function (msg) {
105+
let me = this;
106+
return new Promise(function resolver(resolve, reject) {
107+
if (me.isReadableStream(msg.payload)) {
108+
//msg.payload.resume();
109+
toArray(msg.payload)
110+
.then(function(parts) {
111+
var buffers = [], part = null;
112+
113+
for (var i = 0; i < parts.length; ++i) {
114+
part = parts[i];
115+
buffers.push((part instanceof Buffer) ? part : new Buffer(part));
116+
}
117+
118+
msg.payload = Buffer.concat(buffers);
119+
resolve();
120+
});
121+
} else {
122+
resolve();
123+
}
124+
});
125+
},
126+
127+
102128
reportError: function (node, msg, err) {
103129
var messageTxt = err;
104130

@@ -162,7 +188,7 @@ PayloadUtils.prototype = {
162188
// default
163189
return cb(txt.split(' ').length);
164190
};
165-
191+
166192
if (ct === 'ja') {
167193
fn = function(txt, cb) {
168194
cb(200);

0 commit comments

Comments
 (0)