diff --git a/src/remote-config/remote-config-api-client.ts b/src/remote-config/remote-config-api-client.ts index d23a6256e4..ed1d4d759f 100644 --- a/src/remote-config/remote-config-api-client.ts +++ b/src/remote-config/remote-config-api-client.ts @@ -477,6 +477,10 @@ export class RemoteConfigApiClient { optionsCopy.endTime = new Date(optionsCopy.endTime).toISOString(); } } + // Remove undefined fields from optionsCopy + Object.keys(optionsCopy).forEach(key => + (typeof (optionsCopy as any)[key] === 'undefined') && delete (optionsCopy as any)[key] + ); return optionsCopy; } } diff --git a/src/remote-config/remote-config.ts b/src/remote-config/remote-config.ts index fab718e5c5..eface572db 100644 --- a/src/remote-config/remote-config.ts +++ b/src/remote-config/remote-config.ts @@ -145,7 +145,7 @@ export class RemoteConfig implements FirebaseServiceInterface { return this.client.listVersions(options) .then((listVersionsResponse) => { return { - versions: listVersionsResponse.versions.map(version => new VersionImpl(version)), + versions: listVersionsResponse.versions?.map(version => new VersionImpl(version)) ?? [], nextPageToken: listVersionsResponse.nextPageToken, } }); diff --git a/test/integration/remote-config.spec.ts b/test/integration/remote-config.spec.ts index 2e57ebfc01..330fd0efca 100644 --- a/test/integration/remote-config.spec.ts +++ b/test/integration/remote-config.spec.ts @@ -32,7 +32,7 @@ const VALID_PARAMETERS = { }, // eslint-disable-next-line @typescript-eslint/camelcase welcome_message: { - defaultValue: { value: 'welcome text' + Date.now() }, + defaultValue: { value: `welcome text ${Date.now()}` }, conditionalValues: { ios: { value: 'welcome ios text' }, android: { value: 'welcome android text' }, @@ -57,16 +57,22 @@ const VALID_PARAMETER_GROUPS = { }, }; -const VALID_CONDITIONS: admin.remoteConfig.RemoteConfigCondition[] = [{ - name: 'ios', - expression: 'device.os == \'ios\'', - tagColor: 'INDIGO', -}, -{ - name: 'android', - expression: 'device.os == \'android\'', - tagColor: 'GREEN', -}]; +const VALID_CONDITIONS: admin.remoteConfig.RemoteConfigCondition[] = [ + { + name: 'ios', + expression: 'device.os == \'ios\'', + tagColor: 'INDIGO', + }, + { + name: 'android', + expression: 'device.os == \'android\'', + tagColor: 'GREEN', + }, +]; + +const VALID_VERSION = { + description: `template description ${Date.now()}`, +} let currentTemplate: admin.remoteConfig.RemoteConfigTemplate; @@ -88,6 +94,7 @@ describe('admin.remoteConfig', () => { currentTemplate.conditions = VALID_CONDITIONS; currentTemplate.parameters = VALID_PARAMETERS; currentTemplate.parameterGroups = VALID_PARAMETER_GROUPS; + currentTemplate.version = VALID_VERSION; return admin.remoteConfig().validateTemplate(currentTemplate) .then((template) => { expect(template.etag).matches(/^etag-[0-9]*-[0-9]*$/); @@ -95,6 +102,8 @@ describe('admin.remoteConfig', () => { expect(template.conditions).to.deep.equal(VALID_CONDITIONS); expect(template.parameters).to.deep.equal(VALID_PARAMETERS); expect(template.parameterGroups).to.deep.equal(VALID_PARAMETER_GROUPS); + expect(template.version).to.be.not.undefined; + expect(template.version!.description).equals(VALID_VERSION.description); }); }); @@ -103,6 +112,7 @@ describe('admin.remoteConfig', () => { currentTemplate.conditions = []; currentTemplate.parameters = VALID_PARAMETERS; currentTemplate.parameterGroups = VALID_PARAMETER_GROUPS; + currentTemplate.version = VALID_VERSION; return admin.remoteConfig().validateTemplate(currentTemplate) .should.eventually.be.rejected.and.have.property('code', 'remote-config/invalid-argument'); }); @@ -114,6 +124,7 @@ describe('admin.remoteConfig', () => { currentTemplate.conditions = VALID_CONDITIONS; currentTemplate.parameters = VALID_PARAMETERS; currentTemplate.parameterGroups = VALID_PARAMETER_GROUPS; + currentTemplate.version = VALID_VERSION; return admin.remoteConfig().publishTemplate(currentTemplate) .then((template) => { expect(template.etag).matches(/^etag-[0-9]*-[0-9]*$/); @@ -121,6 +132,8 @@ describe('admin.remoteConfig', () => { expect(template.conditions).to.deep.equal(VALID_CONDITIONS); expect(template.parameters).to.deep.equal(VALID_PARAMETERS); expect(template.parameterGroups).to.deep.equal(VALID_PARAMETER_GROUPS); + expect(template.version).to.be.not.undefined; + expect(template.version!.description).equals(VALID_VERSION.description); }); }); @@ -129,13 +142,14 @@ describe('admin.remoteConfig', () => { currentTemplate.conditions = []; currentTemplate.parameters = VALID_PARAMETERS; currentTemplate.parameterGroups = VALID_PARAMETER_GROUPS; + currentTemplate.version = VALID_VERSION; return admin.remoteConfig().publishTemplate(currentTemplate) .should.eventually.be.rejected.and.have.property('code', 'remote-config/invalid-argument'); }); }); describe('getTemplate', () => { - it('verfy that getTemplate() returns the most recently published template', () => { + it('should return the most recently published template', () => { return admin.remoteConfig().getTemplate() .then((template) => { expect(template.etag).matches(/^etag-[0-9]*-[0-9]*$/); @@ -143,6 +157,77 @@ describe('admin.remoteConfig', () => { expect(template.conditions).to.deep.equal(VALID_CONDITIONS); expect(template.parameters).to.deep.equal(VALID_PARAMETERS); expect(template.parameterGroups).to.deep.equal(VALID_PARAMETER_GROUPS); + expect(template.version).to.be.not.undefined; + expect(template.version!.description).equals(VALID_VERSION.description); + }); + }); + }); + + let versionOneNumber: string; + let versionTwoNumber: string; + const versionOneDescription = `getTemplateAtVersion test v1 ${Date.now()}`; + const versionTwoDescription = `getTemplateAtVersion test v2 ${Date.now()}`; + + describe('getTemplateAtVersion', () => { + before(async () => { + // obtain the current active template + let activeTemplate = await admin.remoteConfig().getTemplate(); + + // publish a new template to create a new version number + activeTemplate.version = { description: versionOneDescription }; + activeTemplate = await admin.remoteConfig().publishTemplate(activeTemplate) + expect(activeTemplate.version).to.be.not.undefined; + versionOneNumber = activeTemplate.version!.versionNumber!; + + // publish another template to create a second version number + activeTemplate.version = { description: versionTwoDescription }; + activeTemplate = await admin.remoteConfig().publishTemplate(activeTemplate) + expect(activeTemplate.version).to.be.not.undefined; + versionTwoNumber = activeTemplate.version!.versionNumber!; + }); + + it('should return the requested template version v1', () => { + return admin.remoteConfig().getTemplateAtVersion(versionOneNumber) + .then((template) => { + expect(template.etag).matches(/^etag-[0-9]*-[0-9]*$/); + expect(template.version).to.be.not.undefined; + expect(template.version!.versionNumber).equals(versionOneNumber); + expect(template.version!.description).equals(versionOneDescription); + }); + }); + }); + + describe('listVersions', () => { + it('should return the most recently published 2 versions', () => { + return admin.remoteConfig().listVersions({ + pageSize: 2, + }) + .then((response) => { + expect(response.versions.length).to.equal(2); + // versions should be in reverse chronological order + expect(response.versions[0].description).equals(versionTwoDescription); + expect(response.versions[0].versionNumber).equals(versionTwoNumber); + expect(response.versions[1].description).equals(versionOneDescription); + expect(response.versions[1].versionNumber).equals(versionOneNumber); + }); + }); + }); + + describe('rollback', () => { + it('verify the most recent template version before rollback to the one prior', () => { + return admin.remoteConfig().getTemplate() + .then((template) => { + expect(template.version).to.be.not.undefined; + expect(template.version!.versionNumber).equals(versionTwoNumber); + }); + }); + + it('should rollback to the requested version', () => { + return admin.remoteConfig().rollback(versionOneNumber) + .then((template) => { + expect(template.version).to.be.not.undefined; + expect(template.version!.updateType).equals('ROLLBACK'); + expect(template.version!.description).equals(`Rollback to version ${versionOneNumber}`); }); }); }); diff --git a/test/unit/remote-config/remote-config-api-client.spec.ts b/test/unit/remote-config/remote-config-api-client.spec.ts index 2e1205f65d..9ff46c788b 100644 --- a/test/unit/remote-config/remote-config-api-client.spec.ts +++ b/test/unit/remote-config/remote-config-api-client.spec.ts @@ -599,6 +599,28 @@ describe('RemoteConfigApiClient', () => { }); }); + it('should remove undefined fields from options', () => { + const stub = sinon + .stub(HttpClient.prototype, 'send') + .resolves(utils.responseFrom(TEST_VERSIONS_RESULT, 200)); + stubs.push(stub); + return apiClient.listVersions({ + pageSize: undefined, + pageToken: undefined, + endVersionNumber: 70, + }) + .then(() => { + expect(stub).to.have.been.calledOnce.and.calledWith({ + method: 'GET', + url: 'https://firebaseremoteconfig.googleapis.com/v1/projects/test-project/remoteConfig:listVersions', + headers: EXPECTED_HEADERS, + data: { + endVersionNumber: '70' + } + }); + }); + }); + it('should resolve with a list of template versions on success', () => { const startTime = new Date(2020, 4, 2); const endTime = 'Thu, 07 May 2020 18:44:41 GMT'; diff --git a/test/unit/remote-config/remote-config.spec.ts b/test/unit/remote-config/remote-config.spec.ts index 57b230e7c2..0d05656507 100644 --- a/test/unit/remote-config/remote-config.spec.ts +++ b/test/unit/remote-config/remote-config.spec.ts @@ -366,6 +366,21 @@ describe('RemoteConfig', () => { }); }); + it('should resolve with an empty versions list if the no results are availble for requested list options', () => { + const stub = sinon + .stub(RemoteConfigApiClient.prototype, 'listVersions') + .resolves({}); + stubs.push(stub); + return remoteConfig.listVersions({ + pageSize: 2, + endVersionNumber: 10, + }) + .then((response) => { + expect(response.versions.length).to.equal(0); + expect(response.nextPageToken).to.be.undefined; + }); + }); + it('should resolve with template versions list on success', () => { const stub = sinon .stub(RemoteConfigApiClient.prototype, 'listVersions') @@ -378,6 +393,7 @@ describe('RemoteConfig', () => { expect(response.versions.length).to.equal(2); expect(response.versions[0].updateTime).equals('Thu, 07 May 2020 18:46:09 GMT'); expect(response.versions[1].updateTime).equals('Thu, 07 May 2020 18:44:41 GMT'); + expect(response.nextPageToken).to.equal('76'); }); }); });