diff --git a/README.md b/README.md index e1123692..61d3d158 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,35 @@ Node-RED Watson Nodes for IBM Cloud +### New in version 0.7.5 +- Bump SDK Dependency to 3.15.0 +- Added Portuguese (Brazilian) and Chinese (Simplified and Traditional) as output languages +for Visual Recognition node. +- Added list voices and delete customisation methods to TTS Corpus Builder node. +- STT Node Changes + - Allowing SDK to manage IAM Tokens. + - Streaming mode for STT using IAM key now working. + - Fix to stream mode for max alternatives and smart formatting options + - Keywords, Word Confidence and Customization Weight can now be specified + - Allow Start and End data packets to be specified as JSON objects, as well as +a stringified JSON objects. + - In line with SDK change use createLanguageModel() to create custom model +- Disable SSL Verification option for Assistant Node. +- Natural Language Understanding Node Changes + - Bump Natural Language Understanding to 2018-11-16 + - Add Limit Categories and limit_text_characters options +- Allow JSON input into Personality Insights node. +- Japanese word count was causing a Node-RED crash when run in the cloud. +- Hungarian supported by Language Translator. +- New Document Language Translator node. +- New Assistant V2 Node. +- Discovery Node changes + - Bump Discovery to 2018-12-03 + - Implement Query Notices method +- Bump dependency on file-type to 10.7.0 +- Bump dependency on temp to 0.9.0 + + ### New in version 0.7.4 - Bump SDK Dependency to 3.11.0 - Bump Assistant version to 2018-09-20 diff --git a/package.json b/package.json index 863ebd2b..3eb2beaa 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,16 @@ { "name": "node-red-node-watson", - "version": "0.7.4", + "version": "0.7.5", "description": "A collection of Node-RED nodes for IBM Watson services", "dependencies": { "async": "^1.5.2", "cfenv": "~1.0.0", - "file-type": "^2.7.0", + "file-type": "^10.7.0", "request": "~2.86.0", - "temp": "^0.8.3", + "temp": "^0.9.0", "qs": "6.x", "image-type": "^2.0.2", - "watson-developer-cloud": "^3.11.0", - "kuromoji": "^0.1.1", + "watson-developer-cloud": "^3.15.0", "word-count": "^0.2.2", "is-docx": "^0.0.3", "stream-to-array": "^2.3.0", @@ -35,12 +34,14 @@ ], "node-red": { "nodes": { - "watson-conversation-v1": "services/conversation/v1.js", - "watson-conversation-workspace-manager-v1": "services/conversation/v1-workspace-manager.js", + "watson-conversation-v1": "services/assistant/v1.js", + "watson-conversation-workspace-manager-v1": "services/assistant/v1-workspace-manager.js", + "watson-assistant-v2": "services/assistant/v2.js", "watson-discovery-v1": "services/discovery/v1.js", "watson-discovery-v1-document-loader": "services/discovery/v1-document-loader.js", "watson-discovery-v1-query-builder": "services/discovery/v1-query-builder.js", "watson-language-translator-v3": "services/language_translator/v3.js", + "watson-doc-translator-v3": "services/language_translator/v3-doc.js", "watson-language-translator-identify-v3": "services/language_translator_identify/v3.js", "watson-language-translator-util-v3": "services/language_translator_util/v3.js", "watson-natural-language-classifier-v1": "services/natural_language_classifier/v1.js", diff --git a/services/conversation/icons/conversation-v1-25x25.png b/services/assistant/icons/conversation-v1-25x25.png similarity index 100% rename from services/conversation/icons/conversation-v1-25x25.png rename to services/assistant/icons/conversation-v1-25x25.png diff --git a/services/conversation/v1-exp.html b/services/assistant/v1-exp.html similarity index 100% rename from services/conversation/v1-exp.html rename to services/assistant/v1-exp.html diff --git a/services/conversation/v1-exp.js b/services/assistant/v1-exp.js similarity index 100% rename from services/conversation/v1-exp.js rename to services/assistant/v1-exp.js diff --git a/services/conversation/v1-workspace-manager.html b/services/assistant/v1-workspace-manager.html similarity index 100% rename from services/conversation/v1-workspace-manager.html rename to services/assistant/v1-workspace-manager.html diff --git a/services/conversation/v1-workspace-manager.js b/services/assistant/v1-workspace-manager.js similarity index 100% rename from services/conversation/v1-workspace-manager.js rename to services/assistant/v1-workspace-manager.js diff --git a/services/conversation/v1.html b/services/assistant/v1.html similarity index 100% rename from services/conversation/v1.html rename to services/assistant/v1.html diff --git a/services/conversation/v1.js b/services/assistant/v1.js similarity index 100% rename from services/conversation/v1.js rename to services/assistant/v1.js diff --git a/services/assistant/v2.html b/services/assistant/v2.html new file mode 100644 index 00000000..01f1a59f --- /dev/null +++ b/services/assistant/v2.html @@ -0,0 +1,238 @@ + + + + + + + + diff --git a/services/assistant/v2.js b/services/assistant/v2.js new file mode 100644 index 00000000..a1db637c --- /dev/null +++ b/services/assistant/v2.js @@ -0,0 +1,351 @@ +/** + * Copyright 2018 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function(RED) { + const SERVICE_IDENTIFIER = 'assistant', + OLD_SERVICE_IDENTIFIER = 'conversation', + SERVICE_VERSION = '2018-11-08'; + + var pkg = require('../../package.json'), + AssistantV2 = require('watson-developer-cloud/assistant/v2'), + serviceutils = require('../../utilities/service-utils'), + payloadutils = require('../../utilities/payload-utils'), + service = null, + sApikey = null, + sUsername = null, + sPassword = null; + + service = serviceutils.getServiceCreds(SERVICE_IDENTIFIER); + if (!service) { + service = serviceutils.getServiceCreds(OLD_SERVICE_IDENTIFIER); + } + + if (service) { + sUsername = service.username ? service.username : ''; + sPassword = service.password ? service.password : ''; + sApikey = service.apikey ? service.apikey : ''; + } + + RED.httpAdmin.get('/watson-assistant-v2/vcap', function(req, res) { + res.json(service ? { + bound_service: true + } : null); + }); + + function Node(config) { + var node = this; + RED.nodes.createNode(this, config); + + function setCredentials(msg) { + var creds = { + username : sUsername || node.credentials.username, + password : sPassword || node.credentials.password || config.password, + apikey : sApikey || node.credentials.apikey || config.apikey, + }; + + if (msg.params) { + if (msg.params.username) { + creds.username = msg.params.username; + } + if (msg.params.password) { + creds.password = msg.params.password; + } + if (msg.params.apikey) { + creds.apiKey = msg.params.apikey; + } + } + + return creds; + } + + function credentialCheck(u, p, k) { + if (!k && (!u || !p)) { + return Promise.reject('Missing Watson Assistant service credentials'); + } + return Promise.resolve(); + } + + function payloadCheck(msg) { + if (msg.payload && 'string' != typeof msg.payload) { + return Promise.reject('msg.payload must be either empty or a string'); + } + return Promise.resolve(); + } + + function idCheck(msg) { + if (!config.assistant_id && !(msg.params && msg.params.assistant_id)) { + return Promise.reject('Missing assistant_id. Check node documentation.'); + } + return Promise.resolve(); + } + + function setSessionID(msg) { + let session_id = null; + + if (!config.multisession) { + let id = node.context().flow.get('session_id'); + if (id) { + session_id = id; + } + } else if (msg.params && msg.params.session_id) { + session_id = msg.params.session_id; + } + + return session_id; + } + + function checkAndSet(source, target, field) { + if (source[field]) { + target[field] = source[field]; + } + } + + function setContext(msg, params) { + let context = null; + if (msg.params) { + checkAndSet(msg.params, params, 'context'); + } + return context; + } + + function setAdditionalContext(msg, params) { + if (msg.additional_context) { + params.context = params.context ? + params.context : + {'skills' : {'main skill' : {'user_defined': {}}}}; + + for (var prop in msg.additional_context) { + if (msg.additional_context.hasOwnProperty(prop)) { + params.context.skills['main skill']['user_defined'][prop] + = msg.additional_context[prop]; + } + } + } + } + + function setAssistantID(msg, params) { + checkAndSet(config, params, 'assistant_id'); + if (msg.params) { + checkAndSet(msg.params, params, 'assistant_id'); + } + } + + function setInputOptions(msg, params) { + // Setting the flags this way works as their default + // values are false. + ['alternate_intents', + 'return_context', + 'restart', + 'debug'].forEach((f) => { + checkAndSet(config, params.input.options, f); + if (msg.params) { + checkAndSet(msg.params, params.input.options, f); + } + }); + } + + function setParamInputs(msg, params) { + if (msg.params) { + ['intents', + 'entities'].forEach((f) => { + checkAndSet(msg.params, params, f); + }); + } + } + + function buildInputParams(msg) { + let params = { + 'input' : { + 'message_type': 'text', + 'text' : msg.payload, + 'options' : {} + }, + 'session_id' : setSessionID(msg) + }; + + let context = setContext(msg, params); + if (context) { + params.context = context; + } + + setAdditionalContext(msg, params); + setAssistantID(msg, params); + setInputOptions(msg, params); + setParamInputs(msg, params); + + return Promise.resolve(params); + } + + function setServiceSettings(msg, creds) { + const serviceSettings = { + headers: { + 'User-Agent': pkg.name + '-' + pkg.version + } + }; + + let endpoint = '', + optoutLearning = false, + version = SERVICE_VERSION; + + if (creds.apikey) { + serviceSettings.iam_apikey = creds.apikey; + } else { + serviceSettings.username = creds.username; + serviceSettings.password = creds.password; + } + + if (service) { + endpoint = service.url; + } + if (!config['default-endpoint'] && config['service-endpoint']) { + endpoint = config['service-endpoint']; + } + + if (config['optout-learning']){ + optoutLearning = true; + } + + if (config['timeout'] && config['timeout'] !== '0' && isFinite(config['timeout'])){ + serviceSettings.timeout = parseInt(config['timeout']); + } + + // Look for message overrides + if (msg.params) { + if (msg.params.endpoint) { + endpoint = msg.params.endpoint; + } + if (msg.params.version) { + version = msg.params.version; + } + if ((msg.params['optout_learning'])){ + optoutLearning = true; + } + if (msg.params.timeout !== '0' && isFinite(msg.params.timeout)){ + serviceSettings.timeout = parseInt(msg.params.timeout); + } + if (msg.params.disable_ssl_verification){ + serviceSettings.disable_ssl_verification = true; + } + } + + serviceSettings.version = version; + if (endpoint) { + serviceSettings.url = endpoint; + } + if (optoutLearning) { + serviceSettings.headers = serviceSettings.headers || {}; + serviceSettings.headers['X-Watson-Learning-Opt-Out'] = '1'; + } + + return Promise.resolve(serviceSettings); + } + + function buildService(settings) { + node.service = new AssistantV2(settings); + return Promise.resolve(); + } + + function checkSession(params) { + return new Promise(function resolver(resolve, reject){ + if (params.session_id) { + resolve(); + } else { + node.service.createSession({ + assistant_id: params.assistant_id + }, function(err, response) { + if (err) { + reject(err); + } else if (response && response.session_id) { + params.session_id = response.session_id; + if (!config.multisession) { + node.context().flow.set('session_id', params.session_id); + } + resolve(); + } else { + reject('Unable to set session'); + } + }); + } + }); + } + + function messageTurn(params) { + return new Promise(function resolver(resolve, reject){ + node.service.message(params, function(err, body) { + if (err) { + reject(err); + } else { + resolve(body); + } + }); + }); + } + + this.on('input', function(msg) { + var creds = setCredentials(msg), + params = {}; + + node.status({}); + + credentialCheck(creds.username, creds.password, creds.apikey) + .then(function(){ + return payloadCheck(msg); + }) + .then(function(){ + return idCheck(msg); + }) + .then(function(){ + return buildInputParams(msg); + }) + .then(function(p){ + params = p; + return setServiceSettings(msg, creds); + }) + .then(function(settings){ + return buildService(settings); + }) + .then(function(){ + return checkSession(params); + }) + .then(function(){ + node.status({ fill: 'blue', shape: 'dot', text: 'Calling Assistant service ...'}); + return messageTurn(params); + }) + .then(function(body){ + body.session_id = params.session_id; + msg.payload = body; + return Promise.resolve(); + }) + .then(function(){ + node.status({}); + node.send(msg); + }) + .catch(function(err){ + payloadutils.reportError(node,msg,err); + node.send(msg); + }); + + }); + } + + RED.nodes.registerType('watson-assistant-v2', Node, { + credentials: { + username: {type: 'text'}, + password: {type: 'password'}, + apikey: {type: 'password'} + } + }); +}; diff --git a/services/discovery/discovery-utils.js b/services/discovery/discovery-utils.js index 7965872c..b5b5ea3e 100644 --- a/services/discovery/discovery-utils.js +++ b/services/discovery/discovery-utils.js @@ -21,7 +21,7 @@ DiscoveryUtils.prototype = { buildService: function(username, password, apikey, endpoint) { let serviceSettings = { - version_date: '2018-08-01', + version_date: '2018-12-03', headers: { 'User-Agent': pkg.name + '-' + pkg.version } diff --git a/services/discovery/v1-document-loader.js b/services/discovery/v1-document-loader.js index ba14a79c..aa8fed5b 100644 --- a/services/discovery/v1-document-loader.js +++ b/services/discovery/v1-document-loader.js @@ -127,7 +127,7 @@ module.exports = function (RED) { function execute(params, msg, suffix) { var p = new Promise(function resolver(resolve, reject) { let discovery = discoveryutils.buildService(username, password, apikey, endpoint); - + // modify as getting addJsonDocument will be deprecated messages if ('.json' === suffix) { //method = 'addJsonDocument'; diff --git a/services/discovery/v1.html b/services/discovery/v1.html index 60624ecb..de6ad1c0 100644 --- a/services/discovery/v1.html +++ b/services/discovery/v1.html @@ -51,6 +51,7 @@ Method: Search in collection + Query Notices ______________ Create new environment List existing environments @@ -325,6 +326,13 @@ + + Query Notices + Queries for notices (errors or warnings) that might have been generated by the system + For this method the node needs an Environment ID and Collection ID as input. + + + For more information about the Discovery service, @@ -405,6 +413,7 @@ + ', #node-input-configuration_id'); break; case 'query': + case 'queryNotices': fields.push('#node-input-environment_id' + ', #node-input-collection_id' + ', #node-input-count' diff --git a/services/discovery/v1.js b/services/discovery/v1.js index 4a17a3ea..86fdc0d9 100644 --- a/services/discovery/v1.js +++ b/services/discovery/v1.js @@ -48,6 +48,7 @@ module.exports = function (RED) { break; case 'getCollectionDetails': case 'query': + case 'queryNotices': response = discoveryutils.paramEnvCheck(params) + discoveryutils.paramCollectionCheck(params); break; @@ -242,12 +243,27 @@ module.exports = function (RED) { return p; } + function executeQueryNotices(node, discovery, params, msg) { + var p = new Promise(function resolver(resolve, reject){ + discovery.queryNotices(params, function (err, response) { + if (err) { + reject(err); + } else { + msg.search_results = response; + resolve(); + } + }); + }); + return p; + } + function unknownMethod(node, discovery, params, msg) { return Promise.reject('Unable to process as unknown mode has been specified'); } function executeMethod(node, method, params, msg) { let discovery = discoveryutils.buildService(username, password, apikey, endpoint); + var p = null; switch (method) { case 'createEnvrionment': @@ -286,6 +302,9 @@ module.exports = function (RED) { case 'query': p = executeQuery(node, discovery, params, msg); break; + case 'queryNotices': + p = executeQueryNotices(node, discovery, params, msg); + break; default : p = unknownMethod(node, discovery, params, msg); break; diff --git a/services/language_translator/translator-utils.js b/services/language_translator/translator-utils.js new file mode 100644 index 00000000..1752dc10 --- /dev/null +++ b/services/language_translator/translator-utils.js @@ -0,0 +1,39 @@ +/** + * Copyright 2018 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + + +class TranslatorUtils { + constructor() { + } + + credentialCheck(u, p, k) { + if (!k && (!u || !p)) { + return Promise.reject('Missing Watson Language Translator service credentials'); + } + return Promise.resolve(); + } + + checkForAction(action) { + if (!action) { + return Promise.reject('Missing action, please select one'); + } + return Promise.resolve(); + } + +} + +var translatorutils = new TranslatorUtils(); +module.exports = translatorutils ; diff --git a/services/language_translator/v3-doc.html b/services/language_translator/v3-doc.html new file mode 100644 index 00000000..db2b354f --- /dev/null +++ b/services/language_translator/v3-doc.html @@ -0,0 +1,556 @@ + + + + + + + + diff --git a/services/language_translator/v3-doc.js b/services/language_translator/v3-doc.js new file mode 100644 index 00000000..4679a525 --- /dev/null +++ b/services/language_translator/v3-doc.js @@ -0,0 +1,495 @@ +/** + * Copyright 2018 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function (RED) { + const request = require('request'), + SERVICE_IDENTIFIER = 'language-translator', + SERVICE_VERSION = '2018-05-01'; + + var pkg = require('../../package.json'), + LanguageTranslatorV3 = require('watson-developer-cloud/language-translator/v3'), + fs = require('fs'), + fileType = require('file-type'), + temp = require('temp'), + serviceutils = require('../../utilities/service-utils'), + payloadutils = require('../../utilities/payload-utils'), + translatorutils = require('./translator-utils'), + username = null, + password = null, + sUsername = null, + sPassword = null, + apikey = null, + sApikey = null, + endpoint = '', + sEndpoint = 'https://gateway.watsonplatform.net/language-translator/api', + service = serviceutils.getServiceCreds(SERVICE_IDENTIFIER); + + if (service) { + sUsername = service.username ? service.username : ''; + sPassword = service.password ? service.password : ''; + sApikey = service.apikey ? service.apikey : ''; + sEndpoint = service.url; + } + + // Node RED Admin - fetch and set vcap services + RED.httpAdmin.get('/watson-doc-translator/vcap', function (req, res) { + res.json(service ? {bound_service: true} : null); + }); + + + // API used by widget to fetch available models + RED.httpAdmin.get('/watson-doc-translator/models', function (req, res) { + endpoint = req.query.e ? req.query.e : sEndpoint; + var lt = null, + serviceSettings = { + version: SERVICE_VERSION, + url: endpoint, + headers: { + 'User-Agent': pkg.name + '-' + pkg.version + } + }; + + if (sApikey || req.query.key) { + serviceSettings.iam_apikey = sApikey ? sApikey : req.query.key; + } else { + serviceSettings.username = sUsername ? sUsername : req.query.un; + serviceSettings.password = sPassword ? sPassword : req.query.pwd; + } + + lt = new LanguageTranslatorV3(serviceSettings); + + lt.listModels({}, function (err, models) { + if (err) { + res.json(err); + } else { + res.json(models); + } + }); + }); + + + function Node (config) { + var node = this; + RED.nodes.createNode(this, config); + + function payloadCheck(msg, mode) { + var message = null; + switch (mode) { + case 'listDocuments': + case 'documentStatus': + case 'deleteDocument': + case 'getDocument': + case 'translateSubmittedDocument': + break; + case 'translateDocument': + if (!msg.payload) { + message = 'Missing property: msg.payload'; + } + break; + default: + message = 'Unexpected Mode'; + break; + } + if (message) { + return Promise.reject(message); + } + return Promise.resolve(); + } + + function docID(msg) { + if (msg.payload && 'string' === typeof msg.payload) { + return msg.payload; + } + if (msg.payload && + 'object' === typeof msg.payload && + msg.payload.document_id) { + return msg.payload.document_id; + } + return config['document-id']; + } + + function paramCheck(msg, mode) { + var message = null; + switch (mode) { + case 'listDocuments': + case 'translateDocument': + break; + case 'documentStatus': + case 'deleteDocument': + case 'getDocument': + case 'translateSubmittedDocument': + if (!docID(msg)) { + message = 'Document ID is required'; + } + break; + default: + break; + } + if (message) { + return Promise.reject(message); + } + return Promise.resolve(); + } + + + function buildAuthSettings () { + var authSettings = {}; + if (apikey) { + authSettings.user = 'apikey'; + authSettings.pass = apikey; + } else { + authSettings.user = username; + authSettings.pass = password; + } + return authSettings; + } + + function executeRequest(uriAddress, method) { + return new Promise(function resolver(resolve, reject){ + var authSettings = buildAuthSettings(); + + request({ + uri: uriAddress, + method: method, + auth: authSettings + }, (error, response, body) => { + if (error) { + reject(error); + } else { + switch (response.statusCode) { + case 200: + let data = null; + try { + data = JSON.parse(body); + } catch(e) { + data = body; + } + resolve(data); + break; + case 204: + resolve(body); + break; + case 404: + reject('Document not found ' + response.statusCode); + break; + default: + console.log(body); + reject('Error Invoking API ' + response.statusCode); + break; + } + } + }); + + }); + } + + function executeGetRequest(uriAddress) { + return executeRequest(uriAddress, 'GET'); + } + + function executeDeleteRequest(uriAddress) { + return executeRequest(uriAddress, 'DELETE'); + } + + function verifyDocumentPayload (msg) { + if (!msg.payload) { + return Promise.reject('Missing property: msg.payload'); + } else if ( (msg.payload instanceof Buffer) || + (payloadutils.isJsonObject(msg.payload)) ) { + return Promise.resolve(); + } else { + return Promise.reject('msg.payload should be a data buffer or json object'); + } + } + + function determineSuffix(msg) { + var ext = '.json'; + + if (msg.payload instanceof Buffer) { + var ft = fileType(msg.payload); + + if (ft && ft.ext) { + ext = '.' + ft.ext; + } else { + // We don't know what file type, so just assume .txt + ext = '.txt'; + } + } + + return Promise.resolve(ext); + } + + function loadFile(suffix) { + return new Promise(function resolver(resolve, reject){ + var options = {}; + if (suffix) { + options.suffix = suffix; + } + temp.open(options, function(err, info) { + if (err) { + reject('Error opening temp file'); + } else { + resolve(info); + } + }); + }); + } + + function syncTheFile(info, msg) { + return new Promise(function resolver(resolve, reject){ + fs.writeFile(info.path, msg.payload, function(err) { + if (err) { + reject('Error processing buffer'); + } + resolve(); + }); + }); + } + + function createStream(info) { + //var theStream = fs.createReadStream(info.path, 'utf8'); + var theStream = fs.readFileSync(info.path, 'utf8'); + return Promise.resolve(theStream); + } + + function whatName(msg, suffix){ + var name = 'Doc ' + (new Date()).toString(); // + suffix; + if (msg && msg.filename) { + name = msg.filename; + } else if (config && config.filename ) { + name = config.filename; + } + name = name.replace(/[^0-9a-z]/gi, ''); + return (name + suffix); + } + + function sourceLang(msg) { + if (msg.payload && + 'object' === typeof msg.payload && + msg.payload.source) { + return msg.payload.source; + } + return msg.srclang ? msg.srclang : config.srclang; + } + + function executePostRequest(uriAddress, params, msg) { + return new Promise(function resolver(resolve, reject){ + var authSettings = buildAuthSettings(); + + request({ + uri: uriAddress, + method: 'POST', + auth: authSettings, + formData: params + }, (error, response, body) => { + if (!error && response.statusCode === 200) { + let data = JSON.parse(body); + resolve(data); + } else if (error) { + reject(error); + } else { + reject('Error performing request ' + response.statusCode + ' ' + body); + } + }); + }); + } + + function executeUnknownMethod(msg) { + return Promise.reject('Unable to process as unknown mode has been specified'); + } + + function executeListDocuments(msg) { + let uriAddress = `${endpoint}/v3/documents?version=${SERVICE_VERSION}`; + return executeGetRequest(uriAddress); + } + + function executeGetDocumentStatus(msg) { + var docid = docID(msg); + let uriAddress = `${endpoint}/v3/documents/${docid}?version=${SERVICE_VERSION}`; + + return executeGetRequest(uriAddress); + } + + function executeGetDocument(msg) { + var docid = docID(msg); + let uriAddress = `${endpoint}/v3/documents/${docid}/translated_document?version=${SERVICE_VERSION}`; + + return executeGetRequest(uriAddress); + } + + function executeDeleteDocument(msg) { + var docid = docID(msg); + //let uriAddress = endpoint + '/v3/documents/' + docid + '?version=' + SERVICE_VERSION; + let uriAddress = `${endpoint}/v3/documents/${docid}?version=${SERVICE_VERSION}`; + + return executeDeleteRequest(uriAddress); + } + + function executeTranslateSubmittedDocument(msg) { + let uriAddress = `${endpoint}/v3/documents?version=${SERVICE_VERSION}`; + var params = { + 'source' : sourceLang(msg), + 'target' : msg.destlang ? msg.destlang : config.destlang, + 'document_id' : docID(msg) + }; + return executePostRequest(uriAddress, params, msg); + } + + function executeTranslateDocument(msg) { + var p = null, + fileInfo = null, + fileSuffix = ''; + let uriAddress = `${endpoint}/v3/documents?version=${SERVICE_VERSION}`; + + p = verifyDocumentPayload(msg) + .then (() => { + return determineSuffix(msg); + }) + .then ((suffix) => { + //return loadFile(uriAddress, msg, ext); + fileSuffix = suffix; + return loadFile(suffix); + }) + .then ((info) => { + fileInfo = info; + return syncTheFile(fileInfo, msg); + }) + .then(function(){ + return createStream(fileInfo); + }) + .then(function(theStream){ + //params.file = theStream; + //var fname = 'temp' + fileSuffix; + var params = { + 'source' : msg.srclang ? msg.srclang : config.srclang, + 'target' : msg.destlang ? msg.destlang : config.destlang + }; + var fname = whatName(msg, fileSuffix); + + params.file = { + value: theStream, + options: { + filename: fname + } + }; + + //return Promise.reject('temp disabled'); + return executePostRequest(uriAddress, params, msg); + }); + + return p; + } + + + function executeAction(msg, action) { + var f = null; + + const execute = { + 'listDocuments' : executeListDocuments, + 'translateDocument' : executeTranslateDocument, + 'translateSubmittedDocument' : executeTranslateSubmittedDocument, + 'documentStatus' : executeGetDocumentStatus, + 'deleteDocument' : executeDeleteDocument, + 'getDocument' : executeGetDocument + }; + + f = execute[action] || executeUnknownMethod; + node.status({ fill: 'blue', shape: 'dot', text: 'processing' }); + return f(msg); + } + + function processResponse(msg, data) { + msg.payload = data; + return Promise.resolve(); + } + + function doit(msg) { + let action = msg.action || config.action; + + translatorutils.credentialCheck(username, password, apikey) + .then(function(){ + return translatorutils.checkForAction(action); + }) + .then(function(){ + return payloadCheck(msg, action); + }) + .then(function(){ + return paramCheck(msg, action); + }) + .then(function(){ + node.status({fill:'blue', shape:'dot', text:'executing'}); + return executeAction(msg, action); + }) + .then( (data) => { + node.status({ fill: 'blue', shape: 'dot', text: 'processing response' }); + return processResponse(msg, data); + }) + .then(function(){ + temp.cleanup(); + node.status({}); + node.send(msg); + }) + .catch(function(err){ + temp.cleanup(); + payloadutils.reportError(node, msg, err); + node.send(msg); + }); + } + + this.on('input', function (msg) { + // The dynamic nature of this node has caused problems with the password field. it is + // hidden but not a credential. If it is treated as a credential, it gets lost when there + // is a request to refresh the model list. + // Credentials are needed for each of the modes. + username = sUsername || this.credentials.username; + password = sPassword || this.credentials.password || config.password; + apikey = sApikey || this.credentials.apikey || config.apikey; + + endpoint = sEndpoint; + if ((!config['default-endpoint']) && config['service-endpoint']) { + endpoint = config['service-endpoint']; + } + + node.status({}); + temp.track(); + + if ('object' === typeof msg.payload && + msg.payload.documents && + Array.isArray(msg.payload.documents)) { + let len = msg.payload.documents.length; + + msg.payload.documents.forEach((e, i) => { + let msgClone = Object.assign({}, msg), + pos = i+1; + node.status({ fill: 'blue', shape: 'dot', text: `Processing document ${pos} of ${len}` }); + msgClone.payload = e; + doit(msgClone); + }); + + } else { + doit(msg); + } + }); + } + + RED.nodes.registerType('watson-doc-translator', Node, { + credentials: { + username: {type:'text'}, + password: {type:'password'}, + apikey: {type:'password'} + } + }); +}; diff --git a/services/language_translator/v3.html b/services/language_translator/v3.html index 99773084..c22152b9 100644 --- a/services/language_translator/v3.html +++ b/services/language_translator/v3.html @@ -236,6 +236,7 @@ 'fr' : 'French', 'fi' : 'Finnish', 'hi' : 'Hindi', + 'hu' : 'Hungarian', 'it' : 'Italian', 'de' : 'German', 'ja' : 'Japanese', diff --git a/services/language_translator/v3.js b/services/language_translator/v3.js index 469d733d..abce9d87 100644 --- a/services/language_translator/v3.js +++ b/services/language_translator/v3.js @@ -28,6 +28,7 @@ module.exports = function (RED) { //cfenv = require('cfenv'), payloadutils = require('../../utilities/payload-utils'), serviceutils = require('../../utilities/service-utils'), + translatorutils = require('./translator-utils'), fs = require('fs'), temp = require('temp'), username = null, @@ -112,14 +113,6 @@ module.exports = function (RED) { RED.nodes.createNode(this, config); var node = this; - - function initialCheck(u, p, k) { - if (!k && (!u || !p)) { - return Promise.reject('Missing Watson Language Translator service credentials'); - } - return Promise.resolve(); - } - function payloadCheck(msg) { if (!msg.payload) { return Promise.reject('Missing property: msg.payload'); @@ -127,13 +120,6 @@ module.exports = function (RED) { return Promise.resolve(); } - function checkForAction(action) { - if (!action) { - return Promise.reject('Missing action, please select one'); - } - return Promise.resolve(); - } - function checkForGlobalOverides(msg) { // If the selection is to use global overrides then // look for them @@ -456,12 +442,12 @@ module.exports = function (RED) { node.status({}); - initialCheck(username, password, apikey) + translatorutils.credentialCheck(username, password, apikey) .then(function(){ return payloadCheck(msg); }) .then(function(){ - return checkForAction(action); + return translatorutils.checkForAction(action); }) .then(function(){ return checkForGlobalOverides(msg); diff --git a/services/natural_language_understanding/v1.html b/services/natural_language_understanding/v1.html index 407fc133..2c05405a 100644 --- a/services/natural_language_understanding/v1.html +++ b/services/natural_language_understanding/v1.html @@ -21,6 +21,11 @@ + + Name + + + Username @@ -58,6 +63,11 @@ type="checkbox" id="node-input-categories" /> Categories + + Limit Categories + + + - - - Name - + + Limit Text Characters + +
Query Notices
+
Queries for notices (errors or warnings) that might have been generated by the system
For this method the node needs an Environment ID and Collection ID as input. +
For more information about the Discovery service, @@ -405,6 +413,7 @@ + ', #node-input-configuration_id'); break; case 'query': + case 'queryNotices': fields.push('#node-input-environment_id' + ', #node-input-collection_id' + ', #node-input-count' diff --git a/services/discovery/v1.js b/services/discovery/v1.js index 4a17a3ea..86fdc0d9 100644 --- a/services/discovery/v1.js +++ b/services/discovery/v1.js @@ -48,6 +48,7 @@ module.exports = function (RED) { break; case 'getCollectionDetails': case 'query': + case 'queryNotices': response = discoveryutils.paramEnvCheck(params) + discoveryutils.paramCollectionCheck(params); break; @@ -242,12 +243,27 @@ module.exports = function (RED) { return p; } + function executeQueryNotices(node, discovery, params, msg) { + var p = new Promise(function resolver(resolve, reject){ + discovery.queryNotices(params, function (err, response) { + if (err) { + reject(err); + } else { + msg.search_results = response; + resolve(); + } + }); + }); + return p; + } + function unknownMethod(node, discovery, params, msg) { return Promise.reject('Unable to process as unknown mode has been specified'); } function executeMethod(node, method, params, msg) { let discovery = discoveryutils.buildService(username, password, apikey, endpoint); + var p = null; switch (method) { case 'createEnvrionment': @@ -286,6 +302,9 @@ module.exports = function (RED) { case 'query': p = executeQuery(node, discovery, params, msg); break; + case 'queryNotices': + p = executeQueryNotices(node, discovery, params, msg); + break; default : p = unknownMethod(node, discovery, params, msg); break; diff --git a/services/language_translator/translator-utils.js b/services/language_translator/translator-utils.js new file mode 100644 index 00000000..1752dc10 --- /dev/null +++ b/services/language_translator/translator-utils.js @@ -0,0 +1,39 @@ +/** + * Copyright 2018 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + + +class TranslatorUtils { + constructor() { + } + + credentialCheck(u, p, k) { + if (!k && (!u || !p)) { + return Promise.reject('Missing Watson Language Translator service credentials'); + } + return Promise.resolve(); + } + + checkForAction(action) { + if (!action) { + return Promise.reject('Missing action, please select one'); + } + return Promise.resolve(); + } + +} + +var translatorutils = new TranslatorUtils(); +module.exports = translatorutils ; diff --git a/services/language_translator/v3-doc.html b/services/language_translator/v3-doc.html new file mode 100644 index 00000000..db2b354f --- /dev/null +++ b/services/language_translator/v3-doc.html @@ -0,0 +1,556 @@ + + + + + + + + diff --git a/services/language_translator/v3-doc.js b/services/language_translator/v3-doc.js new file mode 100644 index 00000000..4679a525 --- /dev/null +++ b/services/language_translator/v3-doc.js @@ -0,0 +1,495 @@ +/** + * Copyright 2018 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +module.exports = function (RED) { + const request = require('request'), + SERVICE_IDENTIFIER = 'language-translator', + SERVICE_VERSION = '2018-05-01'; + + var pkg = require('../../package.json'), + LanguageTranslatorV3 = require('watson-developer-cloud/language-translator/v3'), + fs = require('fs'), + fileType = require('file-type'), + temp = require('temp'), + serviceutils = require('../../utilities/service-utils'), + payloadutils = require('../../utilities/payload-utils'), + translatorutils = require('./translator-utils'), + username = null, + password = null, + sUsername = null, + sPassword = null, + apikey = null, + sApikey = null, + endpoint = '', + sEndpoint = 'https://gateway.watsonplatform.net/language-translator/api', + service = serviceutils.getServiceCreds(SERVICE_IDENTIFIER); + + if (service) { + sUsername = service.username ? service.username : ''; + sPassword = service.password ? service.password : ''; + sApikey = service.apikey ? service.apikey : ''; + sEndpoint = service.url; + } + + // Node RED Admin - fetch and set vcap services + RED.httpAdmin.get('/watson-doc-translator/vcap', function (req, res) { + res.json(service ? {bound_service: true} : null); + }); + + + // API used by widget to fetch available models + RED.httpAdmin.get('/watson-doc-translator/models', function (req, res) { + endpoint = req.query.e ? req.query.e : sEndpoint; + var lt = null, + serviceSettings = { + version: SERVICE_VERSION, + url: endpoint, + headers: { + 'User-Agent': pkg.name + '-' + pkg.version + } + }; + + if (sApikey || req.query.key) { + serviceSettings.iam_apikey = sApikey ? sApikey : req.query.key; + } else { + serviceSettings.username = sUsername ? sUsername : req.query.un; + serviceSettings.password = sPassword ? sPassword : req.query.pwd; + } + + lt = new LanguageTranslatorV3(serviceSettings); + + lt.listModels({}, function (err, models) { + if (err) { + res.json(err); + } else { + res.json(models); + } + }); + }); + + + function Node (config) { + var node = this; + RED.nodes.createNode(this, config); + + function payloadCheck(msg, mode) { + var message = null; + switch (mode) { + case 'listDocuments': + case 'documentStatus': + case 'deleteDocument': + case 'getDocument': + case 'translateSubmittedDocument': + break; + case 'translateDocument': + if (!msg.payload) { + message = 'Missing property: msg.payload'; + } + break; + default: + message = 'Unexpected Mode'; + break; + } + if (message) { + return Promise.reject(message); + } + return Promise.resolve(); + } + + function docID(msg) { + if (msg.payload && 'string' === typeof msg.payload) { + return msg.payload; + } + if (msg.payload && + 'object' === typeof msg.payload && + msg.payload.document_id) { + return msg.payload.document_id; + } + return config['document-id']; + } + + function paramCheck(msg, mode) { + var message = null; + switch (mode) { + case 'listDocuments': + case 'translateDocument': + break; + case 'documentStatus': + case 'deleteDocument': + case 'getDocument': + case 'translateSubmittedDocument': + if (!docID(msg)) { + message = 'Document ID is required'; + } + break; + default: + break; + } + if (message) { + return Promise.reject(message); + } + return Promise.resolve(); + } + + + function buildAuthSettings () { + var authSettings = {}; + if (apikey) { + authSettings.user = 'apikey'; + authSettings.pass = apikey; + } else { + authSettings.user = username; + authSettings.pass = password; + } + return authSettings; + } + + function executeRequest(uriAddress, method) { + return new Promise(function resolver(resolve, reject){ + var authSettings = buildAuthSettings(); + + request({ + uri: uriAddress, + method: method, + auth: authSettings + }, (error, response, body) => { + if (error) { + reject(error); + } else { + switch (response.statusCode) { + case 200: + let data = null; + try { + data = JSON.parse(body); + } catch(e) { + data = body; + } + resolve(data); + break; + case 204: + resolve(body); + break; + case 404: + reject('Document not found ' + response.statusCode); + break; + default: + console.log(body); + reject('Error Invoking API ' + response.statusCode); + break; + } + } + }); + + }); + } + + function executeGetRequest(uriAddress) { + return executeRequest(uriAddress, 'GET'); + } + + function executeDeleteRequest(uriAddress) { + return executeRequest(uriAddress, 'DELETE'); + } + + function verifyDocumentPayload (msg) { + if (!msg.payload) { + return Promise.reject('Missing property: msg.payload'); + } else if ( (msg.payload instanceof Buffer) || + (payloadutils.isJsonObject(msg.payload)) ) { + return Promise.resolve(); + } else { + return Promise.reject('msg.payload should be a data buffer or json object'); + } + } + + function determineSuffix(msg) { + var ext = '.json'; + + if (msg.payload instanceof Buffer) { + var ft = fileType(msg.payload); + + if (ft && ft.ext) { + ext = '.' + ft.ext; + } else { + // We don't know what file type, so just assume .txt + ext = '.txt'; + } + } + + return Promise.resolve(ext); + } + + function loadFile(suffix) { + return new Promise(function resolver(resolve, reject){ + var options = {}; + if (suffix) { + options.suffix = suffix; + } + temp.open(options, function(err, info) { + if (err) { + reject('Error opening temp file'); + } else { + resolve(info); + } + }); + }); + } + + function syncTheFile(info, msg) { + return new Promise(function resolver(resolve, reject){ + fs.writeFile(info.path, msg.payload, function(err) { + if (err) { + reject('Error processing buffer'); + } + resolve(); + }); + }); + } + + function createStream(info) { + //var theStream = fs.createReadStream(info.path, 'utf8'); + var theStream = fs.readFileSync(info.path, 'utf8'); + return Promise.resolve(theStream); + } + + function whatName(msg, suffix){ + var name = 'Doc ' + (new Date()).toString(); // + suffix; + if (msg && msg.filename) { + name = msg.filename; + } else if (config && config.filename ) { + name = config.filename; + } + name = name.replace(/[^0-9a-z]/gi, ''); + return (name + suffix); + } + + function sourceLang(msg) { + if (msg.payload && + 'object' === typeof msg.payload && + msg.payload.source) { + return msg.payload.source; + } + return msg.srclang ? msg.srclang : config.srclang; + } + + function executePostRequest(uriAddress, params, msg) { + return new Promise(function resolver(resolve, reject){ + var authSettings = buildAuthSettings(); + + request({ + uri: uriAddress, + method: 'POST', + auth: authSettings, + formData: params + }, (error, response, body) => { + if (!error && response.statusCode === 200) { + let data = JSON.parse(body); + resolve(data); + } else if (error) { + reject(error); + } else { + reject('Error performing request ' + response.statusCode + ' ' + body); + } + }); + }); + } + + function executeUnknownMethod(msg) { + return Promise.reject('Unable to process as unknown mode has been specified'); + } + + function executeListDocuments(msg) { + let uriAddress = `${endpoint}/v3/documents?version=${SERVICE_VERSION}`; + return executeGetRequest(uriAddress); + } + + function executeGetDocumentStatus(msg) { + var docid = docID(msg); + let uriAddress = `${endpoint}/v3/documents/${docid}?version=${SERVICE_VERSION}`; + + return executeGetRequest(uriAddress); + } + + function executeGetDocument(msg) { + var docid = docID(msg); + let uriAddress = `${endpoint}/v3/documents/${docid}/translated_document?version=${SERVICE_VERSION}`; + + return executeGetRequest(uriAddress); + } + + function executeDeleteDocument(msg) { + var docid = docID(msg); + //let uriAddress = endpoint + '/v3/documents/' + docid + '?version=' + SERVICE_VERSION; + let uriAddress = `${endpoint}/v3/documents/${docid}?version=${SERVICE_VERSION}`; + + return executeDeleteRequest(uriAddress); + } + + function executeTranslateSubmittedDocument(msg) { + let uriAddress = `${endpoint}/v3/documents?version=${SERVICE_VERSION}`; + var params = { + 'source' : sourceLang(msg), + 'target' : msg.destlang ? msg.destlang : config.destlang, + 'document_id' : docID(msg) + }; + return executePostRequest(uriAddress, params, msg); + } + + function executeTranslateDocument(msg) { + var p = null, + fileInfo = null, + fileSuffix = ''; + let uriAddress = `${endpoint}/v3/documents?version=${SERVICE_VERSION}`; + + p = verifyDocumentPayload(msg) + .then (() => { + return determineSuffix(msg); + }) + .then ((suffix) => { + //return loadFile(uriAddress, msg, ext); + fileSuffix = suffix; + return loadFile(suffix); + }) + .then ((info) => { + fileInfo = info; + return syncTheFile(fileInfo, msg); + }) + .then(function(){ + return createStream(fileInfo); + }) + .then(function(theStream){ + //params.file = theStream; + //var fname = 'temp' + fileSuffix; + var params = { + 'source' : msg.srclang ? msg.srclang : config.srclang, + 'target' : msg.destlang ? msg.destlang : config.destlang + }; + var fname = whatName(msg, fileSuffix); + + params.file = { + value: theStream, + options: { + filename: fname + } + }; + + //return Promise.reject('temp disabled'); + return executePostRequest(uriAddress, params, msg); + }); + + return p; + } + + + function executeAction(msg, action) { + var f = null; + + const execute = { + 'listDocuments' : executeListDocuments, + 'translateDocument' : executeTranslateDocument, + 'translateSubmittedDocument' : executeTranslateSubmittedDocument, + 'documentStatus' : executeGetDocumentStatus, + 'deleteDocument' : executeDeleteDocument, + 'getDocument' : executeGetDocument + }; + + f = execute[action] || executeUnknownMethod; + node.status({ fill: 'blue', shape: 'dot', text: 'processing' }); + return f(msg); + } + + function processResponse(msg, data) { + msg.payload = data; + return Promise.resolve(); + } + + function doit(msg) { + let action = msg.action || config.action; + + translatorutils.credentialCheck(username, password, apikey) + .then(function(){ + return translatorutils.checkForAction(action); + }) + .then(function(){ + return payloadCheck(msg, action); + }) + .then(function(){ + return paramCheck(msg, action); + }) + .then(function(){ + node.status({fill:'blue', shape:'dot', text:'executing'}); + return executeAction(msg, action); + }) + .then( (data) => { + node.status({ fill: 'blue', shape: 'dot', text: 'processing response' }); + return processResponse(msg, data); + }) + .then(function(){ + temp.cleanup(); + node.status({}); + node.send(msg); + }) + .catch(function(err){ + temp.cleanup(); + payloadutils.reportError(node, msg, err); + node.send(msg); + }); + } + + this.on('input', function (msg) { + // The dynamic nature of this node has caused problems with the password field. it is + // hidden but not a credential. If it is treated as a credential, it gets lost when there + // is a request to refresh the model list. + // Credentials are needed for each of the modes. + username = sUsername || this.credentials.username; + password = sPassword || this.credentials.password || config.password; + apikey = sApikey || this.credentials.apikey || config.apikey; + + endpoint = sEndpoint; + if ((!config['default-endpoint']) && config['service-endpoint']) { + endpoint = config['service-endpoint']; + } + + node.status({}); + temp.track(); + + if ('object' === typeof msg.payload && + msg.payload.documents && + Array.isArray(msg.payload.documents)) { + let len = msg.payload.documents.length; + + msg.payload.documents.forEach((e, i) => { + let msgClone = Object.assign({}, msg), + pos = i+1; + node.status({ fill: 'blue', shape: 'dot', text: `Processing document ${pos} of ${len}` }); + msgClone.payload = e; + doit(msgClone); + }); + + } else { + doit(msg); + } + }); + } + + RED.nodes.registerType('watson-doc-translator', Node, { + credentials: { + username: {type:'text'}, + password: {type:'password'}, + apikey: {type:'password'} + } + }); +}; diff --git a/services/language_translator/v3.html b/services/language_translator/v3.html index 99773084..c22152b9 100644 --- a/services/language_translator/v3.html +++ b/services/language_translator/v3.html @@ -236,6 +236,7 @@ 'fr' : 'French', 'fi' : 'Finnish', 'hi' : 'Hindi', + 'hu' : 'Hungarian', 'it' : 'Italian', 'de' : 'German', 'ja' : 'Japanese', diff --git a/services/language_translator/v3.js b/services/language_translator/v3.js index 469d733d..abce9d87 100644 --- a/services/language_translator/v3.js +++ b/services/language_translator/v3.js @@ -28,6 +28,7 @@ module.exports = function (RED) { //cfenv = require('cfenv'), payloadutils = require('../../utilities/payload-utils'), serviceutils = require('../../utilities/service-utils'), + translatorutils = require('./translator-utils'), fs = require('fs'), temp = require('temp'), username = null, @@ -112,14 +113,6 @@ module.exports = function (RED) { RED.nodes.createNode(this, config); var node = this; - - function initialCheck(u, p, k) { - if (!k && (!u || !p)) { - return Promise.reject('Missing Watson Language Translator service credentials'); - } - return Promise.resolve(); - } - function payloadCheck(msg) { if (!msg.payload) { return Promise.reject('Missing property: msg.payload'); @@ -127,13 +120,6 @@ module.exports = function (RED) { return Promise.resolve(); } - function checkForAction(action) { - if (!action) { - return Promise.reject('Missing action, please select one'); - } - return Promise.resolve(); - } - function checkForGlobalOverides(msg) { // If the selection is to use global overrides then // look for them @@ -456,12 +442,12 @@ module.exports = function (RED) { node.status({}); - initialCheck(username, password, apikey) + translatorutils.credentialCheck(username, password, apikey) .then(function(){ return payloadCheck(msg); }) .then(function(){ - return checkForAction(action); + return translatorutils.checkForAction(action); }) .then(function(){ return checkForGlobalOverides(msg); diff --git a/services/natural_language_understanding/v1.html b/services/natural_language_understanding/v1.html index 407fc133..2c05405a 100644 --- a/services/natural_language_understanding/v1.html +++ b/services/natural_language_understanding/v1.html @@ -21,6 +21,11 @@ +