Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Address comments and feedback
  • Loading branch information
trekforever committed Apr 5, 2024
commit 18f56305e220bba4c203848f717986c334182e46
13 changes: 9 additions & 4 deletions src/remote-config/remote-config-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,12 +375,17 @@ export interface InitServerTemplateOptions extends GetServerTemplateOptions {
* example, customers can reduce initialization latency by pre-fetching and
* caching template data and then using this option to initialize the SDK with
* that data.
* The template can be initialized with either a {@link ServerTemplateData}
* object or a JSON string.
*/
template?: ServerTemplateData|string,
template?: ServerTemplateDataType,
}

/**
* Represents the type of a Remote Config server template that can be set on
* {@link ServerTemplate}. This can either be a {@link ServerTemplateData} object
* or a template JSON string.
*/
export type ServerTemplateDataType = ServerTemplateData | string;

/**
* Represents a stateful abstraction for a Remote Config server template.
*/
Expand All @@ -400,7 +405,7 @@ export interface ServerTemplate {
* Sets and caches a {@link ServerTemplateData} or a JSON string representing
* the server template
*/
set(template: ServerTemplateData | string): void;
set(template: ServerTemplateDataType): void;
Copy link
Member

Choose a reason for hiding this comment

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

Just curious (because I think either is fine) so we need a separate type for this or should this just be ServerTemplateData | string here?

Copy link
Author

Choose a reason for hiding this comment

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

I think either works, I originally had it as ServerTemplateData | string but we are defining it in quite a few places across multiple files, so I moved it to a type 😃


/**
* Returns a JSON representation of {@link ServerTemplateData}
Expand Down
14 changes: 9 additions & 5 deletions src/remote-config/remote-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
DefaultConfig,
GetServerTemplateOptions,
InitServerTemplateOptions,
ServerTemplateDataType,
} from './remote-config-api';
import { isString } from 'lodash';

Expand Down Expand Up @@ -326,19 +327,22 @@ class ServerTemplateImpl implements ServerTemplate {
* Takes in either a {@link ServerTemplateData} or a JSON string
* representing the template, parses it, and caches it.
*/
public set(template: ServerTemplateData | string): void {
public set(template: ServerTemplateDataType): void {
let parsed;
if (isString(template)) {
try {
this.cache = new ServerTemplateDataImpl(JSON.parse(template));
parsed = JSON.parse(template);
} catch (e) {
// Transforms JSON parse errors to Firebase error.
throw new FirebaseRemoteConfigError(
'invalid-argument',
`Failed to parse the JSON string: ${template}. ` + e
);
`Failed to parse the JSON string: ${template}. ` + e);
}
} else {
this.cache = template;
parsed = template;
}
// Throws template parse errors.
this.cache = new ServerTemplateDataImpl(parsed);
}

/**
Expand Down
73 changes: 40 additions & 33 deletions test/unit/remote-config/remote-config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -955,10 +955,6 @@ describe('RemoteConfig', () => {
});

describe('set', () => {
const INVALID_PARAMETERS: any[] = [null, '', 'abc', 1, true, []];
const INVALID_CONDITIONS: any[] = [null, '', 'abc', 1, true, {}];
let sourceTemplate = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);

it('should set template when passed', () => {
const template = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE) as ServerTemplateData;
template.parameters = {
Expand All @@ -970,13 +966,14 @@ describe('RemoteConfig', () => {
valueType: 'STRING'
}
};
template.version = {
...deepCopy(VERSION_INFO),
updateTime: new Date(VERSION_INFO.updateTime).toUTCString()
} as Version;
const initializedTemplate = remoteConfig.initServerTemplate();
initializedTemplate.set(template);
const parsed = initializedTemplate.toJSON();
const expectedVersion = deepCopy(VERSION_INFO);
expectedVersion.updateTime = new Date(expectedVersion.updateTime).toUTCString();
template.version = expectedVersion as Version;
expect(parsed).deep.equals(deepCopy(template));
expect(parsed).deep.equals(template);
Copy link
Contributor

Choose a reason for hiding this comment

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

This is very readable 👍

});

it('should set and instantiates template when json string is passed', () => {
Expand All @@ -990,44 +987,54 @@ describe('RemoteConfig', () => {
valueType: 'STRING'
}
};
template.version = {
...deepCopy(VERSION_INFO),
updateTime: new Date(VERSION_INFO.updateTime).toUTCString()
} as Version;
const templateJson = JSON.stringify(template);
const initializedTemplate = remoteConfig.initServerTemplate();
initializedTemplate.set(templateJson);
const parsed = initializedTemplate.toJSON();
const expectedVersion = deepCopy(VERSION_INFO);
expectedVersion.updateTime = new Date(expectedVersion.updateTime).toUTCString();
template.version = expectedVersion as Version;
expect(parsed).deep.equals(deepCopy(template));
expect(parsed).deep.equals(template);
});

it('should throw if template is an invalid JSON', () => {
describe('should throw error if there are any JSON or tempalte parsing errors', () => {
const INVALID_PARAMETERS: any[] = [null, '', 'abc', 1, true, []];
const INVALID_CONDITIONS: any[] = [null, '', 'abc', 1, true, {}];

let sourceTemplate = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
const jsonString = '{invalidJson: null}';
const initializedTemplate = remoteConfig.initServerTemplate();
expect(() => initializedTemplate.set(jsonString))
.to.throw(/Failed to parse the JSON string: ([\D\w]*)\./);
});

INVALID_PARAMETERS.forEach((invalidParameter) => {
sourceTemplate.parameters = invalidParameter;
it(`should throw if the parameters is ${JSON.stringify(invalidParameter)}`, () => {
const jsonString = JSON.stringify(sourceTemplate);
const initializedTemplate = remoteConfig.initServerTemplate();
expect(() => initializedTemplate.set(jsonString))
it('should throw if template is an invalid JSON', () => {
expect(() => remoteConfig.initServerTemplate({ template: jsonString }))
.to.throw(/Failed to parse the JSON string: ([\D\w]*)\./);
});
});

sourceTemplate = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
INVALID_CONDITIONS.forEach((invalidConditions) => {
sourceTemplate.conditions = invalidConditions;
it(`should throw if the conditions is ${JSON.stringify(invalidConditions)}`, () => {

INVALID_PARAMETERS.forEach((invalidParameter) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Thinking: it feels like we're re-testing the cases covered by the tests on ServerTemplateDataImpl. Looking at the tests, I see us already doing this on the tests for initServerTemplate and load. Makes me think there's an opportunity for a later change to extract ServerTemplateDataImpl to the internal dir so we can test it directly.

sourceTemplate.parameters = invalidParameter;
const jsonString = JSON.stringify(sourceTemplate);
const initializedTemplate = remoteConfig.initServerTemplate();
expect(() => initializedTemplate.set(jsonString))
.to.throw(/Failed to parse the JSON string: ([\D\w]*)\./);
it(`should throw if the template is invalid - parameters is ${JSON.stringify(invalidParameter)}`, () => {
expect(() => remoteConfig.initServerTemplate({ template: jsonString }))
.to.throw('Remote Config parameters must be a non-null object');
});
});

sourceTemplate = deepCopy(SERVER_REMOTE_CONFIG_RESPONSE);
INVALID_CONDITIONS.forEach((invalidConditions) => {
sourceTemplate.conditions = invalidConditions;
const jsonString = JSON.stringify(sourceTemplate);
it(`should throw if the template is invalid - conditions is ${JSON.stringify(invalidConditions)}`, () => {
expect(() => remoteConfig.initServerTemplate({ template: jsonString }))
.to.throw('Remote Config conditions must be an array');
});
});
});

it('should throw if template is an invalid JSON', () => {
const jsonString = '{invalidJson: null}';
const initializedTemplate = remoteConfig.initServerTemplate();
expect(() => initializedTemplate.set(jsonString))
.to.throw(/Failed to parse the JSON string: ([\D\w]*)\./);
});
});

describe('evaluate', () => {
Expand Down