Skip to content

Commit 3fa522c

Browse files
authored
refactor: change all websocket method parameters to lower camel case (#941)
* add types for WebSocket Stream class constructor options * warn user when a parameter is passed in that matches the service option but not the user option BREAKING CHANGE: All parameters have been converted to their lower camel case version. BREAKING CHANGE: Support for the `token` parameter has been removed BREAKING CHANGE: Support for the `customization_id` parameter has been removed BREAKING CHANGE: Method `setAuthorizationHeaderToken` has been removed from the WebSocket Stream classes
1 parent de9fe5a commit 3fa522c

File tree

10 files changed

+522
-262
lines changed

10 files changed

+522
-262
lines changed

UPGRADE-5.0.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,12 @@ _Note: If migrating from a version less than 4.0, also see the [v4 migration gui
88
## Breaking changes
99
### Support for Node v6 and v8 dropped
1010
The SDK no longer supports Node versions 6 and 8, as reflected in the `engines` property in the package.json file. Version 6 reached end of life in April 2019 and Version 8 reaches end of life on 31 December 2019.
11+
12+
### WebSocket Methods
13+
- All parameters are now lower camel case
14+
- Support for the `token` parameter has been removed
15+
- Support for the `customization_id` parameter has been removed
16+
- Method `setAuthorizationHeaderToken` has been removed from the WebSocket Stream classes. It now exists as a shared function called `setAuthorizationHeader` in `lib/websocket-utils.ts`.
17+
18+
#### RecognizeStream
19+
- `RecognizeStream.readableObjectMode` will always be a Boolean value - before, it could have been `undefined`. This is to align with the new convention in Node 12.

lib/recognize-stream.ts

Lines changed: 126 additions & 167 deletions
Large diffs are not rendered by default.

lib/synthesize-stream.ts

Lines changed: 66 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* (C) Copyright IBM Corp. 2014, 2019.
2+
* (C) Copyright IBM Corp. 2018, 2019.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -14,28 +14,38 @@
1414
* limitations under the License
1515
*/
1616

17-
import extend = require('extend');
17+
import { OutgoingHttpHeaders } from 'http';
1818
import { qs } from 'ibm-cloud-sdk-core';
1919
import pick = require('object.pick');
20-
import { Readable } from 'stream';
21-
import websocket = require ('websocket');
22-
23-
const w3cWebSocket = websocket.w3cwebsocket;
24-
25-
const PAYLOAD_PARAMS_ALLOWED = [
26-
'text',
27-
'accept',
28-
'timings'
29-
];
30-
31-
const QUERY_PARAMS_ALLOWED = [
32-
'watson-token',
33-
'voice',
34-
'customization_id',
35-
'x-watson-learning-opt-out',
36-
'x-watson-metadata',
37-
'access_token'
38-
];
20+
import { Readable, ReadableOptions } from 'stream';
21+
import { w3cwebsocket as w3cWebSocket } from 'websocket';
22+
import { processUserParameters, setAuthorizationHeader } from './websocket-utils';
23+
24+
// these options represent the superset of the base params,
25+
// query params, and opening message params, with the keys
26+
// in lowerCamelCase format so we can expose a consistent style
27+
// to the user. this object should be updated any time either
28+
// payloadParamsAllowed or queryParamsAllowed is changed
29+
interface Options extends ReadableOptions {
30+
/* base options */
31+
url?: string;
32+
headers?: OutgoingHttpHeaders;
33+
tokenManager?: any;
34+
rejectUnauthorized?: boolean;
35+
36+
/* payload options */
37+
text: string;
38+
accept: string;
39+
timings?: string[];
40+
41+
/* query params */
42+
accessToken?: string;
43+
watsonToken?: string;
44+
voice?: string;
45+
customizationId?: string;
46+
xWatsonLearningOptOut?: boolean;
47+
xWatsonMetadata?: string;
48+
}
3949

4050
interface SynthesizeStream extends Readable {
4151
_readableState;
@@ -54,7 +64,7 @@ class SynthesizeStream extends Readable {
5464

5565
static WEBSOCKET_CONNECTION_ERROR: string = 'WebSocket connection error';
5666

57-
private options;
67+
private options: Options;
5868
private socket;
5969
private initialized: boolean;
6070

@@ -67,24 +77,23 @@ class SynthesizeStream extends Readable {
6777
*
6878
* Note that the WebSocket connection is not established until the first chunk of data is recieved. This allows for IAM token request management by the SDK.
6979
*
70-
* @param {Object} options
71-
* @param {String} options.text - The text that us to be synthesized. Provide plain text or text that is annotated with SSML. SSML input can include the SSML <mark> element. Pass a maximum of 5 KB of text.
72-
* @param {String} options.accept - The requested audio format (MIME type) of the audio.
73-
* @param {String[]} [options.timings] - An array that specifies whether the service is to return word timing information for all strings of the input text
74-
* @param {String} [options.voice='en-US_MichaelVoice'] - The voice that is to be used for the synthesis.
75-
* @param {String} [options.customization_id] - The customization ID (GUID) of a custom voice model that is to be used for the synthesis.
76-
* @param {String} [options.url='wss://stream.watsonplatform.net/speech-to-text/api'] base URL for service
77-
* @param {String} [options.watson-token] - Auth token
78-
* @param {String} [options.access_token] - IAM auth token
79-
* @param {Object} [options.headers] - Only works in Node.js, not in browsers. Allows for custom headers to be set, including an Authorization header (preventing the need for auth tokens)
80-
* @param {Boolean} [options.x-watson-learning-opt-out=false] - set to true to opt-out of allowing Watson to use this request to improve it's services
81-
* @param {String} [options.x-watson-metadata] - Associates a customer ID with data that is passed over the connection.
82-
* @param {IamTokenManagerV1} [options.token_manager] - Token manager for authenticating with IAM
83-
* @param {Boolean} [options.rejectUnauthorized] - If true, disable SSL verification for the WebSocket connection
84-
*
80+
* @param {Options} options
81+
* @param {string} [options.url] - Base url for service (default='wss://stream.watsonplatform.net/speech-to-text/api')
82+
* @param {OutgoingHttpHeaders} [options.headers] - Only works in Node.js, not in browsers. Allows for custom headers to be set, including an Authorization header (preventing the need for auth tokens)
83+
* @param {any} [options.tokenManager] - Token manager for authenticating with IAM
84+
* @param {boolean} [options.rejectUnauthorized] - If false, disable SSL verification for the WebSocket connection (default=true)
85+
* @param {string} options.text - The text that us to be synthesized
86+
* @param {string} options.accept - The requested format (MIME type) of the audio
87+
* @param {string[]} [options.timings] - An array that specifies whether the service is to return word timing information for all strings of the input text
88+
* @param {string} [options.accessToken] - Bearer token to put in query string
89+
* @param {string} [options.watsonToken] - Valid Watson authentication token (for Cloud Foundry)
90+
* @param {string} [options.voice] - The voice to use for the synthesis (default='en-US_MichaelVoice')
91+
* @param {string} [options.customizationId] - The customization ID (GUID) of a custom voice model that is to be used for the synthesis
92+
* @param {boolean} [options.xWatsonLearningOptOut] - Indicates whether IBM can use data that is sent over the connection to improve the service for future users (default=false)
93+
* @param {string} [options.xWatsonMetadata] - Associates a customer ID with all data that is passed over the connection. The parameter accepts the argument customer_id={id}, where {id} is a random or generic string that is to be associated with the data
8594
* @constructor
8695
*/
87-
constructor(options) {
96+
constructor(options: Options) {
8897
super(options);
8998
this.options = options;
9099
this.initialized = false;
@@ -93,9 +102,19 @@ class SynthesizeStream extends Readable {
93102
initialize() {
94103
const options = this.options;
95104

96-
const queryParams = pick(options, QUERY_PARAMS_ALLOWED);
105+
// process query params
106+
const queryParamsAllowed = [
107+
'access_token',
108+
'watson-token',
109+
'voice',
110+
'customization_id',
111+
'x-watson-learning-opt-out',
112+
'x-watson-metadata',
113+
];
114+
const queryParams = processUserParameters(options, queryParamsAllowed);
97115
const queryString = qs.stringify(queryParams);
98116

117+
// synthesize the url
99118
const url =
100119
(options.url || 'wss://stream.watsonplatform.net/text-to-speech/api')
101120
.replace(/^http/, 'ws') +
@@ -115,7 +134,13 @@ class SynthesizeStream extends Readable {
115134
const self = this;
116135

117136
socket.onopen = () => {
118-
const payload = pick(options, PAYLOAD_PARAMS_ALLOWED);
137+
// process the payload params
138+
const payloadParamsAllowed = [
139+
'text',
140+
'accept',
141+
'timings',
142+
];
143+
const payload = processUserParameters(options, payloadParamsAllowed);
119144
socket.send(JSON.stringify(payload));
120145
/**
121146
* emitted once the WebSocket connection has been established
@@ -165,7 +190,7 @@ class SynthesizeStream extends Readable {
165190
// even though we aren't controlling the read from websocket,
166191
// we can take advantage of the fact that _read is async and hack
167192
// this funtion to retrieve a token if the service is using IAM auth
168-
this.setAuthorizationHeaderToken(err => {
193+
setAuthorizationHeader(this.options, err => {
169194
if (err) {
170195
this.emit('error', err);
171196
this.push(null);
@@ -177,30 +202,6 @@ class SynthesizeStream extends Readable {
177202
}
178203
});
179204
}
180-
181-
/**
182-
* This function retrieves an IAM access token and stores it in the
183-
* request header before calling the callback function, which will
184-
* execute the next iteration of `_read()`
185-
*
186-
*
187-
* @private
188-
* @param {Function} callback
189-
*/
190-
setAuthorizationHeaderToken(callback) {
191-
if (this.options.token_manager) {
192-
this.options.token_manager.getToken((err, token) => {
193-
if (err) {
194-
callback(err);
195-
}
196-
const authHeader = { authorization: 'Bearer ' + token };
197-
this.options.headers = extend(this.options.headers, authHeader);
198-
callback(null);
199-
});
200-
} else {
201-
callback(null);
202-
}
203-
}
204205
}
205206

206207
export = SynthesizeStream;

lib/websocket-utils.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/**
2+
* (C) Copyright IBM Corp. 2019.
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+
import camelcase = require('camelcase');
18+
import extend = require('extend');
19+
20+
/**
21+
* To adhere to our Node style guideline, we expose lowerCamelCase parameters to the user. However, the
22+
* service expects different case conventions so we have to serialize the user-provided params. We do this
23+
* by passing in the user params with the allowed params, looking for the camelcase version of each allowed
24+
* param, and creating an object with the correct keys.
25+
*
26+
* @param {object} options - the user-provided options, with lower camel case parameters
27+
* @param {string[]} allowedParams - array of the parameter names that the service allows
28+
* @returns {object}
29+
*/
30+
export function processUserParameters(options: any, allowedParams: string[]): any {
31+
const processedOptions = {};
32+
33+
// look for the camelcase version of each parameter - that is what we expose to the user
34+
allowedParams.forEach(param => {
35+
const keyName = camelcase(param);
36+
if (options[keyName] !== undefined) {
37+
processedOptions[param] = options[keyName];
38+
} else if (options[param] !== undefined) {
39+
// if the user used the service property name, warn them and give them the name to use
40+
console.warn(`Unrecognized parameter: "${param}". Did you mean "${keyName}"?`);
41+
}
42+
});
43+
44+
return processedOptions;
45+
}
46+
47+
/**
48+
* This function retrieves an access token and stores it in the
49+
* request header before calling the callback function.
50+
*
51+
* @param {object} options - the internal options of a websocket stream class
52+
* @param {Function} callback
53+
*/
54+
export function setAuthorizationHeader(options: any, callback: Function): void {
55+
// assuming the token manger would fall under property 'tokenManager'
56+
// this will likely change with the new authenticators anyways
57+
if (options.tokenManager) {
58+
options.tokenManager.getToken((err, token) => {
59+
if (err) {
60+
return callback(err);
61+
}
62+
const authHeader = { authorization: 'Bearer ' + token };
63+
options.headers = extend(options.headers, authHeader);
64+
callback(null);
65+
});
66+
} else {
67+
callback(null);
68+
}
69+
}

package-lock.json

Lines changed: 26 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
"@types/node": "^11.9.4",
8585
"async": "^2.6.2",
8686
"axios": "^0.18.0",
87+
"camelcase": "^5.3.1",
8788
"dotenv": "^6.2.0",
8889
"extend": "~3.0.2",
8990
"ibm-cloud-sdk-core": "^0.3.5",

speech-to-text/v1.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ class SpeechToTextV1 extends GeneratedSpeechToTextV1 {
182182

183183
// if using iam, pass the token manager to the RecognizeStream object
184184
if (this.tokenManager) {
185-
params.token_manager = this.tokenManager;
185+
params.tokenManager = this.tokenManager;
186186
}
187187

188188
// include analytics headers

0 commit comments

Comments
 (0)