node-red-node-watson
Version: 
A collection of Node-RED nodes for IBM Watson services
272 lines (231 loc) • 9.57 kB
JavaScript
/**
 * Copyright 2013,2016 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 = 'language-translator',
    LanguageTranslatorV3 = require('ibm-watson/language-translator/v3'),
    { IamAuthenticator } = require('ibm-watson/auth'),
    LANGS = {
      'es': 'Spanish',
      'ar': 'Arabic',
      'arz': 'Spoken Arabic',
      'en': 'English',
      'fr': 'French',
      'it': 'Italian',
      'zh': 'Chinese',
      'ko': 'Korean',
      'pt': 'Portuguese',
      'de': 'German',
      'ja': 'Japanese',
      'nl': 'Dutch',
      'pl': 'Polish',
      'ru': 'Russian',
      'tr': 'Turkish',
      'zh-TW' : 'Taiwanese',
      'zht': 'Traditional Chinese',
      'bg' : 'Bulgarian',
      'ca' : 'Catalan',
      'cs' : 'Czech',
      'da' : 'Danish',
      'el' : 'Greek',
      'et' : 'Estonian',
      'fi' : 'Finnish',
      'ga' : 'Galican',
      'he' : 'Hebrew',
      'hi' : 'Hindi',
      'hr' : 'Croatian',
      'hu' : 'Hungarian',
      'id' : 'Indonesian',
      'lt' : 'Lithuanian',
      'ms' : 'Malay',
      'nb' : 'Norwegian Bokmål',
      'ro' : 'Romanian',
      'sk' : 'Slovak',
      'sl' : 'Slovenian',
      'sv' : 'Swedish',
      'th' : 'Thai'
    };
  var pkg = require('../../package.json'),
    cfenv = require('cfenv'),
    apikey = null, sApikey = null,
    payloadutils = require('../../utilities/payload-utils'),
    serviceutils = require('../../utilities/service-utils'),
    responseutils = require('../../utilities/response-utils'),
    //service = cfenv.getAppEnv().getServiceCreds(/language translator/i),
    service = serviceutils.getServiceCreds(SERVICE_IDENTIFIER),
    endpoint = '',
    sEndpoint = 'https://gateway.watsonplatform.net/language-translator/api';
    //endpointUrl = 'https://gateway.watsonplatform.net/language-translator/api';
  if (service) {
    sApikey = service.apikey ? service.apikey : '';
    sEndpoint = service.url;
  }
  // These are APIs that the node has created to allow it to dynamically fetch IBM Cloud
  // credentials, and also translation models. This allows the node to keep up to
  // date with new tranlations, without the need for a code update of this node.
  // Node RED Admin - fetch and set vcap services
  RED.httpAdmin.get('/watson-translator-util/vcap', function (req, res) {
    res.json(service ? {bound_service: true} : null);
  });
  // This is the Language Translation Node.
  // The node supports four modes
  //
  // 1. translate, for which it will specify a domain, obtained from the available models
  //    along with source and target languages. The node will have only displayed
  //    available translations for the model / domain
  // 2. train, for which a glossary file is required.
  // 3. status, to determine whethere a trained corpus is available
  // 4. delete, to remove a trained corpus extension.
  function SMTNode (config) {
    RED.nodes.createNode(this, config);
    var node = this;
    // 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.
    apikey = sApikey || this.credentials.apikey || config.apikey;
    // The node has received an input as part of a flow, need to determine
    // what the request is for, and based on that if the required fields
    // have been provided.
    this.on('input', function(msg, send, done) {
      let message = '',
        authSettings = {},
        serviceSettings = {
          version: '2018-05-01',
          headers: {
            'User-Agent': pkg.name + '-' + pkg.version
          }
        };
      if (!apikey) {
        message = 'Missing Language Translation service credentials';
        node.error(message, msg);
        return;
      }
      authSettings.apikey = apikey;
      serviceSettings.authenticator = new IamAuthenticator(authSettings);
      endpoint = sEndpoint;
      if ((!config['default-endpoint']) && config['service-endpoint']) {
        endpoint = config['service-endpoint'];
      }
      if (endpoint) {
        serviceSettings.url = endpoint;
      }
      var lt = new LanguageTranslatorV3(serviceSettings);
      // set global variable in order to make them accessible for the tranlsation node
      var globalContext = this.context().global;
      globalContext.set('g_domain','');
      globalContext.set('g_src','');
      globalContext.set('g_dest','');
      globalContext.set('g_model_id','');
      // ---- UTILITY FUNCTIONS ----
      // this functions creates a N dimensional array
      // it will be used in order to make an array of arrays from the wanted options to populate a dashboard dropdown list
      // the entries of the array to be created would be 'domains', 'model_id', 'source' & 'target
      function capitalize (string) {
        return string.charAt(0).toUpperCase() + string.slice(1);
      }
      function makeLanguageBeautifier(string) {
        if (LANGS[string]) {
          return LANGS[string];
        }
        return string;
      }
      // ---- END OF UTILITY FUNCTIONS ----
      if (lt) {
        node.status({fill:'blue', shape:'dot', text:'fetching models'});
        lt.listModels({})
          .then((response) => {
            node.status({fill:'blue', shape:'dot', text:'parsing response'});
            responseutils.parseResponseFor(msg, response, 'models');
            msg.payload = msg.models;
            // the overall array would be used to populate the dropdown list
            var dropdown_array = [];
            var domain_src_target_model = [];
            var domain_src_target = '';
            var sTmp3 = '';
            msg.dropdown_object = {};
            // Populating 'DOMAIN's into an array which would be returned by the msg object
            var ldom = []; // domains array
            msg.options_ldom = {};
            msg.domains = {};
            // Populating 'model_id' into an array which would be returned by the msg object
            var model_id_array = []; // model_id array
            msg.options_model_id = {};
            msg.model_id_obj = {};
            // Populating 'source's into an array which would be returned by the msg object
            var src_lang_array = []; // source language array
            msg.options_src_lang = {};
            msg.src_lang_object = {};
            // Populating 'target's into an array which would be returned by the msg object
            var target_lang_array = []; // dest language array
            msg.options_target_lang = {};
            msg.target_lang_object = {};
            for (var i = 0; i < msg.models.length; i++) {
              ldom[i] = msg.models[i].domain;
              ldom[i] = capitalize(ldom[i]);
              model_id_array[i] = msg.models[i].model_id;
              src_lang_array[i] = msg.models[i].source;
              src_lang_array[i] = makeLanguageBeautifier(src_lang_array[i]);
              target_lang_array[i] = msg.models[i].target;
              target_lang_array[i] = makeLanguageBeautifier(target_lang_array[i]);
              sTmp3 = makeLanguageBeautifier(target_lang_array[i]);
              domain_src_target = ldom[i] + ', ' + src_lang_array[i] + ', ' + target_lang_array[i];
              var j = {};
              j[domain_src_target] = model_id_array[i];
              domain_src_target_model.push(j);
              dropdown_array[i] = domain_src_target_model[i];
            }
            model_id_array.sort();
            dropdown_array.sort();
            // Domains unique values
            ldom.forEach(function(item) {
              msg.options_ldom[item] = item;
            });
            // model_id unique values
            model_id_array.forEach(function(item) {
              msg.options_model_id[item] = item;
            });
            // source language unique values
            src_lang_array.forEach(function(item) {
              msg.options_src_lang[item] = item;
            });
            // target language unique values
            target_lang_array.forEach(function(item) {
              msg.options_target_lang[item] = item;
            });
            msg.domains = Object.keys(msg.options_ldom);
            msg.model_id_obj = Object.keys(msg.options_model_id);
            msg.src_lang_object = Object.keys(msg.options_src_lang);
            msg.target_lang_object = Object.keys(msg.options_target_lang);
            msg.dropdown_object = dropdown_array;
            node.status({});
            send(msg);
            done();
          })
          .catch((err) => {
            let errMsg = payloadutils.reportError(node, msg, err);
            done(errMsg);
          });
      } else {
        node.error('Error instantiating the language service',msg);
      }
    });
  }
  RED.nodes.registerType('watson-translator-util', SMTNode, {
    credentials: {
      apikey: {type:'password'}
    }
  });
};