@@ -75,6 +75,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
7575 #featureFlagRefreshTimer: RefreshTimer ;
7676
7777 // selectors
78+ #keyValueSelectors: PagedSettingSelector [ ] = [ ] ;
7879 #featureFlagSelectors: PagedSettingSelector [ ] = [ ] ;
7980
8081 constructor (
@@ -94,9 +95,10 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
9495 }
9596
9697 if ( options ?. refreshOptions ?. enabled ) {
97- const { watchedSettings, refreshIntervalInMs } = options . refreshOptions ;
98- // validate watched settings
99- if ( watchedSettings === undefined || watchedSettings . length === 0 ) {
98+ const { watchedSettings, refreshIntervalInMs, registerAll } = options . refreshOptions ;
99+
100+ // validate refresh options
101+ if ( registerAll !== true && ( watchedSettings === undefined || watchedSettings . length === 0 ) ) {
100102 throw new Error ( "Refresh is enabled but no watched settings are specified." ) ;
101103 }
102104
@@ -110,19 +112,30 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
110112 }
111113 }
112114
113- for ( const setting of watchedSettings ) {
114- if ( setting . key . includes ( "*" ) || setting . key . includes ( "," ) ) {
115- throw new Error ( "The characters '*' and ',' are not supported in key of watched settings." ) ;
115+ if ( registerAll !== true ) {
116+ if ( watchedSettings === undefined || watchedSettings . length === 0 ) {
117+ throw new Error ( "Refresh is enabled but no watched settings are specified ." ) ;
116118 }
117- if ( setting . label ?. includes ( "*" ) || setting . label ?. includes ( "," ) ) {
118- throw new Error ( "The characters '*' and ',' are not supported in label of watched settings." ) ;
119+ else {
120+ for ( const setting of watchedSettings ) {
121+ if ( setting . key . includes ( "*" ) || setting . key . includes ( "," ) ) {
122+ throw new Error ( "The characters '*' and ',' are not supported in key of watched settings." ) ;
123+ }
124+ if ( setting . label ?. includes ( "*" ) || setting . label ?. includes ( "," ) ) {
125+ throw new Error ( "The characters '*' and ',' are not supported in label of watched settings." ) ;
126+ }
127+ this . #sentinels. push ( setting ) ;
128+ }
119129 }
120- this . #sentinels. push ( setting ) ;
130+ } else if ( watchedSettings && watchedSettings . length > 0 ) {
131+ throw new Error ( "Watched settings should not be specified when registerAll is enabled." ) ;
121132 }
122133
123134 this . #refreshTimer = new RefreshTimer ( this . #refreshInterval) ;
124135 }
125136
137+ this . #keyValueSelectors = getValidKeyValueSelectors ( options ?. selectors ) ;
138+
126139 // feature flag options
127140 if ( options ?. featureFlagOptions ?. enabled ) {
128141 // validate feature flag selectors
@@ -185,6 +198,10 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
185198 return ! ! this . #options?. refreshOptions ?. enabled ;
186199 }
187200
201+ get #registerAll( ) : boolean {
202+ return ! ! this . #options?. refreshOptions ?. registerAll ;
203+ }
204+
188205 get #featureFlagEnabled( ) : boolean {
189206 return ! ! this . #options?. featureFlagOptions ?. enabled ;
190207 }
@@ -204,26 +221,27 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
204221 async #loadSelectedKeyValues( ) : Promise < ConfigurationSetting [ ] > {
205222 const loadedSettings : ConfigurationSetting [ ] = [ ] ;
206223
207- // validate selectors
208- const selectors = getValidKeyValueSelectors ( this . #options?. selectors ) ;
209-
210- for ( const selector of selectors ) {
224+ for ( const selector of this . #keyValueSelectors) {
211225 const listOptions : ListConfigurationSettingsOptions = {
212226 keyFilter : selector . keyFilter ,
213227 labelFilter : selector . labelFilter
214228 } ;
215229
216- const settings = listConfigurationSettingsWithTrace (
230+ const pageEtags : string [ ] = [ ] ;
231+ const pageIterator = listConfigurationSettingsWithTrace (
217232 this . #requestTraceOptions,
218233 this . #client,
219234 listOptions
220- ) ;
221-
222- for await ( const setting of settings ) {
223- if ( ! isFeatureFlag ( setting ) ) { // exclude feature flags
224- loadedSettings . push ( setting ) ;
235+ ) . byPage ( ) ;
236+ for await ( const page of pageIterator ) {
237+ pageEtags . push ( page . etag ?? "" ) ;
238+ for ( const setting of page . items ) {
239+ if ( ! isFeatureFlag ( setting ) ) { // exclude feature flags
240+ loadedSettings . push ( setting ) ;
241+ }
225242 }
226243 }
244+ selector . pageEtags = pageEtags ;
227245 }
228246 return loadedSettings ;
229247 }
@@ -232,10 +250,6 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
232250 * Update etag of watched settings from loaded data. If a watched setting is not covered by any selector, a request will be sent to retrieve it.
233251 */
234252 async #updateWatchedKeyValuesEtag( existingSettings : ConfigurationSetting [ ] ) : Promise < void > {
235- if ( ! this . #refreshEnabled) {
236- return ;
237- }
238-
239253 for ( const sentinel of this . #sentinels) {
240254 const matchedSetting = existingSettings . find ( s => s . key === sentinel . key && s . label === sentinel . label ) ;
241255 if ( matchedSetting ) {
@@ -256,7 +270,9 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
256270 async #loadSelectedAndWatchedKeyValues( ) {
257271 const keyValues : [ key : string , value : unknown ] [ ] = [ ] ;
258272 const loadedSettings = await this . #loadSelectedKeyValues( ) ;
259- await this . #updateWatchedKeyValuesEtag( loadedSettings ) ;
273+ if ( ! this . #registerAll) {
274+ await this . #updateWatchedKeyValuesEtag( loadedSettings ) ;
275+ }
260276
261277 // process key-values, watched settings have higher priority
262278 for ( const setting of loadedSettings ) {
@@ -413,19 +429,46 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
413429 return Promise . resolve ( false ) ;
414430 }
415431
416- // try refresh if any of watched settings is changed.
417432 let needRefresh = false ;
418- for ( const sentinel of this . #sentinels. values ( ) ) {
419- const response = await this . #getConfigurationSetting( sentinel , {
420- onlyIfChanged : true
421- } ) ;
422-
423- if ( response ?. statusCode === 200 // created or changed
424- || ( response === undefined && sentinel . etag !== undefined ) // deleted
425- ) {
426- sentinel . etag = response ?. etag ; // update etag of the sentinel
427- needRefresh = true ;
428- break ;
433+ if ( this . #registerAll) {
434+ // check whether there is any change based on page etags
435+ for ( const selector of this . #featureFlagSelectors) {
436+ const listOptions : ListConfigurationSettingsOptions = {
437+ keyFilter : selector . keyFilter ,
438+ labelFilter : selector . labelFilter ,
439+ pageEtags : selector . pageEtags // when etags are provided, sdk will send conditional request
440+ } ;
441+ const pageIterator = listConfigurationSettingsWithTrace (
442+ this . #requestTraceOptions,
443+ this . #client,
444+ listOptions
445+ ) . byPage ( ) ;
446+
447+ for await ( const page of pageIterator ) {
448+ if ( page . _response . status === 200 ) { // created or changed
449+ needRefresh = true ;
450+ break ;
451+ }
452+ }
453+
454+ if ( needRefresh ) {
455+ break ; // short-circuit if result from any of the selectors is changed
456+ }
457+ }
458+ } else {
459+ // check whether there is any change based on watched settings etags
460+ for ( const sentinel of this . #sentinels. values ( ) ) {
461+ const response = await this . #getConfigurationSetting( sentinel , {
462+ onlyIfChanged : true
463+ } ) ;
464+
465+ if ( response ?. statusCode === 200 // created or changed
466+ || ( response === undefined && sentinel . etag !== undefined ) // deleted (404 exception is caught in #getConfigurationSetting)
467+ ) {
468+ sentinel . etag = response ?. etag ; // update etag of the sentinel
469+ needRefresh = true ;
470+ break ;
471+ }
429472 }
430473 }
431474
@@ -779,15 +822,15 @@ function getValidSelectors(selectors: SettingSelector[]): SettingSelector[] {
779822}
780823
781824function getValidKeyValueSelectors ( selectors ?: SettingSelector [ ] ) : SettingSelector [ ] {
782- if ( ! selectors || selectors . length === 0 ) {
825+ if ( selectors === undefined || selectors . length === 0 ) {
783826 // Default selector: key: *, label: \0
784827 return [ { keyFilter : KeyFilter . Any , labelFilter : LabelFilter . Null } ] ;
785828 }
786829 return getValidSelectors ( selectors ) ;
787830}
788831
789832function getValidFeatureFlagSelectors ( selectors ?: SettingSelector [ ] ) : SettingSelector [ ] {
790- if ( ! selectors || selectors . length === 0 ) {
833+ if ( selectors === undefined || selectors . length === 0 ) {
791834 // selectors must be explicitly provided.
792835 throw new Error ( "Feature flag selectors must be provided." ) ;
793836 } else {
0 commit comments