@@ -22,9 +22,10 @@ import { buildUrl } from './urlUtils';
2222
2323/**
2424 * Context holding feature flag state for a specific host.
25+ * Stores all feature flags from the server for extensibility.
2526 */
2627export interface FeatureFlagContext {
27- telemetryEnabled ?: boolean ;
28+ flags : Map < string , string > ; // All feature flags from server (extensible for future flags)
2829 lastFetched ?: Date ;
2930 refCount : number ;
3031 cacheDuration : number ; // 15 minutes in ms
@@ -54,6 +55,7 @@ export default class FeatureFlagCache {
5455 let ctx = this . contexts . get ( host ) ;
5556 if ( ! ctx ) {
5657 ctx = {
58+ flags : new Map < string , string > ( ) ,
5759 refCount : 0 ,
5860 cacheDuration : this . CACHE_DURATION_MS ,
5961 } ;
@@ -78,10 +80,14 @@ export default class FeatureFlagCache {
7880 }
7981
8082 /**
81- * Checks if telemetry is enabled for the host .
83+ * Generic method to check if a feature flag is enabled .
8284 * Uses cached value if available and not expired.
85+ *
86+ * @param host The host to check
87+ * @param flagName The feature flag name to query
88+ * @returns true if flag is enabled (value is "true"), false otherwise
8389 */
84- async isTelemetryEnabled ( host : string ) : Promise < boolean > {
90+ async isFeatureEnabled ( host : string , flagName : string ) : Promise < boolean > {
8591 const logger = this . context . getLogger ( ) ;
8692 const ctx = this . contexts . get ( host ) ;
8793
@@ -93,26 +99,36 @@ export default class FeatureFlagCache {
9399
94100 if ( isExpired ) {
95101 try {
96- // Fetch feature flag from server
97- ctx . telemetryEnabled = await this . fetchFeatureFlag ( host ) ;
102+ // Fetch all feature flags from server
103+ await this . fetchFeatureFlags ( host ) ;
98104 ctx . lastFetched = new Date ( ) ;
99105 } catch ( error : any ) {
100106 // Log at debug level only, never propagate exceptions
101- logger . log ( LogLevel . debug , `Error fetching feature flag : ${ error . message } ` ) ;
107+ logger . log ( LogLevel . debug , `Error fetching feature flags : ${ error . message } ` ) ;
102108 }
103109 }
104110
105- return ctx . telemetryEnabled ?? false ;
111+ // Get flag value and parse as boolean
112+ const value = ctx . flags . get ( flagName ) ;
113+ return value ?. toLowerCase ( ) === 'true' ;
114+ }
115+
116+ /**
117+ * Convenience method to check if telemetry is enabled for the host.
118+ * Uses cached value if available and not expired.
119+ */
120+ async isTelemetryEnabled ( host : string ) : Promise < boolean > {
121+ return this . isFeatureEnabled ( host , this . FEATURE_FLAG_NAME ) ;
106122 }
107123
108124 /**
109- * Fetches feature flag from server using connector-service API.
110- * Calls GET /api/2.0/connector-service/feature-flags/OSS_NODEJS/{version}
125+ * Fetches all feature flags from server using connector-service API.
126+ * Calls GET /api/2.0/connector-service/feature-flags/NODEJS/{version}
127+ * Stores all flags in the context for extensibility.
111128 *
112- * @param host The host to fetch feature flag for
113- * @returns true if feature flag is enabled, false otherwise
129+ * @param host The host to fetch feature flags for
114130 */
115- private async fetchFeatureFlag ( host : string ) : Promise < boolean > {
131+ private async fetchFeatureFlags ( host : string ) : Promise < void > {
116132 const logger = this . context . getLogger ( ) ;
117133
118134 try {
@@ -143,41 +159,39 @@ export default class FeatureFlagCache {
143159 } ) ;
144160
145161 if ( ! response . ok ) {
146- logger . log ( LogLevel . debug , `Feature flag fetch failed: ${ response . status } ${ response . statusText } ` ) ;
147- return false ;
162+ logger . log ( LogLevel . debug , `Feature flags fetch failed: ${ response . status } ${ response . statusText } ` ) ;
163+ return ;
148164 }
149165
150166 // Parse response JSON
151167 const data : any = await response . json ( ) ;
152168
153169 // Response format: { flags: [{ name: string, value: string }], ttl_seconds?: number }
154170 if ( data && data . flags && Array . isArray ( data . flags ) ) {
155- // Update cache duration if TTL provided
156171 const ctx = this . contexts . get ( host ) ;
157- if ( ctx && data . ttl_seconds ) {
158- ctx . cacheDuration = data . ttl_seconds * 1000 ; // Convert to milliseconds
159- logger . log ( LogLevel . debug , `Updated cache duration to ${ data . ttl_seconds } seconds` ) ;
172+ if ( ! ctx ) {
173+ return ;
174+ }
175+
176+ // Clear existing flags and store all flags from response
177+ ctx . flags . clear ( ) ;
178+ for ( const flag of data . flags ) {
179+ if ( flag . name && flag . value !== undefined ) {
180+ ctx . flags . set ( flag . name , String ( flag . value ) ) ;
181+ }
160182 }
161183
162- // Look for our specific feature flag
163- const flag = data . flags . find ( ( f : any ) => f . name === this . FEATURE_FLAG_NAME ) ;
184+ logger . log ( LogLevel . debug , `Stored ${ ctx . flags . size } feature flags from server` ) ;
164185
165- if ( flag ) {
166- // Parse boolean value (can be string "true"/"false")
167- const value = String ( flag . value ) . toLowerCase ( ) ;
168- const enabled = value === 'true' ;
169- logger . log ( LogLevel . debug , `Feature flag ${ this . FEATURE_FLAG_NAME } : ${ enabled } ` ) ;
170- return enabled ;
186+ // Update cache duration if TTL provided
187+ if ( data . ttl_seconds ) {
188+ ctx . cacheDuration = data . ttl_seconds * 1000 ; // Convert to milliseconds
189+ logger . log ( LogLevel . debug , `Updated cache duration to ${ data . ttl_seconds } seconds` ) ;
171190 }
172191 }
173-
174- // Feature flag not found in response, default to false
175- logger . log ( LogLevel . debug , `Feature flag ${ this . FEATURE_FLAG_NAME } not found in response` ) ;
176- return false ;
177192 } catch ( error : any ) {
178193 // Log at debug level only, never propagate exceptions
179- logger . log ( LogLevel . debug , `Error fetching feature flag from ${ host } : ${ error . message } ` ) ;
180- return false ;
194+ logger . log ( LogLevel . debug , `Error fetching feature flags from ${ host } : ${ error . message } ` ) ;
181195 }
182196 }
183197
0 commit comments