Skip to content

Commit aed5646

Browse files
erikeldridgetrekforeverjenh
authored
Update SSRC API client (#2457)
Add API changes needed for SSRC --------- Co-authored-by: Xin Wei <[email protected]> Co-authored-by: jen_h <[email protected]>
1 parent bd9d4d8 commit aed5646

File tree

3 files changed

+92
-15
lines changed

3 files changed

+92
-15
lines changed

src/remote-config/remote-config-api-client-internal.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,19 @@ import { PrefixedFirebaseError } from '../utils/error';
2121
import * as utils from '../utils/index';
2222
import * as validator from '../utils/validator';
2323
import { deepCopy } from '../utils/deep-copy';
24-
import { ListVersionsOptions, ListVersionsResult, RemoteConfigTemplate } from './remote-config-api';
24+
import {
25+
ListVersionsOptions,
26+
ListVersionsResult,
27+
RemoteConfigTemplate,
28+
RemoteConfigServerTemplateData
29+
} from './remote-config-api';
2530

2631
// Remote Config backend constants
27-
const FIREBASE_REMOTE_CONFIG_V1_API = 'https://firebaseremoteconfig.googleapis.com/v1';
32+
/**
33+
* Allows the `FIREBASE_REMOTE_CONFIG_URL_BASE` environment
34+
* variable to override the default API endpoint URL.
35+
*/
36+
const FIREBASE_REMOTE_CONFIG_URL_BASE = process.env.FIREBASE_REMOTE_CONFIG_URL_BASE || 'https://firebaseremoteconfig.googleapis.com';
2837
const FIREBASE_REMOTE_CONFIG_HEADERS = {
2938
'X-Firebase-Client': `fire-admin-node/${utils.getSdkVersion()}`,
3039
// There is a known issue in which the ETag is not properly returned in cases where the request
@@ -166,6 +175,24 @@ export class RemoteConfigApiClient {
166175
});
167176
}
168177

