├── .gitignore
├── CLA.md
├── CONTRIBUTING.md
├── ChangeHistory.md
├── LICENSE
├── README.md
├── codacy
└── codacy-conf.json
├── package.json
├── services
├── assistant
│ ├── icons
│ │ └── conversation-v1-25x25.png
│ ├── v1-workspace-manager.html
│ ├── v1-workspace-manager.js
│ ├── v1.html
│ ├── v1.js
│ ├── v2.html
│ └── v2.js
├── discovery
│ ├── discovery-utils.js
│ ├── discovery-utils2.js
│ ├── icons
│ │ └── discovery.png
│ ├── v1-document-loader.html
│ ├── v1-document-loader.js
│ ├── v1-query-builder.html
│ ├── v1-query-builder.js
│ ├── v1.html
│ ├── v1.js
│ ├── v2-project-manager.html
│ └── v2-project-manager.js
├── language_translator
│ ├── icons
│ │ └── LanguageTranslator25x25.png
│ ├── translator-utils.js
│ ├── v3-doc.html
│ ├── v3-doc.js
│ ├── v3.html
│ └── v3.js
├── language_translator_identify
│ ├── icons
│ │ └── LanguageTranslator25x25.png
│ ├── v3.html
│ └── v3.js
├── language_translator_util
│ ├── icons
│ │ └── LanguageTranslator25x25.png
│ ├── v3.html
│ └── v3.js
├── natural_language_classifier
│ ├── icons
│ │ ├── NLClassifier.png
│ │ └── temp.txt
│ ├── v1.html
│ └── v1.js
├── natural_language_understanding
│ ├── icons
│ │ └── NaturalLanguageUnderstanding.png
│ ├── v1-model-manager.html
│ ├── v1-model-manager.js
│ ├── v1.html
│ └── v1.js
├── speech_to_text
│ ├── icons
│ │ └── speech_to_text.png
│ ├── stt-utils.js
│ ├── v1-corpus-builder.html
│ ├── v1-corpus-builder.js
│ ├── v1.html
│ └── v1.js
├── text_to_speech
│ ├── icons
│ │ └── text_to_speech.png
│ ├── tts-utils.js
│ ├── v1-corpus-builder.html
│ ├── v1-corpus-builder.js
│ ├── v1.html
│ └── v1.js
└── tone_analyzer
│ ├── icons
│ └── tone_analyzer.png
│ ├── tone_analyser_sample.txt
│ ├── v3.html
│ └── v3.js
└── utilities
├── payload-utils.js
├── response-utils.js
├── service-utils.js
└── tone-utils.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/CLA.md:
--------------------------------------------------------------------------------
1 | Dan Cunnington
2 | Yves Le Cleach
3 | Soheel Chughtai
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # CONTRIBUTING
2 |
3 | ## Code
4 |
5 | * The code should follow Watson Developer Cloud [coding guidances](https://github.com/watson-developer-cloud/api-guidelines)
6 | * The code should follow: https://github.com/airbnb/javascript
7 | * 2 spaces identation
8 | * `snake_case`
9 |
10 | ## Issues
11 |
12 | If you encounter an issue with using the labs here, you are welcome to submit
13 | a [bug report](https://github.com/watson_developer_cloud/node-red-labs/issues).
14 | Before that, please search for similar issues. It's possible somebody has already encountered this issue.
15 |
16 | ## Pull Requests
17 |
18 | If you want to contribute to the repository, follow these steps:
19 |
20 | 1. Fork the repo.
21 | 2. Develop code changes.
22 | 5. Commit your changes.
23 | 6. Push to your fork and submit a pull request.
24 |
25 | # Getting start !
26 |
27 | The main steps are :
28 | * check pre-requisites on your laptop (Linux, Mac)
29 | * setup your development environment to use Node-RED on localhost
30 | * coding : modify or create a new node using Node-RED on localhost
31 | * commit your work and pull-request
32 |
33 | Note : this procedure have been tested on OS X Yosemite
34 |
35 | ## Pre-requisites:
36 |
37 | Install a Node version manager if not already done :
38 | - Linux/OS X : use [nvm](https://github.com/creationix/nvm)
39 | Example :
40 | ```
41 | curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
42 | ```
43 | Using npm, install Node 4.5 :
44 | ```
45 | nvm install 4.5
46 | ```
47 | - and use it :
48 | ```
49 | nvm use 4.5
50 | ```
51 | Notice : npm is provided along with Node.js
52 |
53 | ## Setup your development environment
54 | Here are the steps to successfully setup your development environment to contribute to this project
55 |
56 | - Fork the main [Node-RED Watson project](https://github.com/watson-developer-cloud/node-red-node-watson) using your GitHub account (ex: @ylecleach)
57 | - Create a work directory that will contains the source code of your fork
58 | ```
59 | mkdir ~/dev/src ; cd ~/dev/src
60 | git clone https://github.com/ylecleach/node-red-node-watson
61 | ```
62 | **Notice** : replace **ylecleach** by your own GitHub ID
63 |
64 | - create a npm link to your forked project. This will also build this project dependencies.
65 | ```
66 | cd ~/dev/src/node-red-node-watson
67 | npm link
68 | ```
69 |
70 | - Install Node-RED on localhost, assuming we install it on ~/dev directory (you can install it in another location as you wish)
71 | ```
72 | cd ~/dev
73 | npm install node-red
74 | ```
75 |
76 | - Install your fork project into local Node-RED using npm link:
77 | ```
78 | cd ~/dev
79 | npm link node-red-node-watson
80 | ```
81 |
82 | - Starting Node-RED on localhost
83 | ```
84 | cd ~/dev/node_modules/node-red
85 | npm start
86 | ```
87 |
88 | - open Node-RED on [http://localhost:1880](http://localhost:1880) and check you have the Watson nodes properly installed in the Node-RED palette.
89 |
90 | Then you can work on your project locally, update or create nodes, and all you have to do is to stop and start your local Node-RED.
91 |
92 | ## Co-requisites
93 |
94 | If you need to install co-requisites nodes such as the nodes for dropbox support
95 | ```
96 | cd ~/dev
97 | npm install node-red-node-dropbox
98 | ```
99 |
100 |
101 | ## Modify or Create new Watson nodes
102 |
103 | If your want to modify an existing Watson nodes, just search the node in the following file under the node-red / nodes section :
104 | ```
105 | vi ~/dev/src/node-red-node-watson/package.json
106 | ```
107 |
108 | If you want to add a watson node, then you have to create an entry such as :
109 | ```
110 | "watson-dialog": "services/dialog/v1.js"
111 | ```
112 | using the same name convention. For example the Watson Dialog Node is composed of these two files :
113 | - services/dialog/v1.js
114 | - services/dialog/v1.html
115 |
116 | Please refer to those examples and to the [NodeRED.org](http://nodered.org/docs/creating-nodes/) documentation.
117 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Node-RED Watson Nodes for IBM Cloud
2 | =====================================
3 |
4 | [](https://www.codacy.com/app/BetaWorks-NodeRED-Watson/node-red-node-watson?utm_source=github.com&utm_medium=referral&utm_content=watson-developer-cloud/node-red-node-watson&utm_campaign=Badge_Grade)
5 | [](https://www.npmjs.com/package/node-red-node-watson)
6 | [](https://www.npmjs.com/package/node-red-node-watson)
7 |
8 |
9 |
10 |
11 | ### New in version 0.10.3
12 | - fix bug in cloud bound STT configuration
13 |
14 | ### New in version 0.10.2
15 | - fix bug in cloud bound TTS configuration
16 |
17 | ### New in version 0.10.1
18 | - README update
19 |
20 | ### New in version 0.10.0
21 | - Bump of dependencies
22 | - ibm-watson to 6.2.2
23 | - Min version of Node is 12.20.0
24 | - Drop Personality Insights node
25 | - Drop Visual Recognition nodes
26 | - Remove Username / Password fields from all nodes
27 | - Bump to latest version of services
28 | - New Discovery V2 node
29 | - Add Classifications to Natural Language Understanding node
30 |
31 |
32 | ### Watson Nodes for Node-RED
33 | A collection of nodes to interact with the IBM Watson services in [IBM Cloud](http://cloud.ibm.com).
34 |
35 | # Nodes
36 |
37 | - Assistant (formerly Conversation)
38 | - Add conversational capabilities into applications.
39 | - Discovery
40 | - List environments created for the Discovery service
41 | - Language Identification
42 | - Detects the language used in text
43 | - Language Translator
44 | - Translates text from one language to another
45 | - Natural Language Classifier
46 | - Uses machine learning algorithms to return the top matching predefined classes for short text inputs.
47 | - Natural Language Understanding
48 | - Analyze text to extract meta-data from content such as concepts, entities, keywords ...
49 | - Speech To Text
50 | - Convert audio containing speech to text
51 | - Text To Speech
52 | - Convert text to audio speech
53 | - Tone Analyzer
54 | - Discover, understand, and revise the language tones in text.
55 |
56 | ### Usage
57 | Example usage flows can be found here [node-red-labs](https://github.com/watson-developer-cloud/node-red-labs)
58 |
59 | ### Contributing
60 |
61 | For simple typos and fixes please just raise an issue pointing out our mistakes.
62 | If you need to raise a pull request please read our [contribution guidelines](https://github.com/watson-developer-cloud/node-red-node-watson/blob/master/CONTRIBUTING.md)
63 | before doing so.
64 |
65 | ### Copyright and license
66 |
67 | Copyright 2018, 2022 IBM Corp. under [the Apache 2.0 license](LICENSE).
68 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-red-node-watson",
3 | "version": "0.10.3",
4 | "description": "A collection of Node-RED nodes for IBM Watson services",
5 | "dependencies": {
6 | "cfenv": "~1.2.4",
7 | "file-type": "^12.4.2",
8 | "request": "~2.88.2",
9 | "temp": "^0.9.4",
10 | "qs": "^6.10.3",
11 | "ibm-watson": "^6.2.2",
12 | "is-docx": "^0.0.3",
13 | "stream-to-array": "^2.3.0",
14 | "ws": "^8.5.0"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "https://github.com/watson-developer-cloud/node-red-node-watson"
19 | },
20 | "license": "Apache-2.0",
21 | "keywords": [
22 | "node-red",
23 | "ibm-cloud",
24 | "watson"
25 | ],
26 | "contributors": [
27 | {
28 | "name": "Soheel Chughtai",
29 | "email": "soheel_chughtai@uk.ibm.com"
30 | }
31 | ],
32 | "node-red": {
33 | "nodes": {
34 | "watson-conversation-v1": "services/assistant/v1.js",
35 | "watson-conversation-workspace-manager-v1": "services/assistant/v1-workspace-manager.js",
36 | "watson-assistant-v2": "services/assistant/v2.js",
37 | "watson-discovery-v1": "services/discovery/v1.js",
38 | "watson-discovery-v1-document-loader": "services/discovery/v1-document-loader.js",
39 | "watson-discovery-v1-query-builder": "services/discovery/v1-query-builder.js",
40 | "watson-discovery-v2-project-manager": "services/discovery/v2-project-manager.js",
41 | "watson-language-translator-v3": "services/language_translator/v3.js",
42 | "watson-doc-translator-v3": "services/language_translator/v3-doc.js",
43 | "watson-language-translator-identify-v3": "services/language_translator_identify/v3.js",
44 | "watson-language-translator-util-v3": "services/language_translator_util/v3.js",
45 | "watson-natural-language-classifier-v1": "services/natural_language_classifier/v1.js",
46 | "watson-natural-language-understanding-v1": "services/natural_language_understanding/v1.js",
47 | "watson-natural-language-understanding-model-manager-v1": "services/natural_language_understanding/v1-model-manager.js",
48 | "watson-speech-to-text-v1": "services/speech_to_text/v1.js",
49 | "watson-speech-to-text-corpus-builder-v1": "services/speech_to_text/v1-corpus-builder.js",
50 | "watson-text-to-speech-v1": "services/text_to_speech/v1.js",
51 | "watson-text-to-speech-corpus-builder-v1": "services/text_to_speech/v1-corpus-builder.js",
52 | "watson-tone-analyzer-v3": "services/tone_analyzer/v3.js"
53 | }
54 | },
55 | "engines": {
56 | "node": ">=12.20.0"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/services/assistant/icons/conversation-v1-25x25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/watson-developer-cloud/node-red-node-watson/dc29f8dd09bdbfd669c5560ed0e0b8eccb4e149f/services/assistant/icons/conversation-v1-25x25.png
--------------------------------------------------------------------------------
/services/assistant/v1.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
73 |
74 |
75 |
113 |
114 |
170 |
--------------------------------------------------------------------------------
/services/assistant/v1.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016, 2022 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | module.exports = function(RED) {
18 | const SERVICE_IDENTIFIER = 'assistant',
19 | OLD_SERVICE_IDENTIFIER = 'conversation',
20 | AssistantV1 = require('ibm-watson/assistant/v1'),
21 | { IamAuthenticator } = require('ibm-watson/auth');
22 |
23 | var pkg = require('../../package.json'),
24 | serviceutils = require('../../utilities/service-utils'),
25 | payloadutils = require('../../utilities/payload-utils'),
26 | service = null,
27 | sApikey = null;
28 |
29 |
30 | service = serviceutils.getServiceCreds(SERVICE_IDENTIFIER);
31 | if (!service) {
32 | service = serviceutils.getServiceCreds(OLD_SERVICE_IDENTIFIER);
33 | }
34 |
35 | if (service) {
36 | sApikey = service.apikey ? service.apikey : '';
37 | }
38 |
39 | RED.httpAdmin.get('/watson-conversation/vcap', function(req, res) {
40 | res.json(service ? {
41 | bound_service: true
42 | } : null);
43 | });
44 |
45 | function verifyPayload(node, msg, config) {
46 | if (!(msg.payload || config['empty-payload'])) {
47 | node.status({
48 | fill: 'red',
49 | shape: 'ring',
50 | text: 'missing payload'
51 | });
52 | return Promise.reject('Missing property: msg.payload');
53 | }
54 | return Promise.resolve();
55 | }
56 |
57 | function verifyOptionalInputs(node, msg, config, params) {
58 | // next 3 not documented but present in WDC Node SDK.
59 | // optional output
60 | if (msg.params && msg.params.output) {
61 | params.output = msg.params.output;
62 | }
63 | if (msg.params && msg.params.entities) {
64 | params.entities = msg.params.entities;
65 | }
66 | if (msg.params && msg.params.intents) {
67 | params.intents = msg.params.intents;
68 | }
69 | }
70 |
71 | function setContextParams(node, msg, config, params) {
72 | if (config.context) {
73 | if (config.multiuser) {
74 | if (msg.user) {
75 | params.context = node.context().flow.get('context-' + msg.user);
76 | } else {
77 | var warning = 'Missing msg.user property, using global context. ' +
78 | 'Multiuser conversation may not function as expected.';
79 | node.warn(warning, msg);
80 | params.context = node.context().flow.get('context');
81 | }
82 | } else {
83 | params.context = node.context().flow.get('context');
84 | }
85 | }
86 |
87 | if (msg.additional_context) {
88 | params.context = params.context ? params.context : {};
89 |
90 | for (prop in msg.additional_context) {
91 | if (msg.additional_context.hasOwnProperty(prop)) {
92 | params.context[prop] = msg.additional_context[prop];
93 | }
94 | }
95 | }
96 | }
97 |
98 | function setWorkspaceParams(node, msg, config, params) {
99 | // workspaceid can be either configured in the node,
100 | // or sent into msg.params.workspace_id
101 | if (config.workspaceid) {
102 | params.workspaceId = config.workspaceid;
103 | }
104 | if (msg.params && msg.params.workspace_id) {
105 | params.workspaceId = msg.params.workspace_id;
106 | }
107 | }
108 |
109 | function setSavedContextParams(node, msg, config, params) {
110 | // option context in JSON format
111 | if (msg.params && msg.params.context) {
112 | if (config.context) {
113 | node.warn('Overridden saved context');
114 |
115 | if (msg.user) {
116 | node.context().flow.set('context-' + msg.user, null);
117 | } else {
118 | node.context().flow.set('context', null);
119 | }
120 | }
121 | params.context = msg.params.context;
122 | }
123 | }
124 |
125 | function setAlternativeIntentsParams(node, msg, config, params) {
126 | // optional alternate_intents : boolean
127 | if (msg.params && msg.params.alternate_intents) {
128 | params.alternateIntents = msg.params.alternate_intents;
129 | }
130 | }
131 |
132 | function verifyInputs(node, msg, config, params) {
133 | return new Promise(function resolver(resolve, reject) {
134 | if (!config.workspaceid && (!msg || !msg.params ||!msg.params.workspace_id)) {
135 | reject('Missing workspace_id. check node documentation.');
136 | }
137 |
138 | // mandatory message
139 | params.input = {
140 | text: msg.payload
141 | };
142 |
143 | var prop = null;
144 |
145 | // Invoke each of the functions building up the params in turn
146 | [setContextParams, setWorkspaceParams, setSavedContextParams,
147 | setAlternativeIntentsParams, verifyOptionalInputs].forEach((f) => {
148 | f(node, msg, config, params);
149 | });
150 |
151 | resolve();
152 |
153 | });
154 | }
155 |
156 | function verifyCredentials(msg, k) {
157 | if ( !(k) ) {
158 | if ( (!msg.params) ||
159 | (!(msg.params.apikey)) ) {
160 | return false;
161 | }
162 | }
163 | return true;
164 | }
165 |
166 |
167 | function verifyServiceCredentials(node, msg, config) {
168 | // If it is present the newly provided user entered key
169 | // takes precedence over the existing one.
170 | // If msg.params contain credentials then these will Overridde
171 | // the bound or configured credentials.
172 | return new Promise(function resolver(resolve, reject) {
173 | let authSettings = {},
174 | serviceSettings = {
175 | headers: {
176 | 'User-Agent': pkg.name + '-' + pkg.version
177 | }
178 | };
179 |
180 | let apiKey = sApikey || node.credentials.apikey,
181 | endpoint = '',
182 | optoutLearning = false,
183 | version = '2021-11-27';
184 |
185 | if (!verifyCredentials(msg, apiKey)) {
186 | reject('Missing Watson Assistant API service credentials');
187 | }
188 |
189 | if (msg.params) {
190 | if (msg.params.apikey) {
191 | apiKey = msg.params.apikey;
192 | }
193 | if (msg.params.version) {
194 | version = msg.params.version;
195 | }
196 | if (msg.params.customerId) {
197 | serviceSettings.headers['X-Watson-Metadata'] = msg.params.customerId;
198 | }
199 | }
200 |
201 | if (apiKey) {
202 | authSettings.apikey = apiKey;
203 | }
204 | serviceSettings.authenticator = new IamAuthenticator(authSettings);
205 |
206 | serviceSettings.version = version;
207 | serviceSettings.version_date = version;
208 |
209 | if (service) {
210 | endpoint = service.url;
211 | }
212 | if (config['service-endpoint']) {
213 | endpoint = config['service-endpoint'];
214 | }
215 | if (msg.params && msg.params.endpoint) {
216 | endpoint = msg.params.endpoint;
217 | }
218 |
219 | if (endpoint) {
220 | serviceSettings.url = endpoint;
221 | }
222 |
223 | if ((msg.params && msg.params['optout_learning'])){
224 | optoutLearning = true;
225 | } else if (config['optout-learning']){
226 | optoutLearning = true;
227 | }
228 |
229 | if (optoutLearning){
230 | serviceSettings.headers = serviceSettings.headers || {};
231 | serviceSettings.headers['X-Watson-Learning-Opt-Out'] = '1';
232 | }
233 |
234 | if (config['timeout'] && config['timeout'] !== '0' && isFinite(config['timeout'])){
235 | serviceSettings.timeout = parseInt(config['timeout']);
236 | }
237 |
238 | if (msg.params && msg.params.timeout !== '0' && isFinite(msg.params.timeout)){
239 | serviceSettings.timeout = parseInt(msg.params.timeout);
240 | }
241 |
242 | if (msg.params && msg.params.disable_ssl_verification){
243 | serviceSettings.disable_ssl_verification = true;
244 | }
245 |
246 | node.service = new AssistantV1(serviceSettings);
247 | resolve();
248 | });
249 | }
250 |
251 | function processResponse(response, node, msg, config) {
252 | return new Promise(function resolver(resolve, reject) {
253 | if (response === null || typeof (response) === 'undefined') {
254 | reject('call to watson conversation service failed');
255 | }
256 |
257 | msg.payload = response;
258 | if (response && response.result) {
259 | msg.payload = response.result;
260 | }
261 |
262 | let body = msg.payload;
263 |
264 | if (config.context && body && body.context) {
265 | if (config.multiuser && msg.user) {
266 | node.context().flow.set('context-' + msg.user, body.context);
267 | } else {
268 | if (msg.user) {
269 | node.warn('msg.user ignored when multiple users not set in node');
270 | }
271 | node.context().flow.set('context', body.context);
272 | }
273 | }
274 |
275 | resolve();
276 | });
277 | }
278 |
279 | function execute(params, node, msg, config) {
280 | return new Promise(function resolver(resolve, reject) {
281 | node.status({
282 | fill: 'blue',
283 | shape: 'dot',
284 | text: 'Calling Conversation service ...'
285 | });
286 | // call POST /message through SDK
287 | node.service.message(params)
288 | .then((response) => {
289 | return processResponse(response, node, msg, config);
290 | })
291 | .then(() => {
292 | resolve();
293 | })
294 | .catch((err) => {
295 | reject(err);
296 | });
297 | });
298 | }
299 |
300 | // This is the Watson Conversation V1 (GA) Node
301 | function WatsonConversationV1Node(config) {
302 | var node = this;
303 |
304 | RED.nodes.createNode(this, config);
305 |
306 | this.on('input', function(msg, send, done) {
307 | var params = {};
308 |
309 | node.status({});
310 |
311 | verifyPayload(node, msg, config)
312 | .then(() => {
313 | return verifyInputs(node, msg, config, params);
314 | })
315 | .then(() => {
316 | return verifyServiceCredentials(node, msg, config);
317 | })
318 | .then(() => {
319 | return execute(params, node, msg, config);
320 | })
321 | .then(() => {
322 | send(msg);
323 | node.status({});
324 | done();
325 | })
326 | .catch(function(err){
327 | let errMsg = payloadutils.reportError(node, msg, err);
328 | done(errMsg);
329 | });
330 | });
331 | }
332 |
333 | RED.nodes.registerType('watson-conversation-v1', WatsonConversationV1Node, {
334 | credentials: {
335 | apikey: {type: 'password'}
336 | }
337 | });
338 | };
339 |
--------------------------------------------------------------------------------
/services/assistant/v2.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
98 |
99 |
153 |
154 |
155 |
226 |
--------------------------------------------------------------------------------
/services/discovery/discovery-utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016, 2022 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 | const pkg = require('../../package.json'),
17 | DiscoveryV1 = require('ibm-watson/discovery/v1'),
18 | { IamAuthenticator } = require('ibm-watson/auth');
19 |
20 |
21 | function DiscoveryUtils() {}
22 | DiscoveryUtils.prototype = {
23 |
24 | buildService: function(apikey, endpoint) {
25 | let authSettings = {};
26 | let serviceSettings = {
27 | version: '2019-04-30',
28 | headers: {
29 | 'User-Agent': pkg.name + '-' + pkg.version
30 | }
31 | };
32 |
33 | if (apikey) {
34 | authSettings.apikey = apikey;
35 | }
36 | serviceSettings.authenticator = new IamAuthenticator(authSettings);
37 |
38 | if (endpoint) {
39 | serviceSettings.url = endpoint;
40 | serviceSettings.serviceUrl = endpoint;
41 | }
42 |
43 | return new DiscoveryV1(serviceSettings);
44 |
45 | },
46 |
47 | buildParamsForName: function(msg, config, params) {
48 | if (msg.discoveryparams && msg.discoveryparams.environmentname) {
49 | params.name = msg.discoveryparams.environmentname;
50 | } else if (config.environmentname) {
51 | params.name = config.environmentname;
52 | } else if (msg.discoveryparams && msg.discoveryparams.configurationname) {
53 | params.name = msg.discoveryparams.configurationname;
54 | } else if (config.configurationname) {
55 | params.name = config.configurationname;
56 | } else if (msg.discoveryparams && msg.discoveryparams.collection_name) {
57 | params.name = msg.discoveryparams.collection_name;
58 | } else if (config.collection_name) {
59 | params.name = config.collection_name;
60 | }
61 | return params;
62 | },
63 |
64 | buildParamsForQuery: function(msg, config, params) {
65 | var sourceField = 'query',
66 | targetField = 'query';
67 |
68 | if (config.nlp_query || (msg.discoveryparams && msg.discoveryparams.nlp_query)) {
69 | targetField = 'naturalLanguageQuery';
70 | }
71 | if (msg.discoveryparams && msg.discoveryparams[sourceField]) {
72 | params[targetField] = msg.discoveryparams[sourceField];
73 | } else if (config[sourceField]) {
74 | params[targetField] = config[sourceField];
75 | }
76 | return params;
77 | },
78 |
79 | buildParamsForPayload: function(msg, config, params) {
80 | var isJSON = this.isJsonString(msg.payload) ||
81 | this.isJsonObject(msg.payload);
82 |
83 | // Payload (text to be analysed) must be a string (content is either raw string or Buffer)
84 | if (typeof msg.payload === 'string' || isJSON) {
85 | params.file = this.isJsonObject(msg.payload) ?
86 | JSON.stringify(msg.payload) :
87 | msg.payload;
88 | params.fileContentType = 'application/json';
89 | }
90 | return params;
91 | },
92 |
93 | buildParamsFor: function(msg, config, params, field) {
94 | if (msg.discoveryparams && msg.discoveryparams[field]) {
95 | params[field] = msg.discoveryparams[field];
96 | } else if (config[field]) {
97 | params[field] = config[field];
98 | }
99 | return params;
100 | },
101 |
102 | buildParamsForPassages: function (me, msg, config, params) {
103 | let passagesFound = false;
104 |
105 | // Allow the passages parameters to be passed in three ways
106 | // 1. As the API was expecting (watson-developer-cloud)
107 | ['passages.fields', 'passages.count', 'passages.characters'
108 | ].forEach(function(f) {
109 | params = me.buildParamsFor(msg, config, params, f);
110 | passagesFound = true;
111 | });
112 |
113 | // 2. As anyone misreading the documentation might do it.
114 | // (again watson-developer-cloud)
115 | if (msg.discoveryparams && msg.discoveryparams.passages) {
116 | passagesFound = true;
117 | ['fields', 'count', 'characters'
118 | ].forEach(function(f) {
119 | if (msg.discoveryparams.passages[f]) {
120 | params['passages.' + f] = msg.discoveryparams.passages[f];
121 | }
122 | });
123 | }
124 |
125 | // 3. As the ibm-watson SDK expects
126 | ['passagesFields', 'passagesCount', 'passagesCharacters'
127 | ].forEach(function(f) {
128 | params = me.buildParamsFor(msg, config, params, f);
129 | passagesFound = true;
130 | });
131 |
132 | if (passagesFound) {
133 | // Perform autocorrect for watson-developer-cloud to ibm-watson
134 | // differences
135 | ['passages.fields', 'passages.count', 'passages.characters'
136 | ].forEach(function(f) {
137 | if (params[f]) {
138 | let parts = f.split(".");
139 | let secondPart = parts[1]
140 | let reformedField = parts[0] + secondPart[0].toUpperCase() + secondPart.slice(1);
141 | params[reformedField] = params[f];
142 | }
143 | });
144 | params.passages = true;
145 | }
146 |
147 | return params;
148 | },
149 |
150 | buildParamsFromConfig: function(config, params, field) {
151 | if (config[field]) {
152 | params[field] = config[field];
153 | }
154 | return params;
155 | },
156 |
157 | // The field to create a new language collection is language, but
158 | // the SDK creates a language_code field which it defaults to 'en-us'
159 | languageCodeFix: function(params) {
160 | if (params.language_code) {
161 | params.language = params.language_code;
162 | }
163 | return params;
164 | },
165 |
166 | buildParams: function(msg, config) {
167 | var params = {},
168 | me = this;
169 |
170 | params = me.buildParamsForName(msg, config, params);
171 | params = me.buildParamsForQuery(msg, config, params);
172 |
173 | ['environmentId', 'collectionId', 'configurationId',
174 | 'collection_name', 'language_code',
175 | 'passages', 'description', 'size', 'filename',
176 | 'highlight'
177 | ].forEach(function(f) {
178 | params = me.buildParamsFor(msg, config, params, f);
179 | });
180 |
181 | params = me.buildParamsForPassages(me, msg, config, params);
182 |
183 | ['count', 'filter', 'aggregation', 'return'].forEach(function(f) {
184 | params = me.buildParamsFromConfig(config, params, f);
185 | });
186 |
187 | params = me.languageCodeFix(params);
188 |
189 | params = me.buildParamsForPayload(msg, config, params);
190 |
191 | return params;
192 | },
193 |
194 | buildMsgOverrides: function(msg, config) {
195 | var params = {};
196 | if (config.environment) {
197 | params.environmentId = config.environment;
198 | }
199 | if (config.collection) {
200 | params.collectionId = config.collection;
201 | }
202 | if (config.passages) {
203 | params.passages = config.passages;
204 | }
205 | if (config.collection) {
206 | params.filename = config.filename;
207 | }
208 |
209 | params = this.buildMsgQueryOverrides(msg, config, params);
210 |
211 | return params;
212 | },
213 |
214 | buildMsgQueryOverrides: function(msg, config, params) {
215 | if (config.nlp_query) {
216 | params.query = config.querynlp;
217 | params.nlp_query = config.nlp_query;
218 | } else {
219 | params = this.buildStructuredQuery(msg, config, params);
220 | }
221 | return params;
222 | },
223 |
224 | buildStructuredQuery: function(msg, config, params) {
225 | if (config.query1 && config.queryvalue1) {
226 | params.query = config.query1 + ':"' + config.queryvalue1 + '"';
227 | }
228 | if (config.query2 && config.queryvalue2) {
229 | if (params.query) {
230 | params.query += ',';
231 | }
232 | params.query += config.query2 + ':"' + config.queryvalue2 + '"';
233 | }
234 | if (config.query3 && config.queryvalue3) {
235 | if (params.query) {
236 | params.query += ',';
237 | }
238 | params.query += config.query3 + ':"' + config.queryvalue3 + '"';
239 | }
240 | return params;
241 | },
242 |
243 |
244 | paramEnvCheck: function(params) {
245 | var response = '';
246 | if (!params.environmentId) {
247 | response = 'Missing Environment ID ';
248 | }
249 | return response;
250 | },
251 |
252 | paramJSONCheck: function(params) {
253 | var response = '';
254 | if (!params.file) {
255 | response = 'Missing JSON file on payload';
256 | }
257 | return response;
258 | },
259 |
260 | paramDocumentCheck: function(params) {
261 | var response = '';
262 | if (!params.file) {
263 | response = 'Missing document file on payload';
264 | }
265 | return response;
266 | },
267 |
268 | paramNameCheck: function(params) {
269 | var response = '';
270 | if (!params.name) {
271 | response = 'Missing Name ';
272 | }
273 | return response;
274 | },
275 |
276 | paramDescriptionCheck: function(params) {
277 | var response = '';
278 | if (!params.description) {
279 | response = 'Missing Description ';
280 | }
281 | return response;
282 | },
283 |
284 | paramCollectionCheck: function(params) {
285 | var response = '';
286 | if (!params.collectionId) {
287 | response = 'Missing Collection ID ';
288 | }
289 | return response;
290 | },
291 |
292 | paramConfigurationCheck: function(params) {
293 | var response = '';
294 | if (!params.configurationId) {
295 | response = 'Missing Configuration ID ';
296 | }
297 | return response;
298 | },
299 |
300 | // Looking for Text, Type and label
301 | buildFieldByStep: function(d, fields, txt) {
302 | for (var k in d) {
303 | var t = txt;
304 | if (isNaN(k)) {
305 | t += txt ? '.' : '';
306 | t += k;
307 | }
308 |
309 | if ('object' === typeof d[k]) {
310 | fields = this.buildFieldByStep(d[k], fields, t);
311 | } else {
312 | switch (k) {
313 | case 'text':
314 | case 'type':
315 | case 'label':
316 | fields.push(t);
317 | break;
318 | }
319 | }
320 | }
321 | return fields;
322 | },
323 |
324 | // sorting functions
325 | uniqueFilter: function(value, index, self) {
326 | return self.indexOf(value) === index;
327 | },
328 |
329 | // Looking for Text, Type and label
330 | buildFieldList: function(schemaData) {
331 | var fields = [];
332 |
333 | if (schemaData &&
334 | 'object' === typeof schemaData &&
335 | schemaData.result &&
336 | 'object' === typeof schemaData.result &&
337 | schemaData.result['fields'] &&
338 | Array.isArray(schemaData.result['fields'])) {
339 | schemaData.result['fields'].forEach((f) => {
340 | if (f['field'] && f['type'] && 'nested' !== f['type']) {
341 | fields.push(f['field']);
342 | }
343 | });
344 | }
345 |
346 | if (fields.length) {
347 | fields = fields.filter(this.uniqueFilter);
348 | fields.sort();
349 | }
350 |
351 | return fields;
352 | },
353 |
354 | // reportError: function (node, msg, message) {
355 | // var messageTxt = message.error ? message.error : message;
356 | // node.status({fill:'red', shape:'dot', text: messageTxt});
357 | // node.error(message, msg);
358 | // } ,
359 |
360 | isJsonString: function(str) {
361 | try {
362 | JSON.parse(str);
363 | } catch (e) {
364 | return false;
365 | }
366 | return true;
367 | },
368 |
369 | isJsonObject: function(str) {
370 | if (str instanceof Array || str instanceof Object ||
371 | 'object' === typeof str || Array.isArray(str)) {
372 | return true;
373 | }
374 | return false;
375 | }
376 |
377 |
378 | };
379 |
380 | var discoveryutils = new DiscoveryUtils();
381 |
382 | module.exports = discoveryutils;
383 |
--------------------------------------------------------------------------------
/services/discovery/discovery-utils2.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 | const pkg = require('../../package.json'),
17 | DiscoveryV2 = require('ibm-watson/discovery/v2'),
18 | { IamAuthenticator } = require('ibm-watson/auth');
19 |
20 |
21 | function DiscoveryUtils2() {}
22 | DiscoveryUtils2.prototype = {
23 |
24 | buildService: function(apikey, endpoint) {
25 | let authSettings = {};
26 | let serviceSettings = {
27 | version: '2020-08-30',
28 | headers: {
29 | 'User-Agent': pkg.name + '-' + pkg.version
30 | }
31 | };
32 |
33 | if (apikey) {
34 | authSettings.apikey = apikey;
35 | }
36 | serviceSettings.authenticator = new IamAuthenticator(authSettings);
37 |
38 | if (endpoint) {
39 | serviceSettings.url = endpoint;
40 | serviceSettings.serviceUrl = endpoint;
41 | }
42 |
43 | return new DiscoveryV2(serviceSettings);
44 | },
45 |
46 |
47 | paramProjectCheck: function(params) {
48 | return this.paramFieldCheck(params, 'projectId');
49 | },
50 |
51 | paramCollectionCheck: function(params) {
52 | return this.paramFieldCheck(params, 'projectId')
53 | + this.paramFieldCheck(params, 'collectionId');
54 | },
55 |
56 | paramQueryIdCheck: function(params) {
57 | return this.paramFieldCheck(params, 'projectId')
58 | + this.paramFieldCheck(params, 'queryId');
59 | },
60 |
61 | paramQueryFieldCheck: function(params) {
62 | if (! params['query'] && ! params['naturalLanguageQuery']) {
63 | return "No query found on msg.payload";
64 | }
65 | return '';
66 | },
67 |
68 |
69 | paramNameCheck: function(params) {
70 | return this.paramFieldCheck(params, 'name');
71 | },
72 |
73 | paramTypeCheck: function(params) {
74 | return this.paramFieldCheck(params, 'type');
75 | },
76 |
77 | paramFieldCheck: function(params, field) {
78 | var response = '';
79 | if (!params[field]) {
80 | response = 'Missing ' + field + ' ';
81 | }
82 | return response;
83 | },
84 |
85 | buildParamsFor: function(msg, config, params, field) {
86 | if (msg.discoveryparams && msg.discoveryparams[field]) {
87 | params[field] = msg.discoveryparams[field];
88 | } else if (config[field]) {
89 | params[field] = config[field];
90 | }
91 | return params;
92 | },
93 |
94 | buildParamsForName: function(msg, config, params) {
95 | let name = '';
96 | if (msg.discoveryparams) {
97 | for (let f of ['projectName', 'projectname',
98 | 'collectionName', 'collectionname']) {
99 | if (msg.discoveryparams[f]) {
100 | name = msg.discoveryparams[f];
101 | break;
102 | }
103 | }
104 | }
105 | if (!name) {
106 | for (let f of ['projectName', 'collectionName']) {
107 | if (config[f]) {
108 | name = config[f];
109 | break;
110 | }
111 | }
112 | }
113 | if (name) {
114 | params.name = name;
115 | }
116 | return params;
117 | },
118 |
119 | buildParamsForType: function(msg, config, params) {
120 | let type = '';
121 | if (msg.discoveryparams) {
122 | if (msg.discoveryparams.projectType) {
123 | type = msg.discoveryparams.projectType;
124 | } else if (msg.discoveryparams.projecttype) {
125 | type = msg.discoveryparams.projecttype;
126 | }
127 | }
128 | if (!type) {
129 | if (config.projectType) {
130 | type = config.projectType
131 | }
132 | }
133 | if (type) {
134 | params.type = type;
135 | }
136 | return params;
137 | },
138 |
139 | addOtherParams: function(msg, params) {
140 | if (msg.discoveryparams) {
141 | if (msg.discoveryparams.defaultQueryParameters) {
142 | params.defaultQueryParameters = msg.discoveryparams.defaultQueryParameters;
143 | }
144 | if (msg.discoveryparams.enrichments) {
145 | params.defaultQueryParameters = msg.discoveryparams.enrichments;
146 | }
147 | }
148 | return params
149 | },
150 |
151 | addQueryParams: function(msg, params) {
152 | if (msg.discoveryparams) {
153 | ['collectionIds', 'filter', 'aggregation',
154 | 'count', '_return', 'offset', 'sort',
155 | 'highlight', 'spellingSuggestions',
156 | 'tableResults', 'suggestedRefinements',
157 | 'passages'
158 | ].forEach(function(f) {
159 | if (msg.discoveryparams[f]) {
160 | params[f] = msg.discoveryparams[f];
161 | }
162 | });
163 | }
164 | return params;
165 | },
166 |
167 | buildParams: function(msg, config) {
168 | var params = {},
169 | me = this;
170 |
171 | params = me.buildParamsForName(msg, config, params);
172 | params = me.buildParamsForType(msg, config, params);
173 | params = me.addOtherParams(msg, params);
174 |
175 | ['projectId', 'collectionId', 'queryId',
176 | 'description', 'language'].forEach(function(f) {
177 | params = me.buildParamsFor(msg, config, params, f);
178 | });
179 |
180 | return params;
181 | }
182 |
183 |
184 | };
185 |
186 | var discoveryutils2 = new DiscoveryUtils2();
187 |
188 | module.exports = discoveryutils2;
189 |
--------------------------------------------------------------------------------
/services/discovery/icons/discovery.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/watson-developer-cloud/node-red-node-watson/dc29f8dd09bdbfd669c5560ed0e0b8eccb4e149f/services/discovery/icons/discovery.png
--------------------------------------------------------------------------------
/services/discovery/v1-document-loader.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
51 |
52 |
71 |
72 |
129 |
--------------------------------------------------------------------------------
/services/discovery/v1-document-loader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 20016, 2022 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | module.exports = function (RED) {
18 |
19 | const SERVICE_IDENTIFIER = 'discovery';
20 | var fs = require('fs'),
21 | temp = require('temp'),
22 | fileType = require('file-type'),
23 | isDocx = require('is-docx'),
24 | discoveryutils = require('./discovery-utils'),
25 | serviceutils = require('../../utilities/service-utils'),
26 | payloadutils = require('../../utilities/payload-utils'),
27 | dservice = serviceutils.getServiceCreds(SERVICE_IDENTIFIER),
28 | apikey = null,
29 | sApikey = null,
30 | endpoint = '',
31 | sEndpoint = '';
32 |
33 | temp.track();
34 |
35 | function initialCheck(k) {
36 | var message = '';
37 | if (!k) {
38 | message = 'Missing Watson Discovery service credentials';
39 | }
40 | if (message) {
41 | return Promise.reject(message);
42 | }
43 | return Promise.resolve();
44 | }
45 |
46 | function checkParams(params){
47 | var response = discoveryutils.paramEnvCheck(params) +
48 | discoveryutils.paramCollectionCheck(params);
49 | if (response) {
50 | return Promise.reject(response);
51 | }
52 | return Promise.resolve();
53 | }
54 |
55 | function verifyPayload(msg) {
56 | if (!msg.payload) {
57 | return Promise.reject('Missing property: msg.payload');
58 | } else if ( (msg.payload instanceof Buffer) ||
59 | (discoveryutils.isJsonObject(msg.payload)) ) {
60 | return Promise.resolve();
61 | }
62 | return Promise.reject('msg.payload should be a data buffer or json object');
63 | }
64 |
65 | function determineSuffix(msg) {
66 | // Let's assume that if we can't determine the suffix that
67 | // its a word doc.
68 | let ext = '.json';
69 | if (! discoveryutils.isJsonObject(msg.payload)) {
70 | let ext = '.json',
71 | ft = fileType(msg.payload);
72 |
73 | if (ft && ft.ext) {
74 | ext = '.' + ft.ext;
75 | }
76 |
77 | if (isDocx(msg.payload)) {
78 | ext = '.docx';
79 | }
80 | }
81 |
82 | return Promise.resolve(ext);
83 | }
84 |
85 | function openTheFile(suffix) {
86 | var p = new Promise(function resolver(resolve, reject){
87 | var options = {};
88 | if (suffix) {
89 | options.suffix = suffix;
90 | }
91 | temp.open(options, function(err, info) {
92 | if (err) {
93 | reject('Error receiving the data buffer');
94 | } else {
95 | resolve(info);
96 | }
97 | });
98 | });
99 | return p;
100 | }
101 |
102 | function syncTheFile(info, msg) {
103 | var p = new Promise(function resolver(resolve, reject){
104 | fs.writeFile(info.path, msg.payload, function(err) {
105 | if (err) {
106 | reject('Error processing pdf buffer');
107 | }
108 | resolve();
109 | });
110 | });
111 | return p;
112 | }
113 |
114 | function createStream(info) {
115 | //var theStream = fs.createReadStream(info.path, 'utf8');
116 | var theStream = fs.readFileSync(info.path, 'utf8');
117 | return Promise.resolve(theStream);
118 | }
119 |
120 | function whatName(params, suffix){
121 | if (params.filename) {
122 | return params.filename;
123 | }
124 | return 'Doc ' + (new Date()).toString() + suffix;
125 | }
126 |
127 | function execute(params, msg, suffix) {
128 | var p = new Promise(function resolver(resolve, reject) {
129 | let discovery = discoveryutils.buildService(apikey, endpoint);
130 |
131 | // modify as getting addJsonDocument will be deprecated messages
132 | if ('.json' === suffix) {
133 | //method = 'addJsonDocument';
134 | //params.file = JSON.stringify(params.file);
135 |
136 | params.file = Buffer.from(JSON.stringify(params.file));
137 | //} else {
138 | //method = 'addDocument';
139 | }
140 | method = 'addDocument';
141 |
142 | discovery[method](params)
143 | .then((response) => {
144 | msg.document = response.result ? response.result : response;
145 | resolve();
146 | })
147 | .catch((err) => {
148 | reject(err);
149 | });
150 |
151 | });
152 | return p;
153 | }
154 |
155 | if (dservice) {
156 | sApikey = dservice.apikey ? dservice.apikey : '';
157 | sEndpoint = dservice.url ? dservice.url : '';
158 | }
159 |
160 | RED.httpAdmin.get('/watson-discovery-docs/vcap', function (req, res) {
161 | res.json(serviceutils.checkServiceBound(SERVICE_IDENTIFIER));
162 | });
163 |
164 |
165 | function Node (config) {
166 | var node = this;
167 | RED.nodes.createNode(this, config);
168 |
169 | this.on('input', function(msg, send, done) {
170 | var message = '',
171 | fileInfo = '',
172 | fileSuffix = '',
173 | params = {};
174 |
175 | apikey = sApikey || this.credentials.apikey;
176 |
177 | endpoint = sEndpoint;
178 | if (config['service-endpoint']) {
179 | endpoint = config['service-endpoint'];
180 | }
181 |
182 | node.status({});
183 | initialCheck(apikey)
184 | .then(function(){
185 | return verifyPayload(msg);
186 | })
187 | .then(function(){
188 | params = discoveryutils.buildParams(msg, config);
189 | return checkParams(params);
190 | })
191 | .then(function(){
192 | return determineSuffix(msg);
193 | })
194 | .then(function(suffix) {
195 | fileSuffix = suffix;
196 | node.status({ fill: 'blue', shape: 'dot', text: 'reading' });
197 | return openTheFile(suffix);
198 | })
199 | .then(function(info){
200 | fileInfo = info;
201 | return syncTheFile(fileInfo, msg);
202 | })
203 | .then(function(){
204 | return createStream(fileInfo);
205 | })
206 | .then(function(theStream){
207 | //params.file = theStream;
208 | //var fname = 'temp' + fileSuffix;
209 | var fname = whatName(params, fileSuffix);
210 | params.file = {
211 | value: theStream,
212 | options: {
213 | filename: fname
214 | }
215 | };
216 |
217 | node.status({ fill: 'blue', shape: 'dot', text: 'processing' });
218 | //return Promise.reject('temp disabled');
219 | return execute(params, msg, fileSuffix);
220 | })
221 | .then(function(){
222 | temp.cleanup();
223 | node.status({});
224 | send(msg);
225 | done();
226 | })
227 | .catch(function(err){
228 | temp.cleanup();
229 | let errMsg = payloadutils.reportError(node, msg, err);
230 | done(errMsg);
231 | });
232 | });
233 | }
234 |
235 | RED.nodes.registerType('watson-discovery-v1-document-loader', Node, {
236 | credentials: {
237 | apikey: {type:'password'}
238 | }
239 | });
240 | };
241 |
--------------------------------------------------------------------------------
/services/discovery/v1-query-builder.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 20016 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | module.exports = function(RED) {
18 |
19 | const SERVICE_IDENTIFIER = 'discovery';
20 | var discoveryutils = require('./discovery-utils'),
21 | serviceutils = require('../../utilities/service-utils'),
22 | dservice = serviceutils.getServiceCreds(SERVICE_IDENTIFIER),
23 | sUsername = null,
24 | sPassword = null,
25 | sApikey = null,
26 | sEndpoint = '';
27 |
28 |
29 | if (dservice) {
30 | sUsername = dservice.username ? dservice.username : '';
31 | sPassword = dservice.password ? dservice.password : '';
32 | sApikey = dservice.apikey ? dservice.apikey : '';
33 | sEndpoint = dservice.url ? dservice.url : '';
34 | }
35 |
36 | RED.httpAdmin.get('/watson-discovery-v1-query-builder/vcap', function(req, res) {
37 | res.json(serviceutils.checkServiceBound(SERVICE_IDENTIFIER));
38 | });
39 |
40 | function processResponse(response, field) {
41 | let reply = response;
42 | if (response) {
43 | if (response.result) {
44 | if (response.result[field]) {
45 | reply = response.result[field];
46 | } else {
47 | reply = response.result;
48 | }
49 | }
50 | }
51 | return reply;
52 | }
53 |
54 | // API used by widget to fetch available environments
55 | RED.httpAdmin.get('/watson-discovery-v1-query-builder/environments', function(req, res) {
56 |
57 | let discovery = discoveryutils.buildService(sUsername ? sUsername : req.query.un,
58 | sPassword ? sPassword : req.query.pwd,
59 | sApikey ? sApikey : req.query.key,
60 | req.query.endpoint ? req.query.endpoint : sEndpoint);
61 |
62 | discovery.listEnvironments({})
63 | .then((response) => {
64 | res.json(processResponse(response,'environments'));
65 | })
66 | .catch((err) => {
67 | res.json(err);
68 | });
69 | });
70 |
71 | // API used by widget to fetch available collections in environment
72 | RED.httpAdmin.get('/watson-discovery-v1-query-builder/collections', function(req, res) {
73 | let discovery = discoveryutils.buildService(sUsername ? sUsername : req.query.un,
74 | sPassword ? sPassword : req.query.pwd,
75 | sApikey ? sApikey : req.query.key,
76 | req.query.endpoint ? req.query.endpoint : sEndpoint);
77 |
78 | discovery.listCollections({environmentId: req.query.environment_id})
79 | .then((response) => {
80 | res.json(processResponse(response,'collections'));
81 | })
82 | .catch((err) => {
83 | res.json(err);
84 | });
85 | });
86 |
87 |
88 | // API used by widget to fetch available collections in environment
89 | RED.httpAdmin.get('/watson-discovery-v1-query-builder/schemas', function(req, res) {
90 | let discovery = discoveryutils.buildService(sUsername ? sUsername : req.query.un,
91 | sPassword ? sPassword : req.query.pwd,
92 | sApikey ? sApikey : req.query.key,
93 | req.query.endpoint ? req.query.endpoint : sEndpoint);
94 |
95 | discovery.listCollectionFields({
96 | environmentId: req.query.environment_id,
97 | collectionId: req.query.collection_id
98 | })
99 | .then((response) => {
100 | let fieldList = discoveryutils.buildFieldList(response);
101 | res.json(fieldList);
102 | })
103 | .catch((err) => {
104 | res.json(err);
105 | });
106 |
107 | });
108 |
109 | function Node(config) {
110 | var node = this;
111 | RED.nodes.createNode(this, config);
112 |
113 | this.on('input', function(msg) {
114 | // Simply return params for query on msg object
115 | msg.discoveryparams = discoveryutils.buildMsgOverrides(msg, config);
116 | node.send(msg);
117 | });
118 | }
119 |
120 | RED.nodes.registerType('watson-discovery-v1-query-builder', Node, {
121 | credentials: {
122 | username: {
123 | type: 'text'
124 | },
125 | password: {
126 | type: 'password'
127 | },
128 | apikey: {
129 | type: 'password'
130 | }
131 | }
132 | });
133 | };
134 |
--------------------------------------------------------------------------------
/services/discovery/v1.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016, 2022 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | module.exports = function (RED) {
18 |
19 | const SERVICE_IDENTIFIER = 'discovery';
20 | var discoveryutils = require('./discovery-utils'),
21 | serviceutils = require('../../utilities/service-utils'),
22 | payloadutils = require('../../utilities/payload-utils'),
23 | responseutils = require('../../utilities/response-utils'),
24 | dservice = serviceutils.getServiceCreds(SERVICE_IDENTIFIER),
25 | apikey = null,
26 | sApikey = null,
27 | endpoint = '',
28 | sEndpoint = '';
29 |
30 | const ExecutionList = {
31 | 'createEnvrionment': executeCreateEnvrionment,
32 | 'listEnvrionments': executeListEnvrionments,
33 | 'getEnvironmentDetails': executeEnvrionmentDetails,
34 | 'createCollection': executeCreateCollection,
35 | 'listCollections': executeListCollections,
36 | 'getCollectionDetails': executeGetCollectionDetails,
37 | 'deleteCollection': executeDeleteCollection,
38 | 'createConfiguration': executeCreateConfiguration,
39 | 'listConfigurations': executeListConfigurations,
40 | 'getConfigurationDetails': executeGetConfigurationDetails,
41 | 'deleteConfiguration': executeDeleteConfiguration,
42 | 'deleteEnvironment': executeDeleteEnvironment,
43 | 'listExpansions': executeListExpansions,
44 | 'listTrainingData': executeListTrainingData,
45 | 'query': executeQuery,
46 | 'queryNotices': executeQueryNotices
47 | };
48 |
49 |
50 | function checkParams(method, params){
51 | var response = '';
52 | switch (method) {
53 | case 'createEnvrionment':
54 | response = discoveryutils.paramNameCheck(params) +
55 | discoveryutils.paramDescriptionCheck(params);
56 | break;
57 | case 'createConfiguration':
58 | response = discoveryutils.paramNameCheck(params) +
59 | discoveryutils.paramEnvCheck(params) +
60 | discoveryutils.paramJSONCheck(params);
61 | break;
62 | case 'getEnvironmentDetails':
63 | case 'listCollections':
64 | response = discoveryutils.paramEnvCheck(params);
65 | break;
66 | case 'getCollectionDetails':
67 | case 'query':
68 | case 'queryNotices':
69 | case 'listExpansions':
70 | case 'listTrainingData':
71 | response = discoveryutils.paramEnvCheck(params) +
72 | discoveryutils.paramCollectionCheck(params);
73 | break;
74 | case 'listConfigurations':
75 | response = discoveryutils.paramEnvCheck(params);
76 | break;
77 | case 'getConfigurationDetails':
78 | response = discoveryutils.paramEnvCheck(params) +
79 | discoveryutils.paramConfigurationCheck(params);
80 | break;
81 | }
82 | if (response) {
83 | return Promise.reject(response);
84 | } else {
85 | return Promise.resolve();
86 | }
87 | }
88 |
89 | function executeCreateEnvrionment(node, discovery, params, msg) {
90 | var p = new Promise(function resolver(resolve, reject){
91 | discovery.createEnvironment(params)
92 | .then((response) => {
93 | msg.environment = response.result ? response.result : response;
94 | resolve();
95 | })
96 | .catch((err) => {
97 | reject(err);
98 | });
99 | });
100 | return p;
101 | }
102 |
103 | function executeListEnvrionments(node, discovery, params, msg) {
104 | var p = new Promise(function resolver(resolve, reject){
105 | discovery.listEnvironments(params)
106 | .then((response) => {
107 | responseutils.parseResponseFor(msg, response, 'environments');
108 | resolve();
109 | })
110 | .catch((err) => {
111 | reject(err);
112 | });
113 | });
114 | return p;
115 | }
116 |
117 | function executeEnvrionmentDetails(node, discovery, params, msg) {
118 | var p = new Promise(function resolver(resolve, reject){
119 | discovery.getEnvironment(params)
120 | .then((response) => {
121 | msg.environment_details = response;
122 | if (response && response.result) {
123 | msg.environment_details = response.result;
124 | }
125 | resolve();
126 | })
127 | .catch((err) => {
128 | reject(err);
129 | });
130 | });
131 | return p;
132 | }
133 |
134 | function executeCreateCollection(node, discovery, params, msg) {
135 | var p = new Promise(function resolver(resolve, reject){
136 |
137 | //params.body = {};
138 | //['name','description','collection_name'
139 | // 'configuration_id'].forEach(function(f) {
140 | // params.body[f] = params[f];
141 | // //delete params[f];
142 | //});
143 |
144 | discovery.createCollection(params)
145 | .then((response) => {
146 | msg.collection = response.result ? response.result : response;
147 | resolve();
148 | })
149 | .catch((err) => {
150 | reject(err);
151 | });
152 | });
153 | return p;
154 | }
155 |
156 | function executeDeleteCollection(node, discovery, params, msg) {
157 | var p = new Promise(function resolver(resolve, reject){
158 | discovery.deleteCollection(params)
159 | .then((response) => {
160 | msg.collection = response.result ? response.result : response;
161 | resolve();
162 | })
163 | .catch((err) => {
164 | reject(err);
165 | });
166 | });
167 | return p;
168 | }
169 |
170 | function executeDeleteConfiguration(node, discovery, params, msg) {
171 | var p = new Promise(function resolver(resolve, reject){
172 | discovery.deleteConfiguration(params)
173 | .then((response) => {
174 | msg.configuration = response.result ? response.result : response;
175 | resolve();
176 | })
177 | .catch((err) => {
178 | reject(err);
179 | });
180 | });
181 | return p;
182 | }
183 |
184 |
185 | function executeDeleteEnvironment(node, discovery, params, msg) {
186 | var p = new Promise(function resolver(resolve, reject){
187 | discovery.deleteEnvironment(params)
188 | .then((response) => {
189 | msg.environment = response.result ? response.result : response;
190 | resolve();
191 | })
192 | .catch((err) => {
193 | reject(err);
194 | });
195 | });
196 | return p;
197 | }
198 |
199 | function executeListCollections(node, discovery, params, msg) {
200 | var p = new Promise(function resolver(resolve, reject){
201 | discovery.listCollections(params)
202 | .then((response) => {
203 | responseutils.parseResponseFor(msg, response, 'collections');
204 | resolve();
205 | })
206 | .catch((err) => {
207 | reject(err);
208 | });
209 | });
210 | return p;
211 | }
212 |
213 | function executeGetCollectionDetails(node, discovery, params, msg) {
214 | var p = new Promise(function resolver(resolve, reject){
215 | discovery.getCollection(params)
216 | .then((response) => {
217 | msg.collection_details = response.result ? response.result : response;
218 | resolve();
219 | })
220 | .catch((err) => {
221 | reject(err);
222 | });
223 | });
224 | return p;
225 | }
226 |
227 | function executeListExpansions(node, discovery, params, msg) {
228 | var p = new Promise(function resolver(resolve, reject){
229 | discovery.listExpansions(params)
230 | .then((response) => {
231 | responseutils.parseResponseFor(msg, response, 'expansions');
232 | resolve();
233 | })
234 | .catch((err) => {
235 | reject(err);
236 | });
237 | });
238 | return p;
239 | }
240 |
241 | function executeListTrainingData(node, discovery, params, msg) {
242 | var p = new Promise(function resolver(resolve, reject){
243 | discovery.listTrainingData(params)
244 | .then((response) => {
245 | msg.trainingData = response.result ? response.result : response;
246 | resolve();
247 | })
248 | .catch((err) => {
249 | reject(err);
250 | });
251 | });
252 | return p;
253 | }
254 |
255 |
256 | function executeCreateConfiguration(node, discovery, params, msg) {
257 | var p = new Promise(function resolver(resolve, reject){
258 | discovery.createConfiguration(params)
259 | .then((response) => {
260 | msg.configuration = response.result ? response.result : response;
261 | resolve();
262 | })
263 | .catch((err) => {
264 | reject(err);
265 | });
266 | });
267 | return p;
268 | }
269 |
270 | function executeListConfigurations(node, discovery, params, msg) {
271 | var p = new Promise(function resolver(resolve, reject){
272 | discovery.listConfigurations(params)
273 | .then((response) => {
274 | responseutils.parseResponseFor(msg, response, 'configurations');
275 | resolve();
276 | })
277 | .catch((err) => {
278 | reject(err);
279 | });
280 | });
281 | return p;
282 | }
283 |
284 | function executeGetConfigurationDetails(node, discovery, params, msg) {
285 | var p = new Promise(function resolver(resolve, reject){
286 | discovery.getConfiguration(params)
287 | .then((response) => {
288 | msg.configuration_details = response.result ? response.result : response;
289 | resolve();
290 | })
291 | .catch((err) => {
292 | reject(err);
293 | });
294 | });
295 | return p;
296 | }
297 |
298 | function executeQuery(node, discovery, params, msg) {
299 | var p = new Promise(function resolver(resolve, reject){
300 | discovery.query(params)
301 | .then((response) => {
302 | msg.search_results = response;
303 | if (response && response.result) {
304 | msg.search_results = response.result;
305 | }
306 | resolve();
307 | })
308 | .catch((err) => {
309 | reject(err);
310 | });
311 | });
312 | return p;
313 | }
314 |
315 | function executeQueryNotices(node, discovery, params, msg) {
316 | var p = new Promise(function resolver(resolve, reject){
317 | discovery.queryNotices(params)
318 | .then((response) => {
319 | msg.search_results = response;
320 | if (response && response.result) {
321 | msg.search_results = response.result;
322 | }
323 | resolve();
324 | })
325 | .catch((err) => {
326 | reject(err);
327 | });
328 | });
329 | return p;
330 | }
331 |
332 | function unknownMethod(node, discovery, params, msg) {
333 | return Promise.reject('Unable to process as unknown mode has been specified');
334 | }
335 |
336 | function executeMethod(node, method, params, msg) {
337 | let discovery = discoveryutils.buildService(apikey, endpoint);
338 |
339 | let exe = ExecutionList[method];
340 | if (!exe) {
341 | exe = unknownMethod
342 | }
343 |
344 | return exe(node, discovery, params, msg);
345 | }
346 |
347 | function initialCheck(k, m) {
348 | var message = '';
349 | if (!k) {
350 | message = 'Missing Watson Discovery service credentials';
351 | } else if (!m || '' === m) {
352 | message = 'Required Discovery method has not been specified';
353 | }
354 | if (message){
355 | return Promise.reject(message);
356 | }
357 | return Promise.resolve();
358 | }
359 |
360 | if (dservice) {
361 | sApikey = dservice.apikey ? dservice.apikey : '';
362 | sEndpoint = dservice.url ? dservice.url : '';
363 | }
364 |
365 | RED.httpAdmin.get('/watson-discovery/vcap', function (req, res) {
366 | res.json(serviceutils.checkServiceBound(SERVICE_IDENTIFIER));
367 | });
368 |
369 |
370 | function Node (config) {
371 | var node = this;
372 | RED.nodes.createNode(this, config);
373 |
374 | this.on('input', function(msg, send, done) {
375 |
376 | var method = config['discovery-method'],
377 | message = '',
378 | params = {};
379 |
380 | apikey = sApikey || node.credentials.apikey;
381 |
382 | endpoint = sEndpoint;
383 | if (config['service-endpoint']) {
384 | endpoint = config['service-endpoint'];
385 | }
386 |
387 | node.status({});
388 | initialCheck(apikey, method)
389 | .then(function(){
390 | params = discoveryutils.buildParams(msg,config);
391 | return checkParams(method, params);
392 | })
393 | .then(function(){
394 | node.status({fill:'blue', shape:'dot', text:'requesting'});
395 | return executeMethod(node, method, params, msg);
396 | })
397 | .then(function(){
398 | node.status({});
399 | send(msg);
400 | done();
401 | })
402 | .catch(function(err){
403 | let errMsg = payloadutils.reportError(node, msg, err);
404 | done(errMsg);
405 | });
406 | });
407 | }
408 |
409 | RED.nodes.registerType('watson-discovery-v1', Node, {
410 | credentials: {
411 | apikey: {type:'password'}
412 | }
413 | });
414 | };
415 |
--------------------------------------------------------------------------------
/services/discovery/v2-project-manager.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | module.exports = function (RED) {
18 |
19 | const SERVICE_IDENTIFIER = 'discovery';
20 | var discoveryutils = require('./discovery-utils2'),
21 | serviceutils = require('../../utilities/service-utils'),
22 | payloadutils = require('../../utilities/payload-utils'),
23 | responseutils = require('../../utilities/response-utils'),
24 | dservice = serviceutils.getServiceCreds(SERVICE_IDENTIFIER),
25 | apikey = null,
26 | sApikey = null,
27 | endpoint = '',
28 | sEndpoint = '';
29 |
30 | const ExecutionList = {
31 | 'listProjects' : executeListProjects,
32 | 'getProject' : executeDiscoveryMethod,
33 | 'createProject' : executeDiscoveryMethod,
34 | 'updateProject' : executeDiscoveryMethod,
35 | 'deleteProject' : executeDiscoveryMethod,
36 |
37 | 'listCollections' : executeListCollections,
38 | 'getCollection' : executeDiscoveryMethod,
39 | 'createCollection' : executeDiscoveryMethod,
40 | 'updateCollection' : executeDiscoveryMethod,
41 | 'deleteCollection' : executeDiscoveryMethod,
42 |
43 | 'getComponentSettings' : executeDiscoveryMethod,
44 |
45 | 'listTrainingQueries' : executeListQueries,
46 | 'getTrainingQuery' : executeDiscoveryMethod,
47 | 'deleteTrainingQueries' : executeDiscoveryMethod,
48 | 'deleteTrainingQuery' : executeDiscoveryMethod,
49 |
50 | 'query' : executeDiscoveryMethod
51 | };
52 |
53 | function executeListProjects(fields) {
54 | fields.response = "projects";
55 | return executeListMethod(fields)
56 | }
57 |
58 | function executeListCollections(fields) {
59 | fields.response = "collections";
60 | return executeListMethod(fields)
61 | }
62 |
63 | function executeListQueries(fields) {
64 | fields.response = "queries";
65 | return executeListMethod(fields)
66 | }
67 |
68 | function executeListMethod(fields) {
69 | var p = new Promise(function resolver(resolve, reject){
70 | fields.discovery[fields.method](fields.params)
71 | .then((response) => {
72 | responseutils.parseResponseFor(fields.msg, response, fields.response);
73 | resolve();
74 | })
75 | .catch((err) => {
76 | reject(err);
77 | });
78 | });
79 | return p;
80 | }
81 |
82 |
83 | function executeDiscoveryMethod(fields) {
84 | var p = new Promise(function resolver(resolve, reject){
85 | fields.discovery[fields.method](fields.params)
86 | .then((response) => {
87 | fields.msg.discovery_response = response;
88 | if (response && response.result) {
89 | fields.msg.discovery_response = response.result;
90 | }
91 | resolve();
92 | })
93 | .catch((err) => {
94 | reject(err);
95 | });
96 | });
97 | return p;
98 | }
99 |
100 |
101 | function initialCheck(k, m) {
102 | var message = '';
103 | if (!k) {
104 | message = 'Missing Watson Discovery service credentials';
105 | } else if (!m || '' === m) {
106 | message = 'Required Discovery method has not been specified';
107 | }
108 | if (message){
109 | return Promise.reject(message);
110 | }
111 | return Promise.resolve();
112 | }
113 |
114 | function checkParams(method, params) {
115 | var response = '';
116 |
117 | switch (method) {
118 | case 'query':
119 | response = discoveryutils.paramProjectCheck(params)
120 | + discoveryutils.paramQueryFieldCheck(params) ;
121 | break;
122 |
123 | case 'getProject':
124 | case 'deleteProject':
125 | case 'listCollections':
126 | case 'getComponentSettings':
127 | case 'listTrainingQueries':
128 | case 'deleteTrainingQueries':
129 | response = discoveryutils.paramProjectCheck(params);
130 | break;
131 |
132 | case 'getCollection':
133 | response = discoveryutils.paramCollectionCheck(params);
134 | break;
135 |
136 | case 'createProject':
137 | response = discoveryutils.paramNameCheck(params)
138 | + discoveryutils.paramTypeCheck(params);
139 | break;
140 |
141 | case 'updateProject':
142 | case 'createCollection':
143 | response = discoveryutils.paramProjectCheck(params)
144 | + discoveryutils.paramNameCheck(params);
145 | break;
146 |
147 | case 'updateCollection':
148 | case 'deleteCollection':
149 | response = discoveryutils.paramProjectCheck(params)
150 | + discoveryutils.paramCollectionCheck(params);
151 | break;
152 |
153 | case 'getTrainingQuery':
154 | case 'deleteTrainingQuery':
155 | response = discoveryutils.paramQueryIdCheck(params);
156 | break;
157 | }
158 |
159 | if (response) {
160 | return Promise.reject(response);
161 | } else {
162 | return Promise.resolve();
163 | }
164 | }
165 |
166 | function unknownMethod(node, discovery, params, msg) {
167 | return Promise.reject('Unable to process as unknown mode has been specified');
168 | }
169 |
170 | function executeMethod(node, method, params, msg) {
171 | let discovery = discoveryutils.buildService(apikey, endpoint);
172 |
173 | let exe = ExecutionList[method];
174 | if (!exe) {
175 | exe = unknownMethod
176 | }
177 |
178 | let fields = {
179 | node : node,
180 | discovery : discovery,
181 | params : params,
182 | msg : msg,
183 | method : method
184 | }
185 |
186 | return exe(fields);
187 | }
188 |
189 |
190 | if (dservice) {
191 | sApikey = dservice.apikey ? dservice.apikey : '';
192 | sEndpoint = dservice.url ? dservice.url : '';
193 | }
194 |
195 | RED.httpAdmin.get('/watson-discovery-v2-pm/vcap', function (req, res) {
196 | res.json(serviceutils.checkServiceBound(SERVICE_IDENTIFIER));
197 | });
198 |
199 |
200 | function Node (config) {
201 | var node = this;
202 | RED.nodes.createNode(this, config);
203 |
204 | this.on('input', function(msg, send, done) {
205 |
206 | var method = config['discovery-method'],
207 | params = {};
208 |
209 | apikey = sApikey || node.credentials.apikey;
210 |
211 | endpoint = sEndpoint;
212 | if (config['service-endpoint']) {
213 | endpoint = config['service-endpoint'];
214 | }
215 |
216 | node.status({});
217 | initialCheck(apikey, method)
218 | .then(() => {
219 | params = discoveryutils.buildParams(msg,config);
220 | if (method == 'query' || method == 'querySearch') {
221 | if (method == 'query') {
222 | params.naturalLanguageQuery = msg.payload;
223 | } else {
224 | params.query = msg.payload;
225 | method = 'query';
226 | }
227 | params = discoveryutils.addQueryParams(msg, params);
228 | }
229 | return checkParams(method, params);
230 | })
231 | .then(function(){
232 | node.status({fill:'blue', shape:'dot', text:'requesting'});
233 | return executeMethod(node, method, params, msg);
234 | })
235 | .then(function(){
236 | node.status({});
237 | send(msg);
238 | done();
239 | })
240 | .catch(function(err){
241 | let errMsg = payloadutils.reportError(node, msg, err);
242 | done(errMsg);
243 | });
244 | });
245 | }
246 |
247 | RED.nodes.registerType('watson-discovery-v2-project-manager', Node, {
248 | credentials: {
249 | apikey: {type:'password'}
250 | }
251 | });
252 | };
253 |
--------------------------------------------------------------------------------
/services/language_translator/icons/LanguageTranslator25x25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/watson-developer-cloud/node-red-node-watson/dc29f8dd09bdbfd669c5560ed0e0b8eccb4e149f/services/language_translator/icons/LanguageTranslator25x25.png
--------------------------------------------------------------------------------
/services/language_translator/translator-utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, 2022 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 |
18 | class TranslatorUtils {
19 | constructor() {
20 | }
21 |
22 | credentialCheck(k) {
23 | if (!k) {
24 | return Promise.reject('Missing Watson Language Translator service credentials');
25 | }
26 | return Promise.resolve();
27 | }
28 |
29 | checkForAction(action) {
30 | if (!action) {
31 | return Promise.reject('Missing action, please select one');
32 | }
33 | return Promise.resolve();
34 | }
35 |
36 | }
37 |
38 | var translatorutils = new TranslatorUtils();
39 | module.exports = translatorutils ;
40 |
--------------------------------------------------------------------------------
/services/language_translator_identify/icons/LanguageTranslator25x25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/watson-developer-cloud/node-red-node-watson/dc29f8dd09bdbfd669c5560ed0e0b8eccb4e149f/services/language_translator_identify/icons/LanguageTranslator25x25.png
--------------------------------------------------------------------------------
/services/language_translator_identify/v3.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
38 |
39 |
55 |
56 |
97 |
--------------------------------------------------------------------------------
/services/language_translator_identify/v3.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013,2022 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | module.exports = function (RED) {
18 | const SERVICE_IDENTIFIER = 'language-translator',
19 | LanguageTranslatorV3 = require('ibm-watson/language-translator/v3'),
20 | { IamAuthenticator } = require('ibm-watson/auth');
21 |
22 | var pkg = require('../../package.json'),
23 | payloadutils = require('../../utilities/payload-utils'),
24 | serviceutils = require('../../utilities/service-utils'),
25 | responseutils = require('../../utilities/response-utils'),
26 | service = serviceutils.getServiceCreds(SERVICE_IDENTIFIER),
27 | apikey = null,
28 | sApikey = null,
29 | endpoint = '', sEndpoint = '';
30 | //endpointUrl = 'https://gateway.watsonplatform.net/language-translator/api';
31 |
32 | if (service) {
33 | sApikey = service.apikey ? service.apikey : '';
34 | sEndpoint = service.url;
35 | }
36 |
37 | function initialCheck(k) {
38 | if (!k) {
39 | return Promise.reject('Missing Watson Language Translator service credentials');
40 | }
41 | return Promise.resolve();
42 | }
43 |
44 | function payloadCheck(msg) {
45 | if (!msg.payload) {
46 | return Promise.reject('Missing property: msg.payload');
47 | }
48 | return Promise.resolve();
49 | }
50 |
51 | function execute(node, msg) {
52 | var p = new Promise(function resolver(resolve, reject) {
53 | let language_translator = null,
54 | authSettings = {},
55 | serviceSettings = {
56 | version: '2018-05-01',
57 | headers: {
58 | 'User-Agent': pkg.name + '-' + pkg.version
59 | }
60 | };
61 |
62 | if (apikey) {
63 | authSettings.apikey = apikey;
64 | }
65 | serviceSettings.authenticator = new IamAuthenticator(authSettings);
66 |
67 | if (endpoint) {
68 | serviceSettings.url = endpoint;
69 | }
70 |
71 | language_translator = new LanguageTranslatorV3(serviceSettings);
72 |
73 | language_translator.identify({text: msg.payload})
74 | .then((response) => {
75 | responseutils.parseResponseFor(msg, response, 'languages');
76 |
77 | if (msg.languages && Array.isArray(msg.languages)) {
78 | msg.lang = msg.languages[0];
79 | }
80 | resolve();
81 | })
82 | .catch((err) => {
83 | reject(err);
84 | })
85 | });
86 | return p;
87 | }
88 |
89 | RED.httpAdmin.get('/watson-language-translator-identify/vcap', function (req, res) {
90 | res.json(service ? {bound_service: true} : null);
91 | });
92 |
93 | function Node (config) {
94 | var node = this;
95 | RED.nodes.createNode(this, config);
96 |
97 | this.on('input', function(msg, send, done) {
98 | apikey = sApikey || this.credentials.apikey;
99 |
100 | endpoint = sEndpoint;
101 | if (config['service-endpoint']) {
102 | endpoint = config['service-endpoint'];
103 | }
104 |
105 | node.status({});
106 | initialCheck(apikey)
107 | .then(function(){
108 | return payloadCheck(msg);
109 | })
110 | .then(function(){
111 | node.status({fill:'blue', shape:'dot', text:'requesting'});
112 | return execute(node, msg);
113 | })
114 | .then(function(){
115 | node.status({});
116 | send(msg);
117 | done();
118 | })
119 | .catch(function(err){
120 | let errMsg = payloadutils.reportError(node, msg, err);
121 | done(errMsg);
122 | });
123 | });
124 | }
125 | RED.nodes.registerType('watson-language-translator-identify', Node, {
126 | credentials: {
127 | apikey: {type:'password'}
128 | }
129 | });
130 | };
131 |
--------------------------------------------------------------------------------
/services/language_translator_util/icons/LanguageTranslator25x25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/watson-developer-cloud/node-red-node-watson/dc29f8dd09bdbfd669c5560ed0e0b8eccb4e149f/services/language_translator_util/icons/LanguageTranslator25x25.png
--------------------------------------------------------------------------------
/services/language_translator_util/v3.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
38 |
39 |
56 |
57 |
58 |
94 |
--------------------------------------------------------------------------------
/services/language_translator_util/v3.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013,2016 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an 'AS IS' BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | module.exports = function (RED) {
18 | const SERVICE_IDENTIFIER = 'language-translator',
19 | LanguageTranslatorV3 = require('ibm-watson/language-translator/v3'),
20 | { IamAuthenticator } = require('ibm-watson/auth'),
21 | LANGS = {
22 | 'es': 'Spanish',
23 | 'ar': 'Arabic',
24 | 'arz': 'Spoken Arabic',
25 | 'en': 'English',
26 | 'fr': 'French',
27 | 'it': 'Italian',
28 | 'zh': 'Chinese',
29 | 'ko': 'Korean',
30 | 'pt': 'Portuguese',
31 | 'de': 'German',
32 | 'ja': 'Japanese',
33 | 'nl': 'Dutch',
34 | 'pl': 'Polish',
35 | 'ru': 'Russian',
36 | 'tr': 'Turkish',
37 | 'zh-TW' : 'Taiwanese',
38 | 'zht': 'Traditional Chinese',
39 | 'bg' : 'Bulgarian',
40 | 'ca' : 'Catalan',
41 | 'cs' : 'Czech',
42 | 'da' : 'Danish',
43 | 'el' : 'Greek',
44 | 'et' : 'Estonian',
45 | 'fi' : 'Finnish',
46 | 'ga' : 'Galican',
47 | 'he' : 'Hebrew',
48 | 'hi' : 'Hindi',
49 | 'hr' : 'Croatian',
50 | 'hu' : 'Hungarian',
51 | 'id' : 'Indonesian',
52 | 'lt' : 'Lithuanian',
53 | 'ms' : 'Malay',
54 | 'nb' : 'Norwegian Bokmål',
55 | 'ro' : 'Romanian',
56 | 'sk' : 'Slovak',
57 | 'sl' : 'Slovenian',
58 | 'sv' : 'Swedish',
59 | 'th' : 'Thai'
60 | };
61 |
62 | var pkg = require('../../package.json'),
63 | cfenv = require('cfenv'),
64 | apikey = null, sApikey = null,
65 | payloadutils = require('../../utilities/payload-utils'),
66 | serviceutils = require('../../utilities/service-utils'),
67 | responseutils = require('../../utilities/response-utils'),
68 | //service = cfenv.getAppEnv().getServiceCreds(/language translator/i),
69 | service = serviceutils.getServiceCreds(SERVICE_IDENTIFIER),
70 | endpoint = '',
71 | sEndpoint = 'https://gateway.watsonplatform.net/language-translator/api';
72 |
73 | //endpointUrl = 'https://gateway.watsonplatform.net/language-translator/api';
74 |
75 | if (service) {
76 | sApikey = service.apikey ? service.apikey : '';
77 | sEndpoint = service.url;
78 | }
79 |
80 | // These are APIs that the node has created to allow it to dynamically fetch IBM Cloud
81 | // credentials, and also translation models. This allows the node to keep up to
82 | // date with new tranlations, without the need for a code update of this node.
83 |
84 | // Node RED Admin - fetch and set vcap services
85 | RED.httpAdmin.get('/watson-translator-util/vcap', function (req, res) {
86 | res.json(service ? {bound_service: true} : null);
87 | });
88 |
89 | // This is the Language Translation Node.
90 | // The node supports four modes
91 | //
92 | // 1. translate, for which it will specify a domain, obtained from the available models
93 | // along with source and target languages. The node will have only displayed
94 | // available translations for the model / domain
95 | // 2. train, for which a glossary file is required.
96 | // 3. status, to determine whethere a trained corpus is available
97 | // 4. delete, to remove a trained corpus extension.
98 |
99 | function SMTNode (config) {
100 | RED.nodes.createNode(this, config);
101 | var node = this;
102 |
103 | // The dynamic nature of this node has caused problems with the password field. it is
104 | // hidden but not a credential. If it is treated as a credential, it gets lost when there
105 | // is a request to refresh the model list.
106 | // Credentials are needed for each of the modes.
107 |
108 | apikey = sApikey || this.credentials.apikey || config.apikey;
109 |
110 | // The node has received an input as part of a flow, need to determine
111 | // what the request is for, and based on that if the required fields
112 | // have been provided.
113 | this.on('input', function(msg, send, done) {
114 |
115 | let message = '',
116 | authSettings = {},
117 | serviceSettings = {
118 | version: '2018-05-01',
119 | headers: {
120 | 'User-Agent': pkg.name + '-' + pkg.version
121 | }
122 | };
123 |
124 | if (!apikey) {
125 | message = 'Missing Language Translation service credentials';
126 | node.error(message, msg);
127 | return;
128 | }
129 |
130 | authSettings.apikey = apikey;
131 | serviceSettings.authenticator = new IamAuthenticator(authSettings);
132 |
133 | endpoint = sEndpoint;
134 | if ((!config['default-endpoint']) && config['service-endpoint']) {
135 | endpoint = config['service-endpoint'];
136 | }
137 | if (endpoint) {
138 | serviceSettings.url = endpoint;
139 | }
140 |
141 | var lt = new LanguageTranslatorV3(serviceSettings);
142 |
143 | // set global variable in order to make them accessible for the tranlsation node
144 | var globalContext = this.context().global;
145 |
146 | globalContext.set('g_domain','');
147 | globalContext.set('g_src','');
148 | globalContext.set('g_dest','');
149 | globalContext.set('g_model_id','');
150 |
151 | // ---- UTILITY FUNCTIONS ----
152 | // this functions creates a N dimensional array
153 | // it will be used in order to make an array of arrays from the wanted options to populate a dashboard dropdown list
154 | // the entries of the array to be created would be 'domains', 'model_id', 'source' & 'target
155 | function capitalize (string) {
156 | return string.charAt(0).toUpperCase() + string.slice(1);
157 | }
158 |
159 | function makeLanguageBeautifier(string) {
160 | if (LANGS[string]) {
161 | return LANGS[string];
162 | }
163 | return string;
164 | }
165 | // ---- END OF UTILITY FUNCTIONS ----
166 |
167 | if (lt) {
168 | node.status({fill:'blue', shape:'dot', text:'fetching models'});
169 | lt.listModels({})
170 | .then((response) => {
171 | node.status({fill:'blue', shape:'dot', text:'parsing response'});
172 | responseutils.parseResponseFor(msg, response, 'models');
173 |
174 | msg.payload = msg.models;
175 | // the overall array would be used to populate the dropdown list
176 | var dropdown_array = [];
177 | var domain_src_target_model = [];
178 | var domain_src_target = '';
179 | var sTmp3 = '';
180 | msg.dropdown_object = {};
181 |
182 | // Populating 'DOMAIN's into an array which would be returned by the msg object
183 | var ldom = []; // domains array
184 | msg.options_ldom = {};
185 | msg.domains = {};
186 |
187 | // Populating 'model_id' into an array which would be returned by the msg object
188 | var model_id_array = []; // model_id array
189 | msg.options_model_id = {};
190 | msg.model_id_obj = {};
191 |
192 | // Populating 'source's into an array which would be returned by the msg object
193 | var src_lang_array = []; // source language array
194 | msg.options_src_lang = {};
195 | msg.src_lang_object = {};
196 |
197 | // Populating 'target's into an array which would be returned by the msg object
198 | var target_lang_array = []; // dest language array
199 | msg.options_target_lang = {};
200 | msg.target_lang_object = {};
201 |
202 | for (var i = 0; i < msg.models.length; i++) {
203 | ldom[i] = msg.models[i].domain;
204 | ldom[i] = capitalize(ldom[i]);
205 | model_id_array[i] = msg.models[i].model_id;
206 | src_lang_array[i] = msg.models[i].source;
207 | src_lang_array[i] = makeLanguageBeautifier(src_lang_array[i]);
208 | target_lang_array[i] = msg.models[i].target;
209 | target_lang_array[i] = makeLanguageBeautifier(target_lang_array[i]);
210 |
211 | sTmp3 = makeLanguageBeautifier(target_lang_array[i]);
212 |
213 | domain_src_target = ldom[i] + ', ' + src_lang_array[i] + ', ' + target_lang_array[i];
214 |
215 | var j = {};
216 | j[domain_src_target] = model_id_array[i];
217 | domain_src_target_model.push(j);
218 |
219 | dropdown_array[i] = domain_src_target_model[i];
220 | }
221 |
222 | model_id_array.sort();
223 |
224 | dropdown_array.sort();
225 |
226 | // Domains unique values
227 | ldom.forEach(function(item) {
228 | msg.options_ldom[item] = item;
229 | });
230 |
231 | // model_id unique values
232 | model_id_array.forEach(function(item) {
233 | msg.options_model_id[item] = item;
234 | });
235 |
236 | // source language unique values
237 | src_lang_array.forEach(function(item) {
238 | msg.options_src_lang[item] = item;
239 | });
240 |
241 | // target language unique values
242 | target_lang_array.forEach(function(item) {
243 | msg.options_target_lang[item] = item;
244 | });
245 |
246 | msg.domains = Object.keys(msg.options_ldom);
247 | msg.model_id_obj = Object.keys(msg.options_model_id);
248 | msg.src_lang_object = Object.keys(msg.options_src_lang);
249 | msg.target_lang_object = Object.keys(msg.options_target_lang);
250 | msg.dropdown_object = dropdown_array;
251 |
252 | node.status({});
253 | send(msg);
254 | done();
255 | })
256 | .catch((err) => {
257 | let errMsg = payloadutils.reportError(node, msg, err);
258 | done(errMsg);
259 | });
260 | } else {
261 | node.error('Error instantiating the language service',msg);
262 | }
263 | });
264 | }
265 |
266 | RED.nodes.registerType('watson-translator-util', SMTNode, {
267 | credentials: {
268 | apikey: {type:'password'}
269 | }
270 | });
271 | };
272 |
--------------------------------------------------------------------------------
/services/natural_language_classifier/icons/NLClassifier.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/watson-developer-cloud/node-red-node-watson/dc29f8dd09bdbfd669c5560ed0e0b8eccb4e149f/services/natural_language_classifier/icons/NLClassifier.png
--------------------------------------------------------------------------------
/services/natural_language_classifier/icons/temp.txt:
--------------------------------------------------------------------------------
1 | temp text file
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/services/natural_language_classifier/v1.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
73 |
74 |
114 |
115 |
192 |
--------------------------------------------------------------------------------
/services/natural_language_classifier/v1.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013,2015 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | module.exports = function(RED) {
18 | const SERVICE_IDENTIFIER = 'natural-language-classifier',
19 | NaturalLanguageClassifierV1 = require('ibm-watson/natural-language-classifier/v1'),
20 | { IamAuthenticator } = require('ibm-watson/auth');
21 |
22 | var pkg = require('../../package.json'),
23 | temp = require('temp'),
24 | fs = require('fs'),
25 | serviceutils = require('../../utilities/service-utils'),
26 | payloadutils = require('../../utilities/payload-utils'),
27 | responseutils = require('../../utilities/response-utils'),
28 | service = serviceutils.getServiceCreds(SERVICE_IDENTIFIER),
29 | apikey = null,
30 | sApikey = null,
31 | endpoint = '',
32 | sEndpoint = 'https://gateway.watsonplatform.net/natural-language-classifier/api';
33 |
34 | if (service) {
35 | sApikey = service.apikey ? service.apikey : '';
36 | sEndpoint = service.url;
37 | }
38 |
39 | temp.track();
40 |
41 |
42 | RED.httpAdmin.get('/watson-natural-language-classifier/vcap', function(req, res) {
43 | res.json(service ? {
44 | bound_service: true
45 | } : null);
46 | });
47 |
48 | function Node(config) {
49 | RED.nodes.createNode(this, config);
50 | let node = this;
51 |
52 | // Perform basic check to see that credentials
53 | // are provided, altough they may still be
54 | // invalid
55 | node.verifyCredentials = function(msg) {
56 | apikey = sApikey || node.credentials.apikey;
57 |
58 | endpoint = sEndpoint;
59 | if (config['service-endpoint']) {
60 | endpoint = config['service-endpoint'];
61 | }
62 |
63 | if (!apikey) {
64 | return Promise.reject('Missing Natural Language Classifier credentials');
65 | } else {
66 | return Promise.resolve();
67 | }
68 | };
69 |
70 | // default the mode if not what expected.
71 | node.modeCheck = function(msg) {
72 | switch(config.mode) {
73 | case 'classify':
74 | case 'createClassifier':
75 | case 'listClassifiers':
76 | case 'getClassifier':
77 | case 'deleteClassifier':
78 | break;
79 | default:
80 | config.mode = 'classify';
81 | }
82 | return Promise.resolve();
83 | };
84 |
85 | // Sanity check on the payload, must be present
86 | node.payloadCheck = function(msg) {
87 | switch(config.mode) {
88 | case 'classify':
89 | case 'createClassifier':
90 | if (!msg.payload) {
91 | return Promise.reject('Payload is required');
92 | }
93 | break;
94 | }
95 | return Promise.resolve();
96 | };
97 |
98 | node.payloadCollectionCheck = function(msg, config, payloadData) {
99 | if ('classify' === config.mode) {
100 | if ('string' === typeof msg.payload && (! config['collections-off'])) {
101 | let collection = msg.payload.match( /\(?([^.?!]|\.\w)+[.?!]\)?/g );
102 | if (collection && collection.length > 1) {
103 | payloadData.collection = [];
104 | collection.forEach((s) => {
105 | let textObject = { text : s };
106 | payloadData.collection.push(textObject);
107 | });
108 | }
109 | } else if (Array.isArray(msg.payload)){
110 | payloadData.collection = [];
111 | msg.payload.forEach((p) => {
112 | if ('string' === typeof p) {
113 | let textObject = { text : p };
114 | payloadData.collection.push(textObject);
115 | } else if ('object' === typeof p) {
116 | payloadData.collection.push(p);
117 | }
118 | });
119 | }
120 | }
121 | return Promise.resolve();
122 | };
123 |
124 | // Standard temp file open
125 | node.openTemp = function() {
126 | var p = new Promise(function resolver(resolve, reject) {
127 | temp.open({
128 | suffix: '.csv'
129 | }, function(err, info) {
130 | if (err) {
131 | reject(err);
132 | } else {
133 | resolve(info);
134 | }
135 | });
136 | });
137 | return p;
138 | };
139 |
140 | node.streamFile = function(msg, config, info) {
141 | var p = new Promise(function resolver(resolve, reject){
142 | payloadutils.stream_buffer(info.path, msg.payload, function(format) {
143 | resolve(info);
144 | });
145 | });
146 | return p;
147 | };
148 |
149 |
150 | // If this is a create then the payload will be a stream
151 | node.checkForCreate = function(msg, config) {
152 | if ('createClassifier' !== config.mode) {
153 | return Promise.resolve(null);
154 | } else if ('string' === typeof msg.payload) {
155 | return Promise.resolve(null);
156 | }
157 | return node.openTemp()
158 | .then(function(info) {
159 | return node.streamFile(msg, config, info);
160 | });
161 | };
162 |
163 | node.buildParams = function(msg, config, info, payloadData) {
164 | var params = {},
165 | message = '';
166 |
167 | switch (config.mode) {
168 | case 'classify':
169 | if (payloadData && payloadData.collection) {
170 | params.collection = payloadData.collection;
171 | } else {
172 | params.text = msg.payload;
173 | }
174 |
175 | params.classifierId = config.classifier;
176 | if (msg.nlcparams && msg.nlcparams.classifier_id) {
177 | params.classifierId = msg.nlcparams.classifier_id;
178 | }
179 | break;
180 | case 'createClassifier':
181 | params.language = config.language;
182 |
183 | let meta = {
184 | language : config.language
185 | };
186 |
187 | params.trainingMetadata = Buffer.from(JSON.stringify(meta));
188 |
189 | //params.trainingMetadata = meta;
190 |
191 | if ('string' === typeof msg.payload) {
192 | params.trainingData = msg.payload;
193 | } else {
194 | params.trainingData = fs.createReadStream(info.path);
195 | }
196 | break;
197 | case 'deleteClassifier':
198 | case 'listClassifiers':
199 | case 'getClassifier':
200 | params.classifierId = config.classifier ? config.classifier : msg.payload;
201 | if (msg.nlcparams && msg.nlcparams.classifier_id) {
202 | params.classifierId = msg.nlcparams.classifier_id;
203 | }
204 | break;
205 | case 'listClassifiers':
206 | break;
207 | default:
208 | message = 'Unknown Natural Language Classification mode, ' + config.mode;
209 | }
210 |
211 | if (message) {
212 | return Promise.reject(message);
213 | } else {
214 | return Promise.resolve(params);
215 | }
216 | };
217 |
218 | node.performOperation = function(msg, config, params) {
219 | var p = new Promise(function resolver(resolve, reject) {
220 | let natural_language_classifier = null,
221 | authSettings = {};
222 | serviceSettings = {
223 | version: 'v1',
224 | headers: {
225 | 'User-Agent': pkg.name + '-' + pkg.version
226 | }
227 | };
228 |
229 | if (apikey) {
230 | authSettings.apikey = apikey;
231 | }
232 | serviceSettings.authenticator = new IamAuthenticator(authSettings);
233 |
234 | if (endpoint) {
235 | serviceSettings.url = endpoint;
236 | }
237 |
238 | natural_language_classifier = new NaturalLanguageClassifierV1(serviceSettings);
239 |
240 | let mode = config.mode;
241 | if (params.collection) {
242 | mode = 'classifyCollection';
243 | }
244 |
245 | natural_language_classifier[mode](params)
246 | .then((response) => {
247 | switch (mode) {
248 | case 'classify':
249 | responseutils.parseResponseFor(msg, response, 'result');
250 | msg.payload = {
251 | classes: msg.result.classes,
252 | top_class: msg.result.top_class
253 | };
254 | break;
255 | case 'classifyCollection':
256 | responseutils.parseResponseFor(msg, response, 'collection');
257 | msg.payload = msg.collection;
258 | break;
259 | case 'listClassifiers':
260 | responseutils.parseResponseFor(msg, response, 'classifiers');
261 | msg.payload = msg.classifiers;
262 | break;
263 | case 'getClassifier':
264 | responseutils.parseResponseFor(msg, response, 'result');
265 | msg.payload = msg.result;
266 | break;
267 | default:
268 | msg.payload = response;
269 | }
270 | resolve();
271 | })
272 | .catch((err) => {
273 | reject(err);
274 | });
275 | });
276 |
277 | return p;
278 | };
279 |
280 |
281 | this.on('input', function(msg, send, done) {
282 | //var params = {}
283 | let payloadData = {};
284 |
285 | node.verifyCredentials(msg)
286 | .then(function() {
287 | return node.modeCheck(msg);
288 | })
289 | .then(function() {
290 | return node.payloadCheck(msg);
291 | })
292 | .then(function() {
293 | return node.payloadCollectionCheck(msg, config, payloadData);
294 | })
295 | .then(function() {
296 | return node.checkForCreate(msg, config);
297 | })
298 | .then(function(info) {
299 | return node.buildParams(msg, config, info, payloadData);
300 | })
301 | .then(function(params) {
302 | node.status({
303 | fill: 'blue',
304 | shape: 'dot',
305 | text: 'requesting'
306 | });
307 | return node.performOperation(msg, config, params);
308 | })
309 | .then(function() {
310 | temp.cleanup();
311 | node.status({});
312 | send(msg);
313 | done();
314 | })
315 | .catch(function(err) {
316 | let errMsg = payloadutils.reportError(node, msg, err);
317 | done(errMsg);
318 | });
319 | });
320 | }
321 | RED.nodes.registerType('watson-natural-language-classifier', Node, {
322 | credentials: {
323 | apikey: {type:'password'}
324 | }
325 | });
326 | };
327 |
--------------------------------------------------------------------------------
/services/natural_language_understanding/icons/NaturalLanguageUnderstanding.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/watson-developer-cloud/node-red-node-watson/dc29f8dd09bdbfd669c5560ed0e0b8eccb4e149f/services/natural_language_understanding/icons/NaturalLanguageUnderstanding.png
--------------------------------------------------------------------------------
/services/natural_language_understanding/v1-model-manager.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
45 |
46 |
47 |
86 |
87 |
114 |
--------------------------------------------------------------------------------
/services/natural_language_understanding/v1-model-manager.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2020 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | module.exports = function(RED) {
18 | const SERVICE_IDENTIFIER = 'natural-language-understanding',
19 | NaturalLanguageUnderstandingV1 = require('ibm-watson/natural-language-understanding/v1'),
20 | { IamAuthenticator } = require('ibm-watson/auth'),
21 | FEATURE = 'nlu-model-mode',
22 | DEFAULT_MODE = 'listModels';
23 |
24 | var pkg = require('../../package.json'),
25 | serviceutils = require('../../utilities/service-utils'),
26 | payloadutils = require('../../utilities/payload-utils'),
27 | sAPIKey = null,
28 | apikey = '',
29 | service = null,
30 | endpoint = '',
31 | sEndpoint = '';
32 |
33 |
34 | service = serviceutils.getServiceCreds(SERVICE_IDENTIFIER);
35 |
36 | if (service) {
37 | sAPIKey = service.api_key || service.apikey;
38 | sEndpoint = service.url;
39 | }
40 |
41 | RED.httpAdmin.get('/watson-nlu-model-manager-v4/vcap', function(req, res) {
42 | res.json(service ? {
43 | bound_service: true
44 | } : null);
45 | });
46 |
47 | function invokeMethod(node, msg) {
48 | return new Promise(function resolver(resolve, reject) {
49 | let service = node.service;
50 | let method = node.config[FEATURE];
51 | let params = node.params;
52 |
53 | service[method](params)
54 | .then((data) => {
55 | let result = data
56 | if (data && data.result) {
57 | result = data.result;
58 | }
59 | resolve(result);
60 | })
61 | .catch((err) => {
62 | reject(err);
63 | })
64 | });
65 | }
66 |
67 | function responseForDeleteMode(node, msg) {
68 | let feature = node.config[FEATURE];
69 | let field = null;
70 |
71 | switch (feature) {
72 | case 'deleteModel':
73 | msg.payload = 'Successfully deleted model: ' + node.params.modelId;
74 | break;
75 | default:
76 | return false;
77 | }
78 | return true;
79 | }
80 |
81 | function processTheResponse (body, node, msg) {
82 | return new Promise(function resolver(resolve, reject) {
83 | if (body == null) {
84 | return reject('call to watson nlu v1 service failed');
85 | } else if (! responseForDeleteMode(node, msg)) {
86 | msg.payload = body;
87 | if (body && body.models) {
88 | msg.payload = body.models;
89 | }
90 | resolve();
91 | }
92 | });
93 | }
94 |
95 | function execute(node, msg) {
96 | return new Promise(function resolver(resolve, reject) {
97 | node.status({
98 | fill: 'blue',
99 | shape: 'dot',
100 | text: 'Invoking ' + node.config[FEATURE] + ' ...'
101 | });
102 |
103 | invokeMethod(node, msg)
104 | .then((data)=> {
105 | return processTheResponse(data, node, msg);
106 | })
107 | .then(() => {
108 | resolve();
109 | })
110 | .catch((err) => {
111 | reject(err);
112 | });
113 | });
114 | }
115 |
116 | function verifyServiceCredentials(node, msg) {
117 | // If it is present the newly provided user entered key
118 | // takes precedence over the existing one.
119 | apikey = sAPIKey || node.credentials.apikey;
120 | if (!apikey) {
121 | return Promise.reject('Missing Watson NLU API service credentials');
122 | }
123 |
124 | let authSettings = {};
125 |
126 | var serviceSettings = {
127 | version: '2021-08-01',
128 | headers: {
129 | 'User-Agent': pkg.name + '-' + pkg.version
130 | }
131 | };
132 |
133 | if (endpoint) {
134 | serviceSettings.url = endpoint;
135 | }
136 |
137 | authSettings.apikey = apikey;
138 | serviceSettings.authenticator = new IamAuthenticator(authSettings);
139 |
140 | node.service = new NaturalLanguageUnderstandingV1(serviceSettings);
141 |
142 | return Promise.resolve();
143 | }
144 |
145 |
146 | function determineEndpoint(config) {
147 | endpoint = sEndpoint;
148 | if (!endpoint && config['nlu-service-endpoint']) {
149 | endpoint = config['nlu-service-endpoint'];
150 | }
151 |
152 | if (!endpoint) {
153 | return Promise.reject('No endpoint URL has been provided');
154 | }
155 | return Promise.resolve();
156 | }
157 |
158 | function paramCheckFor(requiredFields, msg){
159 | let theMissing = [];
160 |
161 | if (!msg || !msg.params) {
162 | theMissing = requiredFields;
163 | } else {
164 | requiredFields.forEach((r) => {
165 | if (! msg.params[r]) {
166 | theMissing.push(r);
167 | }
168 | })
169 | }
170 |
171 | return theMissing;
172 | }
173 |
174 |
175 | function bufferCheck(data) {
176 | return data instanceof Buffer;
177 | }
178 |
179 |
180 | function imagesExpected(feature) {
181 | switch(feature) {
182 | case 'addImages':
183 | case 'analyze':
184 | return true;
185 | default:
186 | return false;
187 | }
188 | }
189 |
190 | function processPayload(node, msg) {
191 | return new Promise(function resolver(resolve, reject) {
192 | if ('deleteModel' === node.config[FEATURE]) {
193 | node.params['modelId'] = msg.payload;
194 | }
195 | return resolve();
196 | });
197 | }
198 |
199 | function verifyPayload(node, msg) {
200 | switch (node.config[FEATURE]) {
201 | case 'listModels':
202 | return Promise.resolve();
203 | case 'deleteModel':
204 | if (!msg.payload || 'string' !== typeof msg.payload) {
205 | return Promise.reject('Missing property model identifier: msg.payload');
206 | }
207 | return Promise.resolve();
208 | default:
209 | return Promise.reject('Unknown mode has been specified');
210 | }
211 | }
212 |
213 | function verifyFeatureMode(node, msg) {
214 | let f = node.config[FEATURE];
215 | if (!f) {
216 | node.config[FEATURE] = DEFAULT_MODE;
217 | }
218 | return Promise.resolve();
219 | }
220 |
221 |
222 | // This is the processing of the On input event
223 | function processOnInput(node, msg) {
224 | return new Promise(function resolver(resolve, reject) {
225 | // Verify that a mode has been set
226 | verifyFeatureMode(node, msg)
227 | .then(() => {
228 | // Using the mode verify that the payload conforms
229 | return verifyPayload(node, msg);
230 | })
231 | .then(() => {
232 | return processPayload(node, msg);
233 | })
234 | .then(() => {
235 | return determineEndpoint(node.config);
236 | })
237 | .then(() => {
238 | return verifyServiceCredentials(node, msg);
239 | })
240 | .then(() => {
241 | return execute(node, msg);
242 | })
243 | .then(() => {
244 | resolve();
245 | })
246 | .catch((err) => {
247 | reject(err);
248 | });
249 | });
250 | }
251 |
252 | // This is the Watson NLU Model Manager Node
253 | function NLUModelManagerNode(config) {
254 | let node = this;
255 |
256 | RED.nodes.createNode(this, config);
257 | node.config = config;
258 | node.params = {};
259 |
260 | node.on('input', function(msg, send, done) {
261 | var params = {};
262 |
263 | node.status({});
264 |
265 | processOnInput(node, msg)
266 | .then(() => {
267 | node.status({});
268 | send(msg);
269 | done();
270 | })
271 | .catch((err) => {
272 | payloadutils.reportError(node, msg, err);
273 | done(err);
274 | });
275 | });
276 | }
277 |
278 |
279 | RED.nodes.registerType('natural-language-understanding-model-manager-v1', NLUModelManagerNode, {
280 | credentials: {
281 | apikey: {
282 | type: 'password'
283 | }
284 | }
285 | });
286 |
287 | };
288 |
--------------------------------------------------------------------------------
/services/natural_language_understanding/v1.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017, 2022 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the 'License');
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | module.exports = function (RED) {
18 | const SERVICE_IDENTIFIER = 'natural-language-understanding',
19 | NaturalLanguageUnderstandingV1 = require('ibm-watson/natural-language-understanding/v1'),
20 | { IamAuthenticator } = require('ibm-watson/auth');
21 |
22 | const NLU_FEATURES = {
23 | 'categories': 'categories',
24 | 'classifications': 'classifications',
25 | 'concepts': 'concepts',
26 | 'doc-emotion': 'emotion',
27 | 'doc-sentiment': 'sentiment',
28 | 'entity': 'entities',
29 | 'keyword': 'keywords',
30 | 'metadata': 'metadata',
31 | 'relation': 'relations',
32 | 'semantic': 'semantic_roles',
33 | 'syntax': 'syntax'
34 | };
35 |
36 | var pkg = require('../../package.json'),
37 | payloadutils = require('../../utilities/payload-utils'),
38 | serviceutils = require('../../utilities/service-utils'),
39 | service = serviceutils.getServiceCreds(SERVICE_IDENTIFIER),
40 | apikey = null,
41 | sApikey = null,
42 | endpoint = '',
43 | sEndpoint = 'https://gateway.watsonplatform.net/natural-language-understanding/api';
44 |
45 |
46 | function initialCheck(k) {
47 | if (!k) {
48 | return Promise.reject('Missing Watson Natural Language Understanding service credentials');
49 | }
50 | return Promise.resolve();
51 | }
52 |
53 | function payloadCheck(msg, options) {
54 | var message = '';
55 | if (!msg.payload) {
56 | message = 'Missing property: msg.payload';
57 | } else if (payloadutils.urlCheck(msg.payload)) {
58 | options['url'] = msg.payload;
59 | } else {
60 | options['text'] = msg.payload;
61 | }
62 | if (message) {
63 | return Promise.reject(message);
64 | }
65 | return Promise.resolve();
66 | }
67 |
68 |
69 | function checkAdditonalMsgOptions(msg, options) {
70 | if (msg.nlu_options && msg.nlu_options.language) {
71 | options['language'] = msg.nlu_options.language;
72 | }
73 | return Promise.resolve();
74 | }
75 |
76 | function checkNonFeatureOptions(config, options) {
77 | var limitCharacters = parseInt(config.limittextcharacters);
78 |
79 | if (! isNaN(limitCharacters) && 0 < limitCharacters) {
80 | options.limitTextCharacters = limitCharacters;
81 | }
82 |
83 | return Promise.resolve();
84 | }
85 |
86 |
87 | function checkFeatureRequest(config, options) {
88 | var message = '',
89 | enabled_features = null;
90 |
91 | enabled_features = Object.keys(NLU_FEATURES).filter(function (feature) {
92 | return config[feature];
93 | });
94 |
95 | if (!enabled_features.length) {
96 | message = 'Node must have at least one selected feature.';
97 | } else {
98 | options.features = {};
99 | for (var f in enabled_features) {
100 | options.features[NLU_FEATURES[enabled_features[f]]] = {};
101 | }
102 | }
103 | if (message) {
104 | return Promise.reject(message);
105 | }
106 | return Promise.resolve();
107 | }
108 |
109 | function processConceptsOptions(config, features) {
110 | if (features.concepts) {
111 | features.concepts.limit =
112 | config['maxconcepts'] ? parseInt(config['maxconcepts']) : 8;
113 | }
114 | }
115 |
116 |
117 |
118 | function processClassificationsOptions(msg, config, features) {
119 | if (features.classifications) {
120 | if (msg.nlu_options && msg.nlu_options.classifications_model) {
121 | features.classifications.model = msg.nlu_options.classifications_model;
122 | } else if (config['classifications-model']) {
123 | features.classifications.model = config['classifications-model'] ;
124 | }
125 | }
126 | }
127 |
128 | function processCategoriesOptions(config, features) {
129 | if (features.categories) {
130 | features.categories.limit =
131 | config['limitcategories'] ? parseInt(config['limitcategories']) : 3;
132 | }
133 | }
134 |
135 | function processEmotionOptions(config, features) {
136 | if (features.emotion && config['doc-emotion-target']) {
137 | features.emotion.targets = config['doc-emotion-target'].split(',');
138 | }
139 | }
140 |
141 | function processSentimentOptions(config, features) {
142 | if (features.sentiment && config['doc-sentiment-target']) {
143 | features.sentiment.targets = config['doc-sentiment-target'].split(',');
144 | }
145 | }
146 |
147 | function processEntitiesOptions(msg, config, features) {
148 | if (features.entities) {
149 | features.entities.emotion =
150 | config['entity-emotion'] ? config['entity-emotion'] : false;
151 | features.entities.sentiment =
152 | config['entity-sentiment'] ? config['entity-sentiment'] : false;
153 | if (config['maxentities']) {
154 | features.entities.limit = parseInt(config['maxentities']);
155 | }
156 | if (msg.nlu_options && msg.nlu_options.entity_model) {
157 | features.entities.model = msg.nlu_options.entity_model;
158 | }
159 | }
160 | }
161 |
162 | function processSyntaxOptions(msg, config, features) {
163 | if (features.syntax) {
164 | features.syntax.sentences =
165 | config['syntax-sentences'] ? config['syntax-sentences'] : false;
166 | if (config['syntax-tokens-lemma'] || config['syntax-tokens-pos']) {
167 | features.syntax.tokens = {};
168 | features.syntax.tokens.lemma =
169 | config['syntax-tokens-lemma'] ? config['syntax-tokens-lemma'] : false;
170 | features.syntax.tokens.part_of_speech =
171 | config['syntax-tokens-pos'] ? config['syntax-tokens-pos'] : false;
172 | }
173 | }
174 | }
175 |
176 | function processRelationsOptions(msg, config, features) {
177 | if (features.relations) {
178 | if (msg.nlu_options && msg.nlu_options.relations_model) {
179 | features.relations.model = msg.nlu_options.relations_model;
180 | }
181 | }
182 | }
183 |
184 | function processKeywordsOptions(config, features) {
185 | if (features.keywords) {
186 | features.keywords.emotion =
187 | config['keyword-emotion'] ? config['keyword-emotion'] : false;
188 | features.keywords.sentiment =
189 | config['keyword-sentiment'] ? config['keyword-sentiment'] : false;
190 | if (config['maxkeywords']) {
191 | features.keywords.limit = parseInt(config['maxkeywords']);
192 | }
193 | }
194 | }
195 |
196 | function processSemanticRolesOptions(config, features) {
197 | if (features.semantic_roles) {
198 | features.semantic_roles.entities =
199 | config['semantic-entities'] ? config['semantic-entities'] : false;
200 | features.semantic_roles.keywords =
201 | config['semantic-keywords'] ? config['semantic-keywords'] : false;
202 | if (config['maxsemantics']) {
203 | features.semantic_roles.limit = parseInt(config['maxsemantics']);
204 | }
205 | }
206 | }
207 |
208 | function checkFeatureOptions(msg, config, options) {
209 | if (options && options.features) {
210 | processConceptsOptions(config, options.features);
211 | processClassificationsOptions(msg, config, options.features);
212 | processCategoriesOptions(config, options.features);
213 | processEmotionOptions(config, options.features);
214 | processSentimentOptions(config, options.features);
215 | processEntitiesOptions(msg, config, options.features);
216 | processRelationsOptions(msg, config, options.features);
217 | processKeywordsOptions(config, options.features);
218 | processSemanticRolesOptions(config, options.features);
219 | processSyntaxOptions(msg, config, options.features);
220 | }
221 | return Promise.resolve();
222 | }
223 |
224 | function invokeService(options) {
225 | let nlu = null,
226 | authSettings = {};
227 | serviceSettings = {
228 | version: '2021-08-01',
229 | headers: {
230 | 'User-Agent': pkg.name + '-' + pkg.version
231 | }
232 | };
233 |
234 | if (apikey) {
235 | authSettings.apikey = apikey;
236 | }
237 | serviceSettings.authenticator = new IamAuthenticator(authSettings);
238 |
239 | if (endpoint) {
240 | serviceSettings.url = endpoint;
241 | }
242 |
243 | nlu = new NaturalLanguageUnderstandingV1(serviceSettings);
244 |
245 | var p = new Promise(function resolver(resolve, reject) {
246 | nlu.analyze(options)
247 | .then((response) => {
248 | resolve(response);
249 | })
250 | .catch((err) => {
251 | reject(err);
252 | });
253 | });
254 | return p;
255 | }
256 |
257 | if (service) {
258 | sApikey = service.apikey ? service.apikey : '';
259 | sEndpoint = service.url;
260 | }
261 |
262 | RED.httpAdmin.get('/natural-language-understanding/vcap', function (req, res) {
263 | res.json(service ? {bound_service: true} : null);
264 | });
265 |
266 |
267 | // This is the Natural Language Understanding Node
268 |
269 | function NLUNode (config) {
270 | RED.nodes.createNode(this, config);
271 | var node = this;
272 |
273 | this.on('input', function(msg, send, done) {
274 | var message = '',
275 | options = {};
276 |
277 | node.status({});
278 |
279 | apikey = sApikey || this.credentials.apikey;
280 |
281 | endpoint = sEndpoint;
282 | if (config['service-endpoint']) {
283 | endpoint = config['service-endpoint'];
284 | }
285 |
286 | initialCheck(apikey)
287 | .then(function(){
288 | return payloadCheck(msg, options);
289 | })
290 | .then(function(){
291 | return checkAdditonalMsgOptions(msg, options);
292 | })
293 | .then(function(){
294 | return checkFeatureRequest(config, options);
295 | })
296 | .then(function(){
297 | return checkFeatureOptions(msg, config, options);
298 | })
299 | .then(function(){
300 | return checkNonFeatureOptions(config, options);
301 | })
302 | .then(function(){
303 | node.status({fill:'blue', shape:'dot', text:'requesting'});
304 | return invokeService(options);
305 | })
306 | .then(function(data){
307 | msg.features = data;
308 | if (data && data.result) {
309 | msg.features = data.result;
310 | }
311 | send(msg);
312 | node.status({});
313 | done();
314 | })
315 | .catch(function(err){
316 | let errMsg = payloadutils.reportError(node, msg, err);
317 | done(errMsg);
318 | });
319 |
320 | });
321 | }
322 |
323 | //Register the node as natural-language-understanding to nodeRED
324 | RED.nodes.registerType('natural-language-understanding', NLUNode, {
325 | credentials: {
326 | apikey: {type: 'password'}
327 | }
328 | });
329 | };
330 |
--------------------------------------------------------------------------------
/services/speech_to_text/icons/speech_to_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/watson-developer-cloud/node-red-node-watson/dc29f8dd09bdbfd669c5560ed0e0b8eccb4e149f/services/speech_to_text/icons/speech_to_text.png
--------------------------------------------------------------------------------
/services/speech_to_text/stt-utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, 2022 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | const pkg = require('../../package.json'),
18 | STTV1 = require('ibm-watson/speech-to-text/v1'),
19 | { IamAuthenticator } = require('ibm-watson/auth');
20 |
21 | class STTUtils {
22 | constructor() {
23 | }
24 |
25 | static initSTTService(req, sApikey, sEndpoint) {
26 | const endpoint = req.query.e ? req.query.e : sEndpoint;
27 |
28 | let authSettings = {};
29 | let serviceSettings = {
30 | url: endpoint,
31 | headers: {
32 | 'User-Agent': pkg.name + '-' + pkg.version
33 | }
34 | };
35 |
36 | if (sApikey || req.query.key) {
37 | authSettings.apikey = sApikey ? sApikey : req.query.key;
38 | }
39 | serviceSettings.authenticator = new IamAuthenticator(authSettings);
40 |
41 | return new STTV1(serviceSettings);
42 | }
43 |
44 | static determineService(apikey, endpoint) {
45 | let authSettings = {};
46 | let serviceSettings = {
47 | headers: {
48 | 'User-Agent': pkg.name + '-' + pkg.version
49 | }
50 | };
51 |
52 | if (apikey) {
53 | authSettings.apikey = apikey;
54 | }
55 |
56 | if (endpoint) {
57 | serviceSettings.url = endpoint;
58 | }
59 | serviceSettings.authenticator = new IamAuthenticator(authSettings);
60 |
61 | return new STTV1(serviceSettings);
62 | }
63 |
64 | static determineServiceFromToken(accessToken, endpoint) {
65 | let authSettings = {};
66 | let serviceSettings = {
67 | headers: {
68 | 'User-Agent': pkg.name + '-' + pkg.version
69 | }
70 | };
71 |
72 | authSettings.accessToken = accessToken;
73 |
74 | if (endpoint) {
75 | serviceSettings.url = endpoint;
76 | }
77 |
78 | serviceSettings.authenticator = new IamAuthenticator(authSettings);
79 |
80 | return new STTV1(serviceSettings);
81 | }
82 |
83 | }
84 |
85 | module.exports = STTUtils;
86 |
--------------------------------------------------------------------------------
/services/text_to_speech/icons/text_to_speech.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/watson-developer-cloud/node-red-node-watson/dc29f8dd09bdbfd669c5560ed0e0b8eccb4e149f/services/text_to_speech/icons/text_to_speech.png
--------------------------------------------------------------------------------
/services/text_to_speech/tts-utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2018, 2022 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | const pkg = require('../../package.json'),
18 | TextToSpeechV1 = require('ibm-watson/text-to-speech/v1'),
19 | { IamAuthenticator } = require('ibm-watson/auth');
20 |
21 | class TTSUtils {
22 | constructor() {
23 | }
24 |
25 | static buildStdSettings (apikey, endpoint) {
26 | let authSettings = {};
27 | let serviceSettings = {
28 | headers: {
29 | 'User-Agent': pkg.name + '-' + pkg.version
30 | }
31 | };
32 |
33 | if (apikey) {
34 | authSettings.apikey = apikey;
35 | }
36 |
37 | serviceSettings.authenticator = new IamAuthenticator(authSettings);
38 |
39 | if (endpoint) {
40 | serviceSettings.url = endpoint;
41 | }
42 |
43 | return new TextToSpeechV1(serviceSettings);
44 | }
45 |
46 | static initTTSService(req, sApikey, sEndpoint) {
47 | const endpoint = req.query.e ? req.query.e : sEndpoint;
48 |
49 | let authSettings = {};
50 | let serviceSettings = {
51 | url: endpoint,
52 | headers: {
53 | 'User-Agent': pkg.name + '-' + pkg.version
54 | }};
55 |
56 | if (sApikey || req.query.key) {
57 | authSettings.apikey = sApikey ? sApikey : req.query.key;
58 | }
59 |
60 | serviceSettings.authenticator = new IamAuthenticator(authSettings);
61 |
62 | return new TextToSpeechV1(serviceSettings);
63 | }
64 |
65 | }
66 |
67 | module.exports = TTSUtils;
68 |
--------------------------------------------------------------------------------
/services/text_to_speech/v1.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015, 2022 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | module.exports = function(RED) {
18 | const SERVICE_IDENTIFIER = 'text-to-speech';
19 |
20 | var pkg = require('../../package.json'),
21 | serviceutils = require('../../utilities/service-utils'),
22 | payloadutils = require('../../utilities/payload-utils'),
23 | ttsutils = require('./tts-utils'),
24 | endpoint = '',
25 | sEndpoint = 'https://stream.watsonplatform.net/text-to-speech/api',
26 | apikey = '', sApikey = '';
27 |
28 |
29 | var service = serviceutils.getServiceCreds(SERVICE_IDENTIFIER);
30 |
31 | if (service) {
32 | sApikey = service.apikey ? service.apikey : '';
33 | sEndpoint = service.url;
34 | }
35 |
36 | // Node RED Admin - fetch and set vcap services
37 | RED.httpAdmin.get('/watson-text-to-speech/vcap', function(req, res) {
38 | res.json(service ? {bound_service: true} : null);
39 | });
40 |
41 |
42 | // API used by widget to fetch available models
43 | RED.httpAdmin.get('/watson-text-to-speech/voices', function (req, res) {
44 | var tts = ttsutils.initTTSService(req, sApikey, sEndpoint);
45 |
46 | tts.listVoices({})
47 | .then((response) => {
48 | let voices = response;
49 | if (response.result) {
50 | voices = response.result;
51 | }
52 | res.json(voices);
53 | })
54 | .catch((err) => {
55 | if (!err.error) {
56 | err.error = 'Error ' + err.code + ' in fetching voices';
57 | }
58 | res.json(err);
59 | });
60 | });
61 |
62 | // API used by widget to fetch available customisations
63 | RED.httpAdmin.get('/watson-text-to-speech/customs', function (req, res) {
64 | var tts = ttsutils.initTTSService(req, sApikey, sEndpoint);
65 |
66 | tts.listCustomModels({})
67 | .then((response) => {
68 | let customs = response;
69 | if (response.result) {
70 | customs = response.result;
71 | }
72 | res.json(customs);
73 | })
74 | .catch((err) => {
75 | res.json(err);
76 | });
77 | });
78 |
79 | function Node(config) {
80 | RED.nodes.createNode(this, config);
81 | var node = this;
82 |
83 | function initialCheck(apikey) {
84 | if (!apikey) {
85 | return Promise.reject('Missing Text To Speech service credentials');
86 | }
87 | return Promise.resolve();
88 | }
89 |
90 | function payloadCheck(msg) {
91 | if (!msg.payload) {
92 | return Promise.reject('Missing property: msg.payload');
93 | }
94 | return Promise.resolve();
95 | }
96 |
97 | function buildParams(msg) {
98 | var params = {
99 | text: msg.payload,
100 | voice: msg.voice || config.voice,
101 | accept: config.format
102 | };
103 |
104 | // Check the params for customisation options
105 | if (config.langcustom && 'NoCustomisationSetting' !== config.langcustom) {
106 | params.customizationId = config.langcustom;
107 | }
108 | return Promise.resolve(params);
109 | }
110 |
111 | function performTTS(msg, params) {
112 | var p = new Promise(function resolver(resolve, reject) {
113 | let tts = ttsutils.buildStdSettings(apikey, endpoint);
114 |
115 | tts.synthesize(params)
116 | .then((body) => {
117 | resolve(body);
118 | })
119 | .catch((err) => {
120 | reject(err);
121 | });
122 |
123 | });
124 | return p;
125 | }
126 |
127 | function processResponse(msg, data) {
128 | return new Promise(function resolver(resolve, reject) {
129 | let body = data
130 | if (data && data.result) {
131 | body = data.result;
132 | }
133 |
134 | let tmpHolder = msg.payload;
135 | msg.payload = body;
136 |
137 | payloadutils.checkForStream(msg)
138 | .then(() => {
139 | if (! config['payload-response']) {
140 | msg.speech = msg.payload;
141 | msg.payload = tmpHolder;
142 | }
143 | resolve();
144 | })
145 | .catch((err) => {
146 | reject(err);
147 | });
148 | });
149 | }
150 |
151 | this.on('input', function(msg, send, done) {
152 | apikey = sApikey || this.credentials.apikey || config.apikey;
153 |
154 | endpoint = sEndpoint;
155 | if (config['service-endpoint']) {
156 | endpoint = config['service-endpoint'];
157 | }
158 |
159 | node.status({});
160 |
161 | initialCheck(apikey)
162 | .then(function(){
163 | return payloadCheck(msg);
164 | })
165 | .then(function(){
166 | return buildParams(msg);
167 | })
168 | .then(function(params){
169 | node.status({fill:"blue", shape:"dot", text:"requesting"});
170 | return performTTS(msg, params);
171 | })
172 | .then(function(body){
173 | return processResponse(msg, body);
174 | })
175 | .then(function(){
176 | node.status({});
177 | send(msg);
178 | done();
179 | })
180 | .catch(function(err){
181 | payloadutils.reportError(node,msg,err);
182 | done(err);
183 | });
184 | })
185 | }
186 | RED.nodes.registerType('watson-text-to-speech', Node, {
187 | credentials: {
188 | apikey: {type:'password'}
189 | }
190 | });
191 | };
192 |
--------------------------------------------------------------------------------
/services/tone_analyzer/icons/tone_analyzer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/watson-developer-cloud/node-red-node-watson/dc29f8dd09bdbfd669c5560ed0e0b8eccb4e149f/services/tone_analyzer/icons/tone_analyzer.png
--------------------------------------------------------------------------------
/services/tone_analyzer/tone_analyser_sample.txt:
--------------------------------------------------------------------------------
1 | Hi Team, I know the times are difficult! Our sales have been disappointing for the past three quarters for our data analytics product suite. We have a competitive data analytics product suite in the industry. But we need to do our job selling it!
2 |
--------------------------------------------------------------------------------
/services/tone_analyzer/v3.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
87 |
88 |
117 |
118 |
237 |
--------------------------------------------------------------------------------
/services/tone_analyzer/v3.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2013,2022 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | module.exports = function (RED) {
18 | const SERVICE_IDENTIFIER = 'tone-analyzer',
19 | ToneAnalyzerV3 = require('ibm-watson/tone-analyzer/v3'),
20 | { IamAuthenticator } = require('ibm-watson/auth');
21 |
22 | var pkg = require('../../package.json'),
23 | serviceutils = require('../../utilities/service-utils'),
24 | payloadutils = require('../../utilities/payload-utils'),
25 | toneutils = require('../../utilities/tone-utils'),
26 | apikey = '', sApikey = '',
27 | endpoint = '',
28 | sEndpoint = 'https://gateway.watsonplatform.net/tone-analyzer/api',
29 | service = null;
30 |
31 | service = serviceutils.getServiceCreds(SERVICE_IDENTIFIER);
32 |
33 | if (service) {
34 | sApikey = service.apikey ? service.apikey : '';
35 | sEndpoint = service.url;
36 | }
37 |
38 | // Node RED Admin - fetch and set vcap services
39 | RED.httpAdmin.get('/watson-tone-analyzer/vcap', function (req, res) {
40 | res.json(service ? {bound_service: true} : null);
41 | });
42 |
43 |
44 | // Check that the credentials have been provided
45 | // Credentials are needed for each the service.
46 | var checkCreds = function(credentials) {
47 | var taSettings = {};
48 |
49 | apikey = sApikey || credentials.apikey;
50 |
51 | if (apikey) {
52 | taSettings.iam_apikey = apikey;
53 | } else {
54 | taSettings = null;
55 | }
56 |
57 | return taSettings;
58 | }
59 |
60 |
61 | // Function that checks the configuration to make sure that credentials,
62 | // payload and options have been provied in the correct format.
63 | var checkConfiguration = function(msg, node) {
64 | var message = null,
65 | taSettings = null;
66 |
67 | taSettings = checkCreds(node.credentials);
68 |
69 | if (!taSettings) {
70 | message = 'Missing Tone Analyzer service credentials';
71 | } else if (msg.payload) {
72 | message = toneutils.checkPayload(msg.payload);
73 | } else {
74 | message = 'Missing property: msg.payload';
75 | }
76 |
77 | if (message) {
78 | return Promise.reject(message);
79 | } else {
80 | return Promise.resolve(taSettings);
81 | }
82 | };
83 |
84 |
85 | function invokeService(config, options, settings) {
86 | let authSettings = {};
87 |
88 | let serviceSettings = {
89 | version: '2017-09-21',
90 | headers: {
91 | 'User-Agent': pkg.name + '-' + pkg.version
92 | }
93 | };
94 |
95 | if (settings.iam_apikey) {
96 | authSettings.apikey = settings.iam_apikey;
97 | }
98 |
99 | serviceSettings.authenticator = new IamAuthenticator(authSettings);
100 |
101 | endpoint = sEndpoint;
102 | if (config['service-endpoint']) {
103 | endpoint = config['service-endpoint'];
104 | }
105 |
106 | if (endpoint) {
107 | serviceSettings.url = endpoint;
108 | }
109 |
110 | if (config['interface-version']) {
111 | serviceSettings.version = config['interface-version'];
112 | }
113 |
114 | const tone_analyzer = new ToneAnalyzerV3(serviceSettings);
115 |
116 | var p = new Promise(function resolver(resolve, reject){
117 | var m = 'tone';
118 | switch (config['tone-method']) {
119 | case 'generalTone' :
120 | break;
121 | case 'customerEngagementTone' :
122 | m = 'toneChat';
123 | break;
124 | }
125 |
126 | tone_analyzer[m](options)
127 | .then((response) => {
128 | resolve(response);
129 | })
130 | .catch((err) => {
131 | reject(err);
132 | })
133 | });
134 |
135 | return p;
136 | }
137 |
138 | // function when the node recieves input inside a flow.
139 | // Configuration is first checked before the service is invoked.
140 | var processOnInput = function(msg, send, done, config, node) {
141 | checkConfiguration(msg, node)
142 | .then(function(settings) {
143 | var options = toneutils.parseOptions(msg, config);
144 | options = toneutils.parseLanguage(msg, config, options);
145 | node.status({fill:'blue', shape:'dot', text:'requesting'});
146 | return invokeService(config, options, settings);
147 | })
148 | .then(function(data){
149 | node.status({})
150 | if (data && data.result) {
151 | msg.response = data.result;
152 | } else {
153 | msg.response = data;
154 | }
155 | send(msg);
156 | node.status({});
157 | done();
158 | })
159 | .catch(function(err){
160 | payloadutils.reportError(node,msg,err);
161 | send(msg);
162 | done(err);
163 | });
164 | }
165 |
166 |
167 | // This is the Tone Analyzer Node.
168 | function Node (config) {
169 | RED.nodes.createNode(this, config);
170 | var node = this;
171 |
172 | // Invoked when the node has received an input as part of a flow.
173 | this.on('input', function(msg, send, done) {
174 | processOnInput(msg, send, done, config, node);
175 | });
176 | }
177 |
178 | RED.nodes.registerType('watson-tone-analyzer-v3', Node, {
179 | credentials: {
180 | apikey: {type:'password'}
181 | }
182 | });
183 | };
184 |
--------------------------------------------------------------------------------
/utilities/payload-utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 | var url = require('url'),
17 | fs = require('fs'),
18 | fileType = require('file-type'),
19 | request = require('request'),
20 | path = require('path'),
21 | toArray = require('stream-to-array'),
22 | stream = require('stream');
23 |
24 | function PayloadUtils() {}
25 |
26 | PayloadUtils.prototype = {
27 | check: function() {
28 | return 'IBM Watson Node-RED Utilities for Payload Handling';
29 | },
30 |
31 | // Function that checks if the input string is a url
32 | urlCheck: function(str) {
33 | var parsed = url.parse(str);
34 | return (!!parsed.hostname && !!parsed.protocol && str.indexOf(' ') < 0);
35 | },
36 |
37 | // Function that checks if the input is a valid json object
38 | isJsonObject: function (payload) {
39 | if (payload instanceof Array || payload instanceof Object ||
40 | 'object' === typeof payload || Array.isArray(payload)) {
41 | return true;
42 | }
43 | return false;
44 | },
45 |
46 | // Function that is syncing up the asynchronous nature of the stream
47 | // so that the full file can be sent to the API.
48 | stream_buffer: function(file, contents, cb) {
49 | fs.writeFile(file, contents, function(err) {
50 | if (err) {
51 | throw err;
52 | }
53 | var ft = fileType(contents);
54 | cb(ft ? ft.ext : 'tmp');
55 | });
56 | },
57 |
58 | // Function that is syncing up the asynchronous nature of the stream
59 | // so that the full file can be sent to the API.
60 | stream_url: function(file, url, cb) {
61 | var wstream = fs.createWriteStream(file);
62 |
63 | wstream.on('finish', function() {
64 | fs.readFile(file, function(err, buf) {
65 | var fmt = null,
66 | error = null;
67 |
68 | if (err) {
69 | error = err;
70 | }
71 | if (fileType(buf)) {
72 | fmt = fileType(buf).ext;
73 | } else {
74 | error = 'Unrecognised file format';
75 | }
76 | cb(error, fmt);
77 | });
78 | });
79 | request(url).pipe(wstream);
80 | },
81 |
82 | // Function that matches the buffer streaming so that they can be used together.
83 | stream_url_format: function(file, url, cb) {
84 | var wstream = fs.createWriteStream(file);
85 |
86 | wstream.on('finish', function() {
87 | fs.readFile(file, function(err, buf) {
88 | var fmt = null,
89 | error = null;
90 |
91 | if (err) {
92 | throw err;
93 | }
94 | if (fileType(buf)) {
95 | fmt = fileType(buf).ext;
96 | }
97 | cb(fmt);
98 | });
99 | });
100 | request(url).pipe(wstream);
101 | },
102 |
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 | runThroughErrOptions: function(err) {
128 | let messageTxt = err;
129 | if (err.error) {
130 | messageTxt = err.error;
131 | } else if (err.description) {
132 | messageTxt = err.description;
133 | } else if (err.message) {
134 | messageTxt = err.message;
135 | }
136 | return messageTxt;
137 | },
138 |
139 | reportError: function (node, msg, err) {
140 | let messageTxt = err;
141 |
142 | if (err.body && 'string' === typeof err.body) {
143 | messageTxt = err.body;
144 | try {
145 | let errBody = JSON.parse(err.body);
146 | messageTxt = PayloadUtils.prototype.runThroughErrOptions(errBody);
147 | } catch(e) {}
148 | } else {
149 | messageTxt = PayloadUtils.prototype.runThroughErrOptions(err);
150 | }
151 |
152 | msg.watsonerror = messageTxt;
153 | node.status({fill:'red', shape:'dot', text: messageTxt});
154 | node.error(messageTxt, msg);
155 | return messageTxt;
156 | },
157 |
158 | isReadableStream : function (obj) {
159 | return obj instanceof stream.Stream &&
160 | typeof (obj._read === 'function') &&
161 | typeof (obj._readableState === 'object');
162 | },
163 |
164 | langTransToSTTFormat : function (code) {
165 | switch (code) {
166 | case 'pt':
167 | code = 'pt-BR';
168 | break;
169 | case 'de':
170 | code = 'de-DE';
171 | break;
172 | case 'ko':
173 | code = 'ko-KR';
174 | break;
175 | case 'fr':
176 | code = 'fr-FR';
177 | break;
178 | case 'en':
179 | code = 'en-US';
180 | break;
181 | case 'ja':
182 | code = 'ja-JP';
183 | break;
184 | case 'es':
185 | code = 'es-ES';
186 | break;
187 | case 'ar':
188 | code = 'ar-AR';
189 | break;
190 | case 'zh':
191 | code = 'zh-CN';
192 | break;
193 | }
194 | return code;
195 | },
196 |
197 | };
198 |
199 | var payloadutils = new PayloadUtils();
200 |
201 | module.exports = payloadutils;
202 |
--------------------------------------------------------------------------------
/utilities/response-utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2020 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | // We are only looking at the token expiry, then refreshing that if
18 | // needed. An alternative would be to refresh the token, before expiry
19 | // using the refresh token, and checking if the refresh token has expired,
20 | // but the token time has proven to be sufficient so far. If not will need
21 | // to make the change to add refresh token processing.
22 |
23 | class ResponseUtils {
24 |
25 | constructor() {
26 | }
27 |
28 | static parseResponseFor(msg, response, field) {
29 | if (response && response.result) {
30 | if (response.result[field]) {
31 | msg[field] = response.result[field];
32 | } else {
33 | msg[field] = response.result;
34 | }
35 | } else {
36 | msg[field] = response;
37 | }
38 | }
39 |
40 | static assignResultToField(msg, response, field) {
41 | if (response && response.result) {
42 | msg[field] = response.result;
43 | } else {
44 | msg[field] = response;
45 | }
46 | }
47 |
48 |
49 | }
50 |
51 | module.exports = ResponseUtils;
52 |
--------------------------------------------------------------------------------
/utilities/service-utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 | var cfenv = require('cfenv');
17 | var appEnv = cfenv.getAppEnv();
18 |
19 | function ServiceUtils() {}
20 | ServiceUtils.prototype = {
21 | //function to determine if WDC service is bound. A simple check on
22 | // name may fail because of duplicate usage. This function verifies
23 | // that the url associated with the service, contains the matched
24 | // input value, hence reducing the chances of a false match.
25 | checkCFForService: function(serviceName, returnBoolean) {
26 | var regex = RegExp('(http|https)(:\/\/)([^\/]+).*('+serviceName+').*');
27 |
28 | var services = appEnv.getServices();
29 |
30 | for (var service in services) {
31 | let section = services[service];
32 | if (Array.isArray(section)) {
33 | section = section[0];
34 | }
35 |
36 | if (section.hasOwnProperty('credentials')) {
37 | if (section.credentials.hasOwnProperty('url')){
38 | if (section.credentials.url.search(regex) === 0){
39 | return returnBoolean ? true : section.credentials;
40 | }
41 | }
42 | }
43 | }
44 | return returnBoolean ? false : null;
45 | },
46 |
47 | // Function to return all the details of a service. Used by Document
48 | // Conversion Node to provide a list and a choice to the user.
49 | // Node: Like the original Document Conversion check, this
50 | // function will look for all bound instances of Document Conversion.
51 | getAllServiceDetails: function(serviceName) {
52 | var regex = RegExp('(http|https)(://)([^\/]+)(/)('+serviceName+').*'),
53 | services = appEnv.getServices(),
54 | theList = [];
55 |
56 | for (var service in services) {
57 | if (services[service].hasOwnProperty('credentials')) {
58 | if (services[service].credentials.hasOwnProperty('url')) {
59 | if (services[service].credentials.url.search(regex) === 0) {
60 | var newCandidate = {};
61 | var v = services[service];
62 | newCandidate.name = v.name ? v.name : '';
63 | newCandidate.label = v.label ? v.label : '';
64 | newCandidate.url = v.credentials.url ?
65 | v.credentials.url : '';
66 | newCandidate.username = v.credentials.username ?
67 | v.credentials.username : '';
68 | newCandidate.password = v.credentials.password ?
69 | v.credentials.password : '';
70 | theList = theList.concat(newCandidate);
71 | }
72 | }
73 | }
74 | }
75 | return theList;
76 | },
77 |
78 | // Check for service return a boolean to indicate if it is bound in
79 | checkServiceBound: function(serviceName) {
80 | return ServiceUtils.prototype.checkCFForService(serviceName, true);
81 | },
82 |
83 | // Check for and return bound servie
84 | getServiceCreds: function(serviceName) {
85 | return ServiceUtils.prototype.checkCFForService(serviceName, false);
86 | },
87 |
88 | getServiceCredsAlchemy: function(serviceName) {
89 | return ServiceUtils.prototype.checkCFForService(serviceName, false);
90 | },
91 |
92 | };
93 |
94 | var serviceutils = new ServiceUtils();
95 |
96 | module.exports = serviceutils;
97 |
--------------------------------------------------------------------------------
/utilities/tone-utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2016 IBM Corp.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | **/
16 |
17 | function ToneUtils () {
18 | }
19 |
20 | ToneUtils.prototype = {
21 | check: function () {
22 | return '"IBM Watson Node-RED Utilities for Tone Analyser';
23 | },
24 |
25 | isJsonString: function(str) {
26 | try {
27 | JSON.parse(str);
28 | } catch (e) {
29 | return false;
30 | }
31 | return true;
32 | },
33 |
34 | isJsonObject: function(str) {
35 | if (str instanceof Array || str instanceof Object ||
36 | Array.isArray(str) || 'object' === typeof str) {
37 | return true;
38 | }
39 | return false;
40 | },
41 |
42 | // Function that checks the payload and determines
43 | // whether it is JSON or a Buffer
44 | checkPayload: function(payload) {
45 | var message = null,
46 | isJSON = this.isJsonString(payload) || this.isJsonObject(payload);
47 |
48 | // Payload (text to be analysed) must be a string (content is either raw string or Buffer)
49 | if (typeof payload !== 'string' && isJSON !== true) {
50 | message = 'The payload must be either a string, JSON or a Buffer';
51 | }
52 |
53 | return message;
54 | },
55 |
56 | // Function to parse and determine tone setting
57 | // 'all' is the setting which needs be be blanked
58 | // if not the service will throw an error
59 | parseToneOption: function(msg, config) {
60 | var tones = msg.tones || config.tones;
61 |
62 | return (tones === 'all' ? '' : tones);
63 | },
64 |
65 | // function to parse through the options in preparation
66 | // for the sevice call.
67 | parseOptions: function(msg, config) {
68 | var options = {};
69 |
70 | if (!config['tone-method']) {
71 | config['tone-method'] = 'generalTone';
72 | }
73 |
74 | switch (config['tone-method']) {
75 | case 'generalTone' :
76 | let isHTML = msg.contentType || config.contentType;
77 | options.sentences = msg.sentences || config.sentences;
78 | options.contentType = isHTML ? 'text/html' : 'text/plain';
79 | options.tones = this.parseToneOption(msg, config);
80 | options.toneInput = this.isJsonObject(msg.payload) ?
81 | JSON.stringify(msg.payload) :
82 | msg.payload;
83 | break;
84 | case 'customerEngagementTone' :
85 | options.utterances = this.isJsonString(msg.payload) ?
86 | JSON.parse(msg.payload) :
87 | msg.payload;
88 | break;
89 | }
90 |
91 | return options;
92 | },
93 |
94 | // function to splice in language options into header
95 | parseLanguage: function(msg, config, options) {
96 | var inputlang = config.inputlang ? config.inputlang : 'en';
97 | //outputlang = config.outputlang ? config.outputlang : 'en';
98 |
99 | // The SDK is currently ignoring this, but this is how it should be done.
100 | // If you need it, then Personality Insights as full set of accept-language
101 | //options.headers = {
102 | // 'content-language': inputlang,
103 | // 'accept-language': outputlang
104 | //}
105 |
106 | // This is how it is currently working.
107 | options.language = inputlang;
108 | return options;
109 | }
110 |
111 | };
112 |
113 | var toneutils = new ToneUtils();
114 |
115 | module.exports = toneutils;
116 |
--------------------------------------------------------------------------------