diff --git a/src/dotenv-azure.ts b/src/dotenv-azure.ts index 1a5ce761..4562ae22 100644 --- a/src/dotenv-azure.ts +++ b/src/dotenv-azure.ts @@ -1,7 +1,7 @@ import * as fs from 'fs' import { URL } from 'url' import Bottleneck from 'bottleneck' -import dotenv, { DotenvParseOptions, DotenvParseOutput } from 'dotenv' +import dotenv, { DotenvParseOptions } from 'dotenv' import { ManagedIdentityCredential, ClientSecretCredential } from '@azure/identity' import { SecretClient } from '@azure/keyvault-secrets' import { AppConfigurationClient, ConfigurationSetting } from '@azure/app-configuration' @@ -60,8 +60,11 @@ export default class DotenvAzure { async config(options: DotenvAzureConfigOptions = {}): Promise { const { safe = false } = options const dotenvResult = dotenv.config(options) - - const azureVars = await this.loadFromAzure(dotenvResult.parsed) + const vars: Record = { + ...(dotenvResult.parsed || {}), + ...process.env, + } + const azureVars = await this.loadFromAzure(vars) const joinedVars = { ...azureVars, ...dotenvResult.parsed } populateProcessEnv(azureVars) @@ -96,10 +99,14 @@ export default class DotenvAzure { * @param dotenvVars - dotenv parse() output containing azure credentials variables * @returns an object with keys and values */ - async loadFromAzure(dotenvVars?: DotenvParseOutput): Promise { + async loadFromAzure(dotenvVars?: Record): Promise { const credentials = this.getAzureCredentials(dotenvVars) const appConfigClient = new AppConfigurationClient(credentials.connectionString) - const { appConfigVars, keyVaultReferences } = await this.getAppConfigurations(appConfigClient) + const labels = dotenvVars?.AZURE_APP_CONFIG_LABELS || '' + const { appConfigVars, keyVaultReferences } = await this.getAppConfigurations( + appConfigClient, + labels + ) const keyVaultSecrets = await this.getSecretsFromKeyVault(credentials, keyVaultReferences) return { ...appConfigVars, ...keyVaultSecrets } } @@ -114,11 +121,14 @@ export default class DotenvAzure { } } - protected async getAppConfigurations(client: AppConfigurationClient): Promise { + protected async getAppConfigurations( + client: AppConfigurationClient, + labels = '' + ): Promise { const appConfigVars: VariablesObject = {} const keyVaultReferences: KeyVaultReferences = {} - for await (const config of client.listConfigurationSettings()) { + for await (const config of client.listConfigurationSettings({ labelFilter: labels })) { if (this.isKeyVaultReference(config)) { keyVaultReferences[config.key] = this.getKeyVaultReferenceInfo(config) } else { @@ -194,10 +204,9 @@ export default class DotenvAzure { ) } - private getAzureCredentials(dotenvVars: DotenvParseOutput = {}): AzureCredentials { - const vars = { ...dotenvVars, ...process.env } + private getAzureCredentials(vars: Record = {}): AzureCredentials { const connectionString = this.connectionString || vars.AZURE_APP_CONFIG_CONNECTION_STRING - + if (!connectionString) { throw new MissingAppConfigCredentialsError() } diff --git a/test/azure.mock.ts b/test/azure.mock.ts index eade5d46..16a8ed6f 100644 --- a/test/azure.mock.ts +++ b/test/azure.mock.ts @@ -18,6 +18,7 @@ export const appConfigListMock = jest.fn(() => isReadOnly: false, key: 'APP_CONFIG_VAR', value: 'ok', + label: 'the-label', }, { isReadOnly: true, diff --git a/test/dotenv-azure.test.ts b/test/dotenv-azure.test.ts index d20ad473..25c61001 100644 --- a/test/dotenv-azure.test.ts +++ b/test/dotenv-azure.test.ts @@ -19,6 +19,7 @@ const mockReadFileSync = readFileSync as jest.Mock describe('DotenvAzure', () => { const OLD_ENV = process.env const AZURE_APP_CONFIG_CONNECTION_STRING = 'app-config-conneciton-string' + const AZURE_APP_CONFIG_LABELS = 'app-config-labels' const AZURE_TENANT_ID = 'tenant-id' const AZURE_CLIENT_ID = 'client-id' const AZURE_CLIENT_SECRET = 'client-secret' @@ -44,6 +45,12 @@ describe('DotenvAzure', () => { expect(await dotenvAzure.config()).toBeDefined() }) + it('does not throw when AZURE_APP_CONFIG_LABELS is defined', async () => { + process.env = { ...OLD_ENV, AZURE_APP_CONFIG_CONNECTION_STRING, AZURE_APP_CONFIG_LABELS } + const dotenvAzure = new DotenvAzure() + expect(await dotenvAzure.config()).toBeDefined() + }) + it('throws when AZURE_APP_CONFIG_URL and AZURE_APP_CONFIG_CONNECTION_STRING are not defined', () => { const dotenvAzure = new DotenvAzure() expect(dotenvAzure.config()).rejects.toThrowError(MissingAppConfigCredentialsError)