178+
public getServerTemplate(): Promise<RemoteConfigServerTemplateData> {
179+
return this.getUrl()
180+
.then((url) => {
181+
const request: HttpRequestConfig = {
182+
method: 'GET',
183+
url: `${url}/namespaces/firebase-server/serverRemoteConfig`,
184+
headers: FIREBASE_REMOTE_CONFIG_HEADERS
185+
};
186+
return this.httpClient.send(request);
187+
})
188+
.then((resp) => {
189+
return this.toRemoteConfigServerTemplate(resp);
190+
})
191+
.catch((err) => {
192+
throw this.toFirebaseError(err);
193+
});
194+
}
195+
169196
private sendPutRequest(template: RemoteConfigTemplate, etag: string, validateOnly?: boolean): Promise<HttpResponse> {
170197
let path = 'remoteConfig';
171198
if (validateOnly) {
@@ -191,7 +218,7 @@ export class RemoteConfigApiClient {
191218
private getUrl(): Promise<string> {
192219
return this.getProjectIdPrefix()
193220
.then((projectIdPrefix) => {
194-
return `${FIREBASE_REMOTE_CONFIG_V1_API}/${projectIdPrefix}`;
221+
return `${FIREBASE_REMOTE_CONFIG_URL_BASE}/v1/${projectIdPrefix}`;
195222
});
196223
}
197224

@@ -255,6 +282,24 @@ export class RemoteConfigApiClient {
255282
};
256283
}
257284

285+
/**
286+
* Creates a RemoteConfigServerTemplate from the API response.
287+
* If provided, customEtag is used instead of the etag returned in the API response.
288+
*
289+
* @param {HttpResponse} resp API response object.
290+
* @param {string} customEtag A custom etag to replace the etag fom the API response (Optional).
291+
*/
292+
private toRemoteConfigServerTemplate(resp: HttpResponse, customEtag?: string): RemoteConfigServerTemplateData {
293+
const etag = (typeof customEtag === 'undefined') ? resp.headers['etag'] : customEtag;
294+
this.validateEtag(etag);
295+
return {
296+
conditions: resp.data.conditions,
297+
parameters: resp.data.parameters,
298+
etag,
299+
version: resp.data.version,
300+
};
301+
}
302+
258303
/**
259304
* Checks if the given RemoteConfigTemplate object is valid.
260305
* The object must have valid parameters, parameter groups, conditions, and an etag.

src/remote-config/remote-config-api.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export interface RemoteConfigCondition {
5555
}
5656

5757
/**
58-
* Interface representing a Remote Config condition in the data-plane.
58+
* Represents a Remote Config condition in the dataplane.
5959
* A condition targets a specific group of users. A list of these conditions make up
6060
* part of a Remote Config template.
6161
*/
@@ -156,7 +156,7 @@ export interface RemoteConfigParameterGroup {
156156
}
157157

158158
/**
159-
* Interface representing a Remote Config client template.
159+
* Represents a Remote Config client template.
160160
*/
161161
export interface RemoteConfigTemplate {
162162
/**
@@ -189,7 +189,7 @@ export interface RemoteConfigTemplate {
189189
}
190190

191191
/**
192-
* Interface representing the data in a Remote Config server template.
192+
* Represents the data in a Remote Config server template.
193193
*/
194194
export interface RemoteConfigServerTemplateData {
195195
/**
@@ -203,7 +203,7 @@ export interface RemoteConfigServerTemplateData {
203203
parameters: { [key: string]: RemoteConfigParameter };
204204

205205
/**
206-
* ETag of the current Remote Config template (readonly).
206+
* Current Remote Config template ETag (read-only).
207207
*/
208208
readonly etag: string;
209209

@@ -214,28 +214,28 @@ export interface RemoteConfigServerTemplateData {
214214
}
215215

216216
/**
217-
* Interface representing a stateful abstraction for a Remote Config server template.
217+
* Represents a stateful abstraction for a Remote Config server template.
218218
*/
219219
export interface RemoteConfigServerTemplate {
220220

221221
/**
222-
* Cached {@link RemoteConfigServerTemplateData}
222+
* Cached {@link RemoteConfigServerTemplateData}.
223223
*/
224224
cache: RemoteConfigServerTemplateData;
225225

226226
/**
227-
* A {@link RemoteConfigServerConfig} containing default values for Config
227+
* A {@link RemoteConfigServerConfig} that contains default Config values.
228228
*/
229229
defaultConfig: RemoteConfigServerConfig;
230230

231231
/**
232-
* Evaluates the current template to produce a {@link RemoteConfigServerConfig}
232+
* Evaluates the current template to produce a {@link RemoteConfigServerConfig}.
233233
*/
234234
evaluate(): RemoteConfigServerConfig;
235235

236236
/**
237237
* Fetches and caches the current active version of the
238-
* {@link RemoteConfigServerTemplate} of the project.
238+
* project's {@link RemoteConfigServerTemplate}.
239239
*/
240240
load(): Promise<void>;
241241
}
@@ -364,6 +364,6 @@ export interface ListVersionsOptions {
364364
}
365365

366366
/**
367-
* Type representing the configuration produced by evaluating a server template.
367+
* Represents the configuration produced by evaluating a server template.
368368
*/
369369
export type RemoteConfigServerConfig = { [key: string]: string | boolean | number }

test/unit/remote-config/remote-config-api-client.spec.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { getSdkVersion } from '../../../src/utils/index';
3333
import {
3434
RemoteConfigTemplate, Version, ListVersionsResult,
3535
} from '../../../src/remote-config/index';
36+
import { RemoteConfigServerTemplateData } from '../../../src/remote-config/remote-config-api';
3637

3738
const expect = chai.expect;
3839

@@ -661,6 +662,36 @@ describe('RemoteConfigApiClient', () => {
661662
});
662663
});
663664

665+
describe('getServerTemplate', () => {
666+
it('should reject when project id is not available', () => {
667+
return clientWithoutProjectId.getServerTemplate()
668+
.should.eventually.be.rejectedWith(noProjectId);
669+
});
670+
671+
// tests for api response validations
672+
runEtagHeaderTests(() => apiClient.getServerTemplate());
673+
runErrorResponseTests(() => apiClient.getServerTemplate());
674+
675+
it('should resolve with the latest template on success', () => {
676+
const stub = sinon
677+
.stub(HttpClient.prototype, 'send')
678+
.resolves(utils.responseFrom(TEST_RESPONSE, 200, { etag: 'etag-123456789012-1' }));
679+
stubs.push(stub);
680+
return apiClient.getServerTemplate()
681+
.then((resp) => {
682+
expect(resp.conditions).to.deep.equal(TEST_RESPONSE.conditions);
683+
expect(resp.parameters).to.deep.equal(TEST_RESPONSE.parameters);
684+
expect(resp.etag).to.equal('etag-123456789012-1');
685+
expect(resp.version).to.deep.equal(TEST_RESPONSE.version);
686+
expect(stub).to.have.been.calledOnce.and.calledWith({
687+
method: 'GET',
688+
url: 'https://firebaseremoteconfig.googleapis.com/v1/projects/test-project/namespaces/firebase-server/serverRemoteConfig',
689+
headers: EXPECTED_HEADERS,
690+
});
691+
});
692+
});
693+
});
694+
664695
function runTemplateVersionNumberTests(rcOperation: (v: string | number) => any): void {
665696
['', null, NaN, true, [], {}].forEach((invalidVersion) => {
666697
it(`should reject if the versionNumber is: ${invalidVersion}`, () => {
@@ -677,7 +708,7 @@ describe('RemoteConfigApiClient', () => {
677708
});
678709
}
679710

680-
function runEtagHeaderTests(rcOperation: () => Promise<RemoteConfigTemplate>): void {
711+
function runEtagHeaderTests(rcOperation: () => Promise<RemoteConfigTemplate | RemoteConfigServerTemplateData>): void {
681712
it('should reject when the etag is not present in the response', () => {
682713
const stub = sinon
683714
.stub(HttpClient.prototype, 'send')
@@ -690,7 +721,8 @@ describe('RemoteConfigApiClient', () => {
690721
});
691722
}
692723

693-
function runErrorResponseTests(rcOperation: () => Promise<RemoteConfigTemplate | ListVersionsResult>): void {
724+
function runErrorResponseTests(
725+
rcOperation: () => Promise<RemoteConfigTemplate | RemoteConfigServerTemplateData | ListVersionsResult>): void {
694726
it('should reject when a full platform error response is received', () => {
695727
const stub = sinon
696728
.stub(HttpClient.prototype, 'send')

0 commit comments

Comments
 (0)