@@ -10,6 +10,7 @@ import type {
1010 AppConfigGetOptions ,
1111 AppConfigGetOutput ,
1212} from '../types/AppConfigProvider' ;
13+ import { APPCONFIG_TOKEN_EXPIRATION } from '../constants' ;
1314
1415/**
1516 * ## Intro
@@ -182,7 +183,10 @@ import type {
182183 */
183184class AppConfigProvider extends BaseProvider {
184185 public client ! : AppConfigDataClient ;
185- protected configurationTokenStore = new Map < string , string > ( ) ;
186+ protected configurationTokenStore = new Map <
187+ string ,
188+ { value : string ; expiration : number }
189+ > ( ) ;
186190 protected valueStore = new Map < string , Uint8Array > ( ) ;
187191 private application ?: string ;
188192 private environment : string ;
@@ -270,23 +274,26 @@ class AppConfigProvider extends BaseProvider {
270274 /**
271275 * Retrieve a configuration from AWS AppConfig.
272276 *
277+ * First we start the session and after that we retrieve the configuration from AppSync.
278+ * When starting a session, the service returns a token that can be used to poll for changes
279+ * for up to 24hrs, so we cache it for later use together with the expiration date.
280+ *
281+ * The value of the configuration is also cached internally because AppConfig returns an empty
282+ * value if the configuration has not changed since the last poll. This way even if your code
283+ * polls the configuration multiple times, we return the most recent value by returning the cached
284+ * one if an empty response is returned by AppConfig.
285+ *
273286 * @param {string } name - Name of the configuration or its ID
274287 * @param {AppConfigGetOptions } options - SDK options to propagate to `StartConfigurationSession` API call
275288 */
276289 protected async _get (
277290 name : string ,
278291 options ?: AppConfigGetOptions
279292 ) : Promise < Uint8Array | undefined > {
280- /**
281- * The new AppConfig APIs require two API calls to return the configuration
282- * First we start the session and after that we retrieve the configuration
283- * We need to store { name: token } pairs to use in the next execution
284- * We also need to store { name : value } pairs because AppConfig returns
285- * an empty value if the session already has the latest configuration
286- * but, we don't want to return an empty value to our callers.
287- * {@link https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-retrieving-the-configuration.html}
288- **/
289- if ( ! this . configurationTokenStore . has ( name ) ) {
293+ if (
294+ ! this . configurationTokenStore . has ( name ) ||
295+ this . configurationTokenStore . get ( name ) ! . expiration <= Date . now ( )
296+ ) {
290297 const sessionOptions : StartConfigurationSessionCommandInput = {
291298 ...( options ?. sdkOptions || { } ) ,
292299 ApplicationIdentifier : this . application ,
@@ -303,20 +310,23 @@ class AppConfigProvider extends BaseProvider {
303310 if ( ! session . InitialConfigurationToken )
304311 throw new Error ( 'Unable to retrieve the configuration token' ) ;
305312
306- this . configurationTokenStore . set ( name , session . InitialConfigurationToken ) ;
313+ this . configurationTokenStore . set ( name , {
314+ value : session . InitialConfigurationToken ,
315+ expiration : Date . now ( ) + APPCONFIG_TOKEN_EXPIRATION ,
316+ } ) ;
307317 }
308318
309319 const getConfigurationCommand = new GetLatestConfigurationCommand ( {
310- ConfigurationToken : this . configurationTokenStore . get ( name ) ,
320+ ConfigurationToken : this . configurationTokenStore . get ( name ) ?. value ,
311321 } ) ;
312322
313323 const response = await this . client . send ( getConfigurationCommand ) ;
314324
315325 if ( response . NextPollConfigurationToken ) {
316- this . configurationTokenStore . set (
317- name ,
318- response . NextPollConfigurationToken
319- ) ;
326+ this . configurationTokenStore . set ( name , {
327+ value : response . NextPollConfigurationToken ,
328+ expiration : Date . now ( ) + APPCONFIG_TOKEN_EXPIRATION ,
329+ } ) ;
320330 } else {
321331 this . configurationTokenStore . delete ( name ) ;
322332 }
0 commit comments