Skip to content

Commit cfb188f

Browse files
committed
feat: add setServiceUrl method as a setter for the serviceUrl property (#41)
* Change `url` to `serviceUrl` * Validate that `serviceUrl` is set in the `sendRequest` method * Make "auth type" case-insensitive * Throw an error for unsupported "auth types" - only default to 'iam' when no "auth type" is provided BREAKING CHANGE: The internal property `url` no longer exists on the `baseOptions` object, it has been renamed to `serviceUrl`
1 parent 190dbf5 commit cfb188f

File tree

7 files changed

+131
-43
lines changed

7 files changed

+131
-43
lines changed

auth/utils/get-authenticator-from-environment.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,24 +54,34 @@ export function getAuthenticatorFromEnvironment(serviceName: string) {
5454
delete credentials.authDisableSsl;
5555
}
5656

57+
// default the auth type to `iam` if authType is undefined, or not a string
58+
let { authType } = credentials;
59+
if (!authType || typeof authType !== 'string') {
60+
authType = 'iam';
61+
}
62+
5763
// create and return the appropriate authenticator
5864
let authenticator;
5965

60-
switch (credentials.authType) {
61-
case 'noAuth':
66+
// fold authType to lower case for case insensitivity
67+
switch (authType.toLowerCase()) {
68+
case 'noauth':
6269
authenticator = new NoAuthAuthenticator();
6370
break;
6471
case 'basic':
6572
authenticator = new BasicAuthenticator(credentials);
6673
break;
67-
case 'bearerToken':
74+
case 'bearertoken':
6875
authenticator = new BearerTokenAuthenticator(credentials);
6976
break;
7077
case 'cp4d':
7178
authenticator = new CloudPakForDataAuthenticator(credentials);
7279
break;
73-
default: // default the authentication type to iam
80+
case 'iam':
7481
authenticator = new IamAuthenticator(credentials);
82+
break;
83+
default:
84+
throw new Error('Invalid value for AUTH_TYPE: ' + authType);
7585
}
7686

7787
return authenticator;

lib/base_service.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import { stripTrailingSlash } from './helper';
2323
import { RequestWrapper } from './requestwrapper';
2424

2525
export interface UserOptions {
26-
url?: string;
26+
url?: string; // deprecated
27+
serviceUrl?: string;
2728
version?: string;
2829
headers?: OutgoingHttpHeaders;
2930
disableSslVerification?: boolean;
@@ -66,12 +67,17 @@ export class BaseService {
6667
const _options = {} as BaseServiceOptions;
6768
const options = extend({}, userOptions);
6869

69-
if (options.url) {
70-
_options.url = stripTrailingSlash(options.url);
70+
// for compatibility
71+
if (options.url && !options.serviceUrl) {
72+
options.serviceUrl = options.url;
7173
}
7274

73-
// check url for common user errors
74-
const credentialProblems = checkCredentials(options, ['url']);
75+
if (options.serviceUrl) {
76+
_options.serviceUrl = stripTrailingSlash(options.serviceUrl);
77+
}
78+
79+
// check serviceUrl for common user errors
80+
const credentialProblems = checkCredentials(options, ['serviceUrl']);
7581
if (credentialProblems) {
7682
throw new Error(credentialProblems);
7783
}
@@ -84,15 +90,15 @@ export class BaseService {
8490

8591
const serviceClass = this.constructor as typeof BaseService;
8692
this.baseOptions = extend(
87-
{ qs: {}, url: serviceClass.URL },
93+
{ qs: {}, serviceUrl: serviceClass.URL },
8894
options,
8995
this.readOptionsFromExternalConfig(),
9096
_options
9197
);
9298

9399
this.requestWrapperInstance = new RequestWrapper(this.baseOptions);
94100

95-
// set authenticator
101+
// enforce that an authenticator is set
96102
if (!options.authenticator) {
97103
throw new Error('Authenticator must be set.');
98104
}
@@ -109,6 +115,15 @@ export class BaseService {
109115
return this.authenticator;
110116
}
111117

118+
/**
119+
* Set the service URL to send requests to.
120+
*
121+
* @param {string} the base URL for the service
122+
*/
123+
public setServiceUrl(url: string): void {
124+
this.baseOptions.serviceUrl = url;
125+
}
126+
112127
/**
113128
* Wrapper around `sendRequest` that enforces the request will be authenticated.
114129
*
@@ -144,7 +159,7 @@ export class BaseService {
144159
const { url, disableSsl } = properties;
145160

146161
if (url) {
147-
results.url = url;
162+
results.serviceUrl = url;
148163
}
149164
if (disableSsl === true) {
150165
results.disableSslVerification = disableSsl;

lib/requestwrapper.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,16 @@ export class RequestWrapper {
122122
*/
123123
public sendRequest(parameters, _callback) {
124124
const options = extend(true, {}, parameters.defaultOptions, parameters.options);
125-
const { path, body, form, formData, qs, method } = options;
126-
let { url, headers } = options;
125+
const { path, body, form, formData, qs, method, serviceUrl } = options;
126+
let { headers, url } = options;
127+
128+
// validate service url parameter has been set
129+
if (!serviceUrl || typeof serviceUrl !== 'string') {
130+
return _callback(new Error('The service URL is required'), null);
131+
}
132+
133+
// use default service url if `url` parameter is not specified in generated method
134+
url = url || serviceUrl;
127135

128136
const multipartForm = new FormData();
129137

@@ -171,7 +179,7 @@ export class RequestWrapper {
171179

172180
// Add service default endpoint if options.url start with /
173181
if (url && url.charAt(0) === '/') {
174-
url = parameters.defaultOptions.url + url;
182+
url = serviceUrl + url;
175183
}
176184

177185
let data = body;

migration-guide.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,6 @@ const service = new MyService({
3131
- `JwtTokenManagerV1` renamed to `JwtTokenManager`
3232
- Token managers no longer support the `accessToken` parameter. There is no need for a token manager when a user is managing their own token. This behavior is replaced by the `BearerTokenAuthenticator` class.
3333
- In the IAM Token Manager: the method `setAuthorizationInfo` is renamed to `setClientIdAndSecret`
34+
35+
#### URL parameter name changed
36+
The variable name for the stored, URL parameter has been changed from `url` to `serviceUrl`. Note that `url` can still be compatibility passed into the constructor as an alias for `serviceUrl`. However, if you try to access the `url` property directly in your code, this is a breaking change.

test/unit/base-service.test.js

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,34 @@ describe('Base Service', () => {
7070
}).toThrow();
7171
});
7272

73-
it('should strip trailing slash of url during instantiation', () => {
73+
it('should strip trailing slash of serviceUrl during instantiation', () => {
74+
const testService = new TestService({
75+
authenticator: AUTHENTICATOR,
76+
serviceUrl: 'https://example.ibm.com/',
77+
});
78+
79+
expect(testService.baseOptions.serviceUrl).toBe('https://example.ibm.com');
80+
});
81+
82+
it('should accept `url` instead of `serviceUrl` for compaitiblity', () => {
7483
const testService = new TestService({
7584
authenticator: AUTHENTICATOR,
76-
version: 'v1',
7785
url: 'https://example.ibm.com/',
7886
});
7987

80-
expect(testService.baseOptions.url).toBe('https://example.ibm.com');
88+
expect(testService.baseOptions.serviceUrl).toBe('https://example.ibm.com');
89+
});
90+
91+
it('should support setting the service url after instantiation', () => {
92+
const testService = new TestService({
93+
authenticator: AUTHENTICATOR,
94+
});
95+
96+
expect(testService.baseOptions.serviceUrl).toBe(DEFAULT_URL);
97+
98+
const newUrl = 'new.com';
99+
testService.setServiceUrl(newUrl);
100+
expect(testService.baseOptions.serviceUrl).toBe(newUrl);
81101
});
82102

83103
it('should throw an error if an authenticator is not passed in', () => {
@@ -120,7 +140,7 @@ describe('Base Service', () => {
120140
authenticator: AUTHENTICATOR,
121141
disableSslVerification: false,
122142
proxy: false,
123-
url: DEFAULT_URL,
143+
serviceUrl: DEFAULT_URL,
124144
qs: EMPTY_OBJECT,
125145
});
126146
});
@@ -134,20 +154,20 @@ describe('Base Service', () => {
134154
expect(testService.baseOptions.qs).toEqual(EMPTY_OBJECT);
135155
});
136156

137-
it('should use the default service url', () => {
157+
it('should use the default service serviceUrl', () => {
138158
const testService = new TestService({
139159
authenticator: AUTHENTICATOR,
140160
});
141161

142-
expect(testService.baseOptions.url).toBe(DEFAULT_URL);
162+
expect(testService.baseOptions.serviceUrl).toBe(DEFAULT_URL);
143163
});
144164

145-
it('should read url and disableSslVerification from env', () => {
146-
const url = 'abc123.com';
165+
it('should read serviceUrl and disableSslVerification from env', () => {
166+
const serviceUrl = 'abc123.com';
147167
const disableSsl = true;
148168

149169
readExternalSourcesMock.mockImplementation(() => ({
150-
url,
170+
url: serviceUrl,
151171
disableSsl,
152172
}));
153173

@@ -157,7 +177,7 @@ describe('Base Service', () => {
157177

158178
const fromCredsFile = testService.readOptionsFromExternalConfig();
159179

160-
expect(fromCredsFile.url).toBe(url);
180+
expect(fromCredsFile.serviceUrl).toBe(serviceUrl);
161181
expect(fromCredsFile.disableSslVerification).toBe(disableSsl);
162182
expect(readExternalSourcesMock).toHaveBeenCalled();
163183
expect(readExternalSourcesMock.mock.calls[0][0]).toBe(DEFAULT_NAME);
@@ -170,12 +190,12 @@ describe('Base Service', () => {
170190

171191
const testService = new TestService({
172192
authenticator: AUTHENTICATOR,
173-
url: 'withtrailingslash.com/api/',
193+
serviceUrl: 'withtrailingslash.com/api/',
174194
proxy: false,
175195
});
176196

177197
expect(testService.baseOptions).toEqual({
178-
url: 'withtrailingslash.com/api',
198+
serviceUrl: 'withtrailingslash.com/api',
179199
disableSslVerification: true,
180200
proxy: false,
181201
qs: EMPTY_OBJECT,
@@ -192,11 +212,11 @@ describe('Base Service', () => {
192212

193213
const parameters = {
194214
defaultOptions: {
195-
url: DEFAULT_URL,
215+
serviceUrl: DEFAULT_URL,
196216
Accept: 'application/json',
197217
},
198218
options: {
199-
url: '/v2/assistants/{assistant_id}/sessions',
219+
serviceUrl: '/v2/assistants/{assistant_id}/sessions',
200220
method: 'POST',
201221
path: {
202222
id: '123',
@@ -219,11 +239,11 @@ describe('Base Service', () => {
219239

220240
const parameters = {
221241
defaultOptions: {
222-
url: DEFAULT_URL,
242+
serviceUrl: DEFAULT_URL,
223243
Accept: 'application/json',
224244
},
225245
options: {
226-
url: '/v2/assistants/{assistant_id}/sessions',
246+
serviceUrl: '/v2/assistants/{assistant_id}/sessions',
227247
method: 'POST',
228248
path: {
229249
id: '123',
@@ -268,11 +288,11 @@ describe('Base Service', () => {
268288
expect(testService.readOptionsFromExternalConfig()).toEqual(EMPTY_OBJECT);
269289
});
270290

271-
it('should check url for common problems', () => {
291+
it('should check serviceUrl for common problems', () => {
272292
expect(() => {
273293
new TestService({
274294
authenticator: AUTHENTICATOR,
275-
url: 'myapi.com/{instanceId}',
295+
serviceUrl: 'myapi.com/{instanceId}',
276296
});
277297
}).toThrow(/Revise these credentials/);
278298
});

test/unit/get-authenticator-from-environment.test.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ describe('Get Authenticator From Environment Module', () => {
3131
expect(() => getAuthenticatorFromEnvironment(SERVICE_NAME)).toThrow();
3232
});
3333

34-
it('should get no auth authenticator', () => {
35-
setUpNoauthPayload();
34+
it('should get no auth authenticator - tests case insentivity of auth type', () => {
35+
setUpNoAuthPayload();
3636
const authenticator = getAuthenticatorFromEnvironment(SERVICE_NAME);
3737
expect(authenticator).toBeInstanceOf(NoAuthAuthenticator);
3838
expect(readExternalSourcesMock).toHaveBeenCalled();
@@ -82,12 +82,19 @@ describe('Get Authenticator From Environment Module', () => {
8282
expect(authenticator).toBeInstanceOf(IamAuthenticator);
8383
expect(authenticator.apikey).toBe(APIKEY);
8484
});
85+
86+
it('should throw an error when an unsupported auth type is provided', () => {
87+
readExternalSourcesMock.mockImplementation(() => ({ apikey: APIKEY, authType: 'unsupported' }));
88+
expect(() => {
89+
getAuthenticatorFromEnvironment(SERVICE_NAME);
90+
}).toThrow();
91+
});
8592
});
8693

8794
// mock payloads for the read-external-sources module
88-
function setUpNoauthPayload() {
95+
function setUpNoAuthPayload() {
8996
readExternalSourcesMock.mockImplementation(() => ({
90-
authType: 'noAuth',
97+
authType: 'noauth',
9198
}));
9299
}
93100

0 commit comments

Comments
 (0)