Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add BaseService.constructServiceURL method #138

Merged
merged 1 commit into from
May 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions lib/base-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,50 @@ export class BaseService {

static DEFAULT_SERVICE_NAME: string;

/**
* Constructs a service URL by formatting a parameterized URL.
*
* @param {string} parameterizedUrl URL that contains variable placeholders, e.g. '{scheme}://ibm.com'.
* @param {Map<string, string>} defaultUrlVariables Map from variable names to default values.
* Each variable in the parameterized URL must have a default value specified in this map.
* @param {Map<string, string>} providedUrlVariables Map from variable names to desired values.
* If a variable is not provided in this map,
* the default variable value will be used instead.
* @returns {string} The formatted URL with all variable placeholders replaced by values.
*/
static constructServiceURL(
parameterizedUrl: string,
defaultUrlVariables: Map<string, string>,
providedUrlVariables: Map<string, string> | null
): string {
// If null was passed, we set the variables to an empty map.
// This results in all default variable values being used.
if (providedUrlVariables === null) {
providedUrlVariables = new Map<string, string>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick initial question - is there a reason that you used Maps instead of basic objects? e.g. {}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm implementing the same method for Java and Go using a map, so I chose the Map class for consistency. The class provides some useful methods like .has(key). I also chose the Map class because the variables are intended to just have keys and values - nothing more nested than that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That all makes sense! I asked because I don't see them used very often but they seem great for this use case 👍

}

// Verify the provided variable names.
providedUrlVariables.forEach((_, name) => {
if (!defaultUrlVariables.has(name)) {
throw new Error(`'${name}' is an invalid variable name.
Valid variable names: [${Array.from(defaultUrlVariables.keys()).sort()}].`);
}
});

// Format the URL with provided or default variable values.
let formattedUrl = parameterizedUrl;

defaultUrlVariables.forEach((defaultValue, name) => {
// Use the default variable value if none was provided.
const providedValue = providedUrlVariables.get(name);
const formatValue = providedValue !== undefined ? providedValue : defaultValue;

formattedUrl = formattedUrl.replace(`{${name}}`, formatValue);
});

return formattedUrl;
}

protected baseOptions: BaseServiceOptions;

private authenticator: AuthenticatorInterface;
Expand Down
49 changes: 49 additions & 0 deletions test/unit/parameterized-url.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const { BaseService } = require('../../dist/lib/base-service');

const parameterizedUrl = '{scheme}://{domain}:{port}';
const defaultUrlVariables = new Map([
['scheme', 'http'],
['domain', 'ibm.com'],
['port', '9300'],
]);

describe('constructServiceURL', () => {
it('should use default variable values when null is passed', () => {
expect(BaseService.constructServiceURL(parameterizedUrl, defaultUrlVariables, null)).toBe(
'http://ibm.com:9300'
);
});

it('should use the values provided and defaults for the rest', () => {
const providedUrlVariables = new Map([
['scheme', 'https'],
['port', '22'],
]);

expect(
BaseService.constructServiceURL(parameterizedUrl, defaultUrlVariables, providedUrlVariables)
).toBe('https://ibm.com:22');
});

it('should use all provided values', () => {
const providedUrlVariables = new Map([
['scheme', 'https'],
['domain', 'google.com'],
['port', '22'],
]);

expect(
BaseService.constructServiceURL(parameterizedUrl, defaultUrlVariables, providedUrlVariables)
).toBe('https://google.com:22');
});

it('should throw an error if a provided variable name is wrong', () => {
const providedUrlVariables = new Map([['server', 'value']]);

expect(() =>
BaseService.constructServiceURL(parameterizedUrl, defaultUrlVariables, providedUrlVariables)
).toThrow(
/'server' is an invalid variable name\.\n\s*Valid variable names: \[domain,port,scheme\]\./
);
});
});