@@ -23,9 +23,16 @@ import {
23
23
RemoteConfigCondition ,
24
24
RemoteConfigParameter ,
25
25
RemoteConfigParameterGroup ,
26
+ RemoteConfigServerTemplate ,
26
27
RemoteConfigTemplate ,
27
28
RemoteConfigUser ,
28
29
Version ,
30
+ ExplicitParameterValue ,
31
+ InAppDefaultValue ,
32
+ ParameterValueType ,
33
+ RemoteConfigServerConfig ,
34
+ RemoteConfigServerTemplateData ,
35
+ RemoteConfigServerTemplateOptions ,
29
36
} from './remote-config-api' ;
30
37
31
38
/**
@@ -168,6 +175,27 @@ export class RemoteConfig {
168
175
169
176
return new RemoteConfigTemplateImpl ( template ) ;
170
177
}
178
+
179
+ /**
180
+ * Instantiates {@link RemoteConfigServerTemplate} and then fetches and caches the latest
181
+ * template version of the project.
182
+ */
183
+ public async getServerTemplate ( options ?: RemoteConfigServerTemplateOptions ) : Promise < RemoteConfigServerTemplate > {
184
+ const template = this . initServerTemplate ( options ) ;
185
+ await template . load ( ) ;
186
+ return template ;
187
+ }
188
+
189
+ /**
190
+ * Synchronously instantiates {@link RemoteConfigServerTemplate}.
191
+ */
192
+ public initServerTemplate ( options ?: RemoteConfigServerTemplateOptions ) : RemoteConfigServerTemplate {
193
+ const template = new RemoteConfigServerTemplateImpl ( this . client , options ?. defaultConfig ) ;
194
+ if ( options ?. template ) {
195
+ template . cache = options ?. template ;
196
+ }
197
+ return template ;
198
+ }
171
199
}
172
200
173
201
/**
@@ -254,6 +282,143 @@ class RemoteConfigTemplateImpl implements RemoteConfigTemplate {
254
282
}
255
283
}
256
284
285
+ /**
286
+ * Remote Config dataplane template data implementation.
287
+ */
288
+ class RemoteConfigServerTemplateImpl implements RemoteConfigServerTemplate {
289
+ public cache : RemoteConfigServerTemplateData ;
290
+
291
+ constructor (
292
+ private readonly apiClient : RemoteConfigApiClient ,
293
+ public readonly defaultConfig : RemoteConfigServerConfig = { }
294
+ ) { }
295
+
296
+ /**
297
+ * Fetches and caches the current active version of the project's {@link RemoteConfigServerTemplate}.
298
+ */
299
+ public load ( ) : Promise < void > {
300
+ return this . apiClient . getServerTemplate ( )
301
+ . then ( ( template ) => {
302
+ this . cache = new RemoteConfigServerTemplateDataImpl ( template ) ;
303
+ } ) ;
304
+ }
305
+
306
+ /**
307
+ * Evaluates the current template in cache to produce a {@link RemoteConfigServerConfig}.
308
+ */
309
+ public evaluate ( ) : RemoteConfigServerConfig {
310
+ if ( ! this . cache ) {
311
+ throw new FirebaseRemoteConfigError (
312
+ 'failed-precondition' ,
313
+ 'No Remote Config Server template in cache. Call load() before calling evaluate().' ) ;
314
+ }
315
+
316
+ const evaluatedConfig : RemoteConfigServerConfig = { } ;
317
+
318
+ for ( const [ key , parameter ] of Object . entries ( this . cache . parameters ) ) {
319
+ const { defaultValue, valueType } = parameter ;
320
+
321
+ if ( ! defaultValue ) {
322
+ // TODO: add logging once we have a wrapped logger.
323
+ continue ;
324
+ }
325
+
326
+ if ( ( defaultValue as InAppDefaultValue ) . useInAppDefault ) {
327
+ // TODO: add logging once we have a wrapped logger.
328
+ continue ;
329
+ }
330
+
331
+ const parameterDefaultValue = ( defaultValue as ExplicitParameterValue ) . value ;
332
+
333
+ evaluatedConfig [ key ] = this . parseRemoteConfigParameterValue ( valueType , parameterDefaultValue ) ;
334
+ }
335
+
336
+ // Merges rendered config over default config.
337
+ const mergedConfig = Object . assign ( this . defaultConfig , evaluatedConfig ) ;
338
+
339
+ // Enables config to be a convenient object, but with the ability to perform additional
340
+ // functionality when a value is retrieved.
341
+ const proxyHandler = {
342
+ get ( target : RemoteConfigServerConfig , prop : string ) {
343
+ return target [ prop ] ;
344
+ }
345
+ } ;
346
+
347
+ return new Proxy ( mergedConfig , proxyHandler ) ;
348
+ }
349
+
350
+ /**
351
+ * Private helper method that processes and parses a parameter value based on {@link ParameterValueType}.
352
+ */
353
+ private parseRemoteConfigParameterValue ( parameterType : ParameterValueType | undefined ,
354
+ parameterDefaultValue : string ) : string | number | boolean {
355
+ const BOOLEAN_TRUTHY_VALUES = [ '1' , 'true' , 't' , 'yes' , 'y' , 'on' ] ;
356
+ const DEFAULT_VALUE_FOR_NUMBER = 0 ;
357
+ const DEFAULT_VALUE_FOR_STRING = '' ;
358
+
359
+ if ( parameterType === 'BOOLEAN' ) {
360
+ return BOOLEAN_TRUTHY_VALUES . indexOf ( parameterDefaultValue ) >= 0 ;
361
+ } else if ( parameterType === 'NUMBER' ) {
362
+ const num = Number ( parameterDefaultValue ) ;
363
+ if ( isNaN ( num ) ) {
364
+ return DEFAULT_VALUE_FOR_NUMBER ;
365
+ }
366
+ return num ;
367
+ } else {
368
+ // Treat everything else as string
369
+ return parameterDefaultValue || DEFAULT_VALUE_FOR_STRING ;
370
+ }
371
+ }
372
+ }
373
+
374
+ /**
375
+ * Remote Config dataplane template data implementation.
376
+ */
377
+ class RemoteConfigServerTemplateDataImpl implements RemoteConfigServerTemplateData {
378
+ public parameters : { [ key : string ] : RemoteConfigParameter } ;
379
+ public parameterGroups : { [ key : string ] : RemoteConfigParameterGroup } ;
380
+ public conditions : RemoteConfigCondition [ ] ;
381
+ public readonly etag : string ;
382
+ public version ?: Version ;
383
+
384
+ constructor ( template : RemoteConfigServerTemplateData ) {
385
+ if ( ! validator . isNonNullObject ( template ) ||
386
+ ! validator . isNonEmptyString ( template . etag ) ) {
387
+ throw new FirebaseRemoteConfigError (
388
+ 'invalid-argument' ,
389
+ `Invalid Remote Config template: ${ JSON . stringify ( template ) } ` ) ;
390
+ }
391
+
392
+ this . etag = template . etag ;
393
+
394
+ if ( typeof template . parameters !== 'undefined' ) {
395
+ if ( ! validator . isNonNullObject ( template . parameters ) ) {
396
+ throw new FirebaseRemoteConfigError (
397
+ 'invalid-argument' ,
398
+ 'Remote Config parameters must be a non-null object' ) ;
399
+ }
400
+ this . parameters = template . parameters ;
401
+ } else {
402
+ this . parameters = { } ;
403
+ }
404
+
405
+ if ( typeof template . conditions !== 'undefined' ) {
406
+ if ( ! validator . isArray ( template . conditions ) ) {
407
+ throw new FirebaseRemoteConfigError (
408
+ 'invalid-argument' ,
409
+ 'Remote Config conditions must be an array' ) ;
410
+ }
411
+ this . conditions = template . conditions ;
412
+ } else {
413
+ this . conditions = [ ] ;
414
+ }
415
+
416
+ if ( typeof template . version !== 'undefined' ) {
417
+ this . version = new VersionImpl ( template . version ) ;
418
+ }
419
+ }
420
+ }
421
+
257
422
/**
258
423
* Remote Config Version internal implementation.
259
424
*/
0 commit comments