Skip to content

Commit 7aba512

Browse files
authored
feat(utils): add indexer endpoint env var support to AppConfigService (#261)
Add support for VITE_APP_CFG_INDEXER_ENDPOINT_{NETWORK_ID} environment variables to configure indexer endpoints at runtime. Changes: - Add parsing for VITE_APP_CFG_INDEXER_ENDPOINT_* pattern in loadFromViteEnvironment - Add merging of loaded indexer endpoints into config.indexerEndpoints - Add unit tests for env var parsing and getIndexerEndpointOverride getter - Update "getters warn before initialization" test to include indexer endpoint This enables configuring indexer endpoints (which may contain API keys) via .env.local files instead of committing secrets to app.config.json.
1 parent 60c4d0a commit 7aba512

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

packages/utils/src/AppConfigService.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export class AppConfigService {
7272
const loadedNetworkServiceConfigs: NetworkServiceConfigs = {};
7373
const loadedGlobalServiceConfigs: GlobalServiceConfigs = {};
7474
const loadedRpcEndpoints: NetworkSpecificRpcEndpoints = {};
75+
const loadedIndexerEndpoints: Record<string, string> = {};
7576
const loadedFeatureFlags: FeatureFlags = {};
7677

7778
for (const key in env) {
@@ -134,6 +135,13 @@ export class AppConfigService {
134135
loadedRpcEndpoints[networkId] = value;
135136
logger.debug(LOG_SYSTEM, `Loaded RPC override for ${networkId}: ${value}`);
136137
}
138+
} else if (key.startsWith(`${VITE_ENV_PREFIX}INDEXER_ENDPOINT_`)) {
139+
const networkIdSuffix = key.substring(`${VITE_ENV_PREFIX}INDEXER_ENDPOINT_`.length);
140+
const networkId = networkIdSuffix.toLowerCase().replace(/_/g, '-');
141+
if (networkId) {
142+
loadedIndexerEndpoints[networkId] = value;
143+
logger.debug(LOG_SYSTEM, `Loaded indexer endpoint for ${networkId}: ${value}`);
144+
}
137145
} else if (key.startsWith(`${VITE_ENV_PREFIX}FEATURE_FLAG_`)) {
138146
const flagNameSuffix = key.substring(`${VITE_ENV_PREFIX}FEATURE_FLAG_`.length);
139147
const flagName = flagNameSuffix.toLowerCase();
@@ -167,6 +175,14 @@ export class AppConfigService {
167175
}
168176
}
169177
}
178+
if (Object.keys(loadedIndexerEndpoints).length > 0) {
179+
if (!this.config.indexerEndpoints) this.config.indexerEndpoints = {};
180+
for (const networkKey in loadedIndexerEndpoints) {
181+
if (Object.prototype.hasOwnProperty.call(loadedIndexerEndpoints, networkKey)) {
182+
this.config.indexerEndpoints[networkKey] = loadedIndexerEndpoints[networkKey];
183+
}
184+
}
185+
}
170186
this.config.featureFlags = { ...this.config.featureFlags, ...loadedFeatureFlags };
171187

172188
logger.info(

packages/utils/src/__tests__/AppConfigService.test.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,23 @@ describe('AppConfigService', () => {
9292
expect(config.rpcEndpoints?.['polygon-mainnet']).toBe('https://polygon.customrpc.com');
9393
});
9494

95+
it('should parse VITE_APP_CFG_INDEXER_ENDPOINT_ variables correctly', async () => {
96+
mockViteEnv['VITE_APP_CFG_INDEXER_ENDPOINT_STELLAR_TESTNET'] =
97+
'https://indexer.stellar-testnet.com?apikey=test123';
98+
mockViteEnv['VITE_APP_CFG_INDEXER_ENDPOINT_STELLAR_MAINNET'] =
99+
'https://indexer.stellar-mainnet.com?apikey=prod456';
100+
101+
await appConfigServiceInstance.initialize([{ type: 'viteEnv', env: mockViteEnv }]);
102+
const config = appConfigServiceInstance.getConfig();
103+
104+
expect(config.indexerEndpoints?.['stellar-testnet']).toBe(
105+
'https://indexer.stellar-testnet.com?apikey=test123'
106+
);
107+
expect(config.indexerEndpoints?.['stellar-mainnet']).toBe(
108+
'https://indexer.stellar-mainnet.com?apikey=prod456'
109+
);
110+
});
111+
95112
it('should parse VITE_APP_CFG_FEATURE_FLAG_ variables correctly', async () => {
96113
mockViteEnv['VITE_APP_CFG_FEATURE_FLAG_SHOW_COOL_THING'] = 'true';
97114
mockViteEnv['VITE_APP_CFG_FEATURE_FLAG_HIDE_OLD_THING'] = 'false';
@@ -536,6 +553,27 @@ describe('AppConfigService', () => {
536553
).toBeUndefined();
537554
});
538555

556+
it('getIndexerEndpointOverride should return correct indexer URL', async () => {
557+
// Re-initialize with indexer endpoint
558+
appConfigServiceInstance = new AppConfigService();
559+
await appConfigServiceInstance.initialize([
560+
{
561+
type: 'viteEnv',
562+
env: {
563+
VITE_APP_CFG_INDEXER_ENDPOINT_STELLAR_TESTNET:
564+
'https://indexer.stellar.com?apikey=test',
565+
},
566+
},
567+
]);
568+
569+
expect(appConfigServiceInstance.getIndexerEndpointOverride('stellar-testnet')).toBe(
570+
'https://indexer.stellar.com?apikey=test'
571+
);
572+
expect(
573+
appConfigServiceInstance.getIndexerEndpointOverride('non-existent-network')
574+
).toBeUndefined();
575+
});
576+
539577
it('getTypedNestedConfig should return typed nested configuration', async () => {
540578
// Setup a configuration with nested objects
541579
const jsonConfig = {
@@ -664,13 +702,19 @@ describe('AppConfigService', () => {
664702
'getRpcEndpointOverride called before initialization.'
665703
);
666704

705+
freshInstance.getIndexerEndpointOverride('test');
706+
expect(logger.warn).toHaveBeenCalledWith(
707+
'AppConfigService',
708+
'getIndexerEndpointOverride called before initialization.'
709+
);
710+
667711
freshInstance.getTypedNestedConfig('test', 'config');
668712
expect(logger.warn).toHaveBeenCalledWith(
669713
'AppConfigService',
670714
'getTypedNestedConfig called before initialization.'
671715
);
672716

673-
expect(logger.warn).toHaveBeenCalledTimes(5);
717+
expect(logger.warn).toHaveBeenCalledTimes(6);
674718
});
675719
});
676720

0 commit comments

Comments
 (0)