From 8e39079bffa452e34802fb8819159bde1fdbb4ae Mon Sep 17 00:00:00 2001 From: cwanjau Date: Thu, 30 Oct 2025 16:16:02 +0300 Subject: [PATCH 01/23] Return configuration diff when dry-run is true --- .../src/appConfigurationImporter.ts | 93 +++++++++++++------ .../src/index.ts | 2 +- .../src/models.ts | 13 +++ 3 files changed, 81 insertions(+), 27 deletions(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index 56c7fc4..acd6d84 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -11,7 +11,7 @@ import { ConfigurationSettingsSource } from "./settingsImport/configurationSetti import { ImportMode } from "./enums"; import { OperationTimeoutError, ArgumentError } from "./errors"; import { AdaptiveTaskManager } from "./internal/adaptiveTaskManager"; -import { ImportProgress, KeyLabelLookup } from "./models"; +import { ImportProgress, KeyLabelLookup, ConfigurationDiff } from "./models"; import { isConfigSettingEqual } from "./internal/utils"; import { v4 as uuidv4 } from "uuid"; import { Constants } from "./internal/constants"; @@ -43,7 +43,8 @@ export class AppConfigurationImporter { * @param timeout - Seconds of entire import progress timeout * @param progressCallback - Callback for report the progress of import * @param importMode - Determines the behavior when importing key-values. The default value, 'All' will import all key-values in the input file to App Configuration. 'Ignore-Match' will only import settings that have no matching key-value in App Configuration. - * @param dryRun - When dry run is enabled, no updates will be performed to App Configuration. Instead, any updates that would have been performed in a normal run will be printed to the console for review + * @param dryRun - When enabled, no updates will be performed to App Configuration. Returns a ConfigurationDiff object and prints changes to console for review. + * @returns ConfigurationDiff when dryRun=true, otherwise void */ public async Import( configSettingsSource: ConfigurationSettingsSource, @@ -52,7 +53,7 @@ export class AppConfigurationImporter { progressCallback?: (progress: ImportProgress) => unknown, importMode?: ImportMode, dryRun?: boolean - ) { + ): Promise { if (importMode == undefined) { importMode = ImportMode.IgnoreMatch; } @@ -61,9 +62,37 @@ export class AppConfigurationImporter { } this.validateImportMode(importMode); - const configSettings = await configSettingsSource.GetConfigurationSettings(); + const configurationDiff: ConfigurationDiff = await this.analyzeConfigurationChanges(configSettingsSource, strict, importMode); + + // Generate correlation ID for operations + const customCorrelationRequestId: string = uuidv4(); + const customHeadersOption: OperationOptions = { + requestOptions: { + customHeaders: { + [Constants.CorrelationRequestIdHeader]: customCorrelationRequestId + } + } + }; + + if (dryRun) { + this.printUpdatesToConsole(configurationDiff.Added, configurationDiff.Deleted); + return configurationDiff; + } + else { + await this.applyUpdatesToServer(configurationDiff.Added, configurationDiff.Deleted, timeout, customHeadersOption, progressCallback); + } + } + private async analyzeConfigurationChanges( + configSettingsSource: ConfigurationSettingsSource, + strict: boolean, + importMode: ImportMode + ): Promise { + const configSettings = await configSettingsSource.GetConfigurationSettings(); + const configurationSettingToDelete: ConfigurationSetting[] = []; + const configurationSettingToModify: SetConfigurationSettingParam[] = []; + const configurationSettingToAdd: SetConfigurationSettingParam[] = []; const srcKeyLabelLookUp: KeyLabelLookup = {}; configSettings.forEach((config: SetConfigurationSettingParam) => { @@ -73,7 +102,9 @@ export class AppConfigurationImporter { srcKeyLabelLookUp[config.key][config.label || ""] = true; }); - // generate correlationRequestId for operations in the same activity + configurationSettingToAdd.push(...configSettings); + + // Generate correlation ID for operations const customCorrelationRequestId: string = uuidv4(); const customHeadersOption: OperationOptions = { requestOptions: { @@ -83,32 +114,42 @@ export class AppConfigurationImporter { } }; - if (strict || importMode == ImportMode.IgnoreMatch) { - for await (const existing of this.configurationClient.listConfigurationSettings({...configSettingsSource.FilterOptions, ...customHeadersOption})) { - - const isKeyLabelPresent: boolean = srcKeyLabelLookUp[existing.key] && srcKeyLabelLookUp[existing.key][existing.label || ""]; + for await (const existing of this.configurationClient.listConfigurationSettings({...configSettingsSource.FilterOptions, ...customHeadersOption})) { + const isKeyLabelPresent: boolean = srcKeyLabelLookUp[existing.key] && srcKeyLabelLookUp[existing.key][existing.label || ""]; + if (strict && !isKeyLabelPresent) { + configurationSettingToDelete.push(existing); + } + + const incoming = configSettings.find(configSetting => configSetting.key == existing.key && + configSetting.label == existing.label); + + if (incoming) { + const settingsAreEqual: boolean = isConfigSettingEqual(incoming, existing); - if (strict && !isKeyLabelPresent) { - configurationSettingToDelete.push(existing); - } - - if (importMode == ImportMode.IgnoreMatch) { - const incoming = configSettings.find(configSetting => configSetting.key == existing.key && - configSetting.label == existing.label); - - if (incoming && isConfigSettingEqual(incoming, existing)) { - configSettings.splice(configSettings.indexOf(incoming), 1); + if (!settingsAreEqual) { + configurationSettingToModify.push(incoming); + // Remove from add list since it's a modification, not an addition + const addIndex: number = configurationSettingToAdd.findIndex(addSetting => + addSetting.key === incoming.key && addSetting.label === incoming.label); + if (addIndex !== -1) { + configurationSettingToAdd.splice(addIndex, 1); + } + } else if (importMode == ImportMode.IgnoreMatch) { + // Remove unchanged settings from add list + const addIndex = configurationSettingToAdd.findIndex(addSetting => + addSetting.key === incoming.key && addSetting.label === incoming.label); + if (addIndex !== -1) { + configurationSettingToAdd.splice(addIndex, 1); } } } } - - if (dryRun) { - this.printUpdatesToConsole(configSettings, configurationSettingToDelete); - } - else { - await this.applyUpdatesToServer(configSettings, configurationSettingToDelete, timeout, customHeadersOption, progressCallback); - } + + return { + Added: configurationSettingToAdd, + Modified: configurationSettingToModify, + Deleted: configurationSettingToDelete + }; } private printUpdatesToConsole( diff --git a/libraries/azure-app-configuration-importer/src/index.ts b/libraries/azure-app-configuration-importer/src/index.ts index f90bec9..db057ed 100644 --- a/libraries/azure-app-configuration-importer/src/index.ts +++ b/libraries/azure-app-configuration-importer/src/index.ts @@ -9,7 +9,7 @@ export { } from "./importOptions"; export * from "./enums"; export * from "./errors"; -export { ImportProgress as ImportResult } from "./models"; +export { ImportProgress as ImportResult, ConfigurationDiff, KvSetConfigurationItem } from "./models"; export { StringConfigurationSettingsSource } from "./settingsImport/stringConfigurationSettingsSource"; export { ConfigurationSettingsSource } from "./settingsImport/configurationSettingsSource"; export { IterableConfigurationSettingsSource } from "./settingsImport/iterableConfigurationSettingsSource"; diff --git a/libraries/azure-app-configuration-importer/src/models.ts b/libraries/azure-app-configuration-importer/src/models.ts index 7abb4a0..e020854 100644 --- a/libraries/azure-app-configuration-importer/src/models.ts +++ b/libraries/azure-app-configuration-importer/src/models.ts @@ -1,6 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import { + SecretReferenceValue, + ConfigurationSetting, + SetConfigurationSettingParam, + FeatureFlagValue +} from "@azure/app-configuration"; + /** * @internal */ @@ -42,4 +49,10 @@ export interface KeyLabelLookup { [key: string]: { [label: string] : boolean } +} + +export interface ConfigurationDiff { + Deleted: ConfigurationSetting[]; + Modified: SetConfigurationSettingParam[]; + Added: SetConfigurationSettingParam[]; } \ No newline at end of file From ff919a413a6b42b7ab76db2c22bbf06b468ca816 Mon Sep 17 00:00:00 2001 From: cwanjau Date: Thu, 30 Oct 2025 16:23:01 +0300 Subject: [PATCH 02/23] Fix lint issues --- .../src/appConfigurationImporter.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index acd6d84..56bd49f 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -134,7 +134,8 @@ export class AppConfigurationImporter { if (addIndex !== -1) { configurationSettingToAdd.splice(addIndex, 1); } - } else if (importMode == ImportMode.IgnoreMatch) { + } + else if (importMode == ImportMode.IgnoreMatch) { // Remove unchanged settings from add list const addIndex = configurationSettingToAdd.findIndex(addSetting => addSetting.key === incoming.key && addSetting.label === incoming.label); From bde8c6433a24fdfaef641bb4e0d61da53cdfa952 Mon Sep 17 00:00:00 2001 From: cwanjau Date: Mon, 3 Nov 2025 09:23:19 +0300 Subject: [PATCH 03/23] Fix failing tests --- .../src/appConfigurationImporter.ts | 23 ++++++------------- .../src/index.ts | 2 +- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index 56bd49f..456fc7c 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -61,9 +61,7 @@ export class AppConfigurationImporter { dryRun = false; } this.validateImportMode(importMode); - - const configurationDiff: ConfigurationDiff = await this.analyzeConfigurationChanges(configSettingsSource, strict, importMode); - + // Generate correlation ID for operations const customCorrelationRequestId: string = uuidv4(); const customHeadersOption: OperationOptions = { @@ -73,9 +71,11 @@ export class AppConfigurationImporter { } } }; - + + const configurationDiff: ConfigurationDiff = await this.analyzeConfigurationChanges(configSettingsSource, strict, importMode, customHeadersOption); + if (dryRun) { - this.printUpdatesToConsole(configurationDiff.Added, configurationDiff.Deleted); + this.printUpdatesToConsole([...configurationDiff.Added, ...configurationDiff.Modified], configurationDiff.Deleted); return configurationDiff; } else { @@ -86,7 +86,8 @@ export class AppConfigurationImporter { private async analyzeConfigurationChanges( configSettingsSource: ConfigurationSettingsSource, strict: boolean, - importMode: ImportMode + importMode: ImportMode, + customHeadersOption: OperationOptions ): Promise { const configSettings = await configSettingsSource.GetConfigurationSettings(); @@ -104,16 +105,6 @@ export class AppConfigurationImporter { configurationSettingToAdd.push(...configSettings); - // Generate correlation ID for operations - const customCorrelationRequestId: string = uuidv4(); - const customHeadersOption: OperationOptions = { - requestOptions: { - customHeaders: { - [Constants.CorrelationRequestIdHeader]: customCorrelationRequestId - } - } - }; - for await (const existing of this.configurationClient.listConfigurationSettings({...configSettingsSource.FilterOptions, ...customHeadersOption})) { const isKeyLabelPresent: boolean = srcKeyLabelLookUp[existing.key] && srcKeyLabelLookUp[existing.key][existing.label || ""]; if (strict && !isKeyLabelPresent) { diff --git a/libraries/azure-app-configuration-importer/src/index.ts b/libraries/azure-app-configuration-importer/src/index.ts index db057ed..792e1db 100644 --- a/libraries/azure-app-configuration-importer/src/index.ts +++ b/libraries/azure-app-configuration-importer/src/index.ts @@ -9,7 +9,7 @@ export { } from "./importOptions"; export * from "./enums"; export * from "./errors"; -export { ImportProgress as ImportResult, ConfigurationDiff, KvSetConfigurationItem } from "./models"; +export { ImportProgress as ImportResult, ConfigurationDiff } from "./models"; export { StringConfigurationSettingsSource } from "./settingsImport/stringConfigurationSettingsSource"; export { ConfigurationSettingsSource } from "./settingsImport/configurationSettingsSource"; export { IterableConfigurationSettingsSource } from "./settingsImport/iterableConfigurationSettingsSource"; From 5640e7192b5483301a6273ae831a05d4bc518dcd Mon Sep 17 00:00:00 2001 From: cwanjau Date: Mon, 3 Nov 2025 09:29:22 +0300 Subject: [PATCH 04/23] Rename function --- .../src/appConfigurationImporter.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index 456fc7c..1d07cf2 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -72,18 +72,18 @@ export class AppConfigurationImporter { } }; - const configurationDiff: ConfigurationDiff = await this.analyzeConfigurationChanges(configSettingsSource, strict, importMode, customHeadersOption); + const configurationDiff: ConfigurationDiff = await this.getConfigurationDiff(configSettingsSource, strict, importMode, customHeadersOption); if (dryRun) { this.printUpdatesToConsole([...configurationDiff.Added, ...configurationDiff.Modified], configurationDiff.Deleted); return configurationDiff; } else { - await this.applyUpdatesToServer(configurationDiff.Added, configurationDiff.Deleted, timeout, customHeadersOption, progressCallback); + await this.applyUpdatesToServer([...configurationDiff.Added, ...configurationDiff.Modified], configurationDiff.Deleted, timeout, customHeadersOption, progressCallback); } } - private async analyzeConfigurationChanges( + private async getConfigurationDiff( configSettingsSource: ConfigurationSettingsSource, strict: boolean, importMode: ImportMode, From 3dec8f01b9213b6e53da13d4e046842042030f4a Mon Sep 17 00:00:00 2001 From: cwanjau Date: Fri, 7 Nov 2025 11:05:19 +0300 Subject: [PATCH 05/23] Add new api to return diff to caller --- .../src/appConfigurationImporter.ts | 70 ++++++------ .../tests/appConfigurationImporter.spec.ts | 103 +++++------------- ...vSetConfigurationSettingsConverter.spec.ts | 34 +++--- 3 files changed, 80 insertions(+), 127 deletions(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index 1d07cf2..1960b06 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -43,23 +43,19 @@ export class AppConfigurationImporter { * @param timeout - Seconds of entire import progress timeout * @param progressCallback - Callback for report the progress of import * @param importMode - Determines the behavior when importing key-values. The default value, 'All' will import all key-values in the input file to App Configuration. 'Ignore-Match' will only import settings that have no matching key-value in App Configuration. - * @param dryRun - When enabled, no updates will be performed to App Configuration. Returns a ConfigurationDiff object and prints changes to console for review. - * @returns ConfigurationDiff when dryRun=true, otherwise void + * @returns void */ public async Import( configSettingsSource: ConfigurationSettingsSource, timeout: number, strict = false, progressCallback?: (progress: ImportProgress) => unknown, - importMode?: ImportMode, - dryRun?: boolean + importMode?: ImportMode ): Promise { if (importMode == undefined) { importMode = ImportMode.IgnoreMatch; } - if (dryRun == undefined) { - dryRun = false; - } + this.validateImportMode(importMode); // Generate correlation ID for operations @@ -72,23 +68,42 @@ export class AppConfigurationImporter { } }; - const configurationDiff: ConfigurationDiff = await this.getConfigurationDiff(configSettingsSource, strict, importMode, customHeadersOption); + const configurationDiff: ConfigurationDiff = await this.getConfigurationChanges(configSettingsSource, strict, importMode, customHeadersOption); - if (dryRun) { - this.printUpdatesToConsole([...configurationDiff.Added, ...configurationDiff.Modified], configurationDiff.Deleted); - return configurationDiff; - } - else { - await this.applyUpdatesToServer([...configurationDiff.Added, ...configurationDiff.Modified], configurationDiff.Deleted, timeout, customHeadersOption, progressCallback); - } + await this.applyUpdatesToServer([...configurationDiff.Added, ...configurationDiff.Modified], configurationDiff.Deleted, timeout, customHeadersOption, progressCallback); } - private async getConfigurationDiff( + /** + * Get configuration differences between source settings and Azure App Configuration service without any applying changes + * + * Example usage: + * ```ts + * const fileData = fs.readFileSync("mylocalPath").toString(); + * const diff = await client.getConfigurationChanges( + * new StringConfigurationSettingsSource({data:fileData, format: ConfigurationFormat.Json}), + * false, + * ImportMode.All, + * options + * ); + * ``` + * @param configSettingsSource - A ConfigurationSettingsSource instance. + * @param strict - Use strict mode to delete settings not in source. + * @param importMode - Determines the behavior when analyzing key-values. 'All' will include all key-values. 'Ignore-Match' will exclude settings that have matching key-values in App Configuration. + * @param customHeadersOption - Custom headers for the operation. + * @returns ConfigurationDiff object containing Added, Modified, and Deleted settings + */ + public async getConfigurationChanges( configSettingsSource: ConfigurationSettingsSource, - strict: boolean, - importMode: ImportMode, - customHeadersOption: OperationOptions + strict: boolean = false, + importMode?: ImportMode, + customHeadersOption?: OperationOptions ): Promise { + if (importMode == undefined) { + importMode = ImportMode.IgnoreMatch; + } + + this.validateImportMode(importMode); + const configSettings = await configSettingsSource.GetConfigurationSettings(); const configurationSettingToDelete: ConfigurationSetting[] = []; @@ -144,23 +159,6 @@ export class AppConfigurationImporter { }; } - private printUpdatesToConsole( - settingsToAdd: SetConfigurationSettingParam[], - settingsToDelete: ConfigurationSetting[] - ): void { - console.log("The following settings will be removed from App Configuration:"); - for (const setting of settingsToDelete) { - - console.log(JSON.stringify({key: setting.key, label: setting.label, contentType: setting.contentType, tags: setting.tags})); - } - - console.log("\nThe following settings will be written to App Configuration:"); - for (const setting of settingsToAdd) { - - console.log(JSON.stringify({key: setting.key, label: setting.label, contentType: setting.contentType, tags: setting.tags})); - } - } - private async applyUpdatesToServer( settingsToAdd: SetConfigurationSettingParam[], settingsToDelete: ConfigurationSetting[], diff --git a/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts b/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts index 7491a9c..1be2430 100644 --- a/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts @@ -231,22 +231,16 @@ describe("Call Import API to import configuration file to AppConfiguration", () assert.equal(total, 3); }); - describe("Call Import API to import configurations from file and pass dryRun and Import mode options",async()=>{ - let spy: sinon.SinonSpy; + describe("Call getConfigurationChanges API to get configurations changes from file and pass Import mode options", () => { let appConfigurationImporter: AppConfigurationImporter; beforeEach(function () { - spy = sinon.spy(console,"log"); const AppConfigurationClientStub = sinon.createStubInstance(AppConfigurationClient); AppConfigurationClientStub.listConfigurationSettings.returns(listConfigurationSettings()); appConfigurationImporter = new AppConfigurationImporter(AppConfigurationClientStub); }); - afterEach(function () { - spy.restore(); - }); - - it("Succeed to import and log all key-values with importMode as All, profile as default", async()=>{ + it("Succeed to get configuration changes with importMode as All, profile as default", async () => { const options = { data: fs.readFileSync(path.join("__dirname", "../tests/sources/default.json")).toString(), format: ConfigurationFormat.Json, @@ -254,26 +248,15 @@ describe("Call Import API to import configuration file to AppConfiguration", () label: "Dev", separator: ":" }; - const stringConfigurationSource = new StringConfigurationSettingsSource(options); - let finished = 0; - let total = 0; - const reportImportProgress = (importProgress: ImportProgress) => { - finished = importProgress.successCount; - total = importProgress.importCount; - }; - await appConfigurationImporter.Import(stringConfigurationSource, 3, false, reportImportProgress, ImportMode.All, true); - - // All key-values in App Configuration will be updated - assert.equal(spy.getCall(1).args[0], "\nThe following settings will be written to App Configuration:"); - assert.equal(spy.getCall(2).args[0], "{\"key\":\"app:Settings:FontSize\",\"label\":\"Dev\"}"); - assert.equal(spy.getCall(3).args[0], "{\"key\":\"app:Settings:BackgroundColor\",\"label\":\"Dev\"}"); - assert.equal(spy.getCall(4).args[0], "{\"key\":\"app:Settings:FontColor\",\"label\":\"Dev\"}"); - assert.equal(finished, 0); - assert.equal(total, 0); + const configurationChanges = await appConfigurationImporter.getConfigurationChanges(stringConfigurationSource, false, ImportMode.All); + assert.equal(configurationChanges.Added.length, 2); + assert.equal(configurationChanges.Modified.length, 1); + assert.equal(configurationChanges.Deleted.length, 0); + assert.equal(configurationChanges.Modified[0].key, "app:Settings:FontColor"); }); - it("Succeed to import and log no matching key values updates with importMode as IgnoreMatch and profile as default", async()=>{ + it("Succeed to get configuration changes and return no matching key values updates with importMode as IgnoreMatch and profile as default", async () => { const options = { data: fs.readFileSync(path.join("__dirname", "../tests/sources/default.json")).toString(), format: ConfigurationFormat.Json, @@ -281,72 +264,46 @@ describe("Call Import API to import configuration file to AppConfiguration", () label: "Dev", separator: ":" }; - - const stringConfigurationSource = new StringConfigurationSettingsSource(options); - let finished = 0; - let total = 0; - const reportImportProgress = (importProgress: ImportProgress) => { - finished = importProgress.successCount; - total = importProgress.importCount; - }; - await appConfigurationImporter.Import(stringConfigurationSource, 3, false, reportImportProgress, ImportMode.IgnoreMatch, true); - + const source = new StringConfigurationSettingsSource(options); + const configurationChanges = await appConfigurationImporter.getConfigurationChanges(source, false, ImportMode.IgnoreMatch); // Only keys with no matching key-values in App Configuration will be updated - assert.equal(spy.getCall(1).args[0], "\nThe following settings will be written to App Configuration:"); - assert.equal(spy.getCall(2).args[0], "{\"key\":\"app:Settings:FontColor\",\"label\":\"Dev\"}"); - assert.equal(finished, 0); - assert.equal(total, 0); + assert.equal(configurationChanges.Added.length, 0); + assert.equal(configurationChanges.Modified.length, 1); + assert.equal(configurationChanges.Modified[0].key, "app:Settings:FontColor"); + assert.equal(configurationChanges.Deleted.length, 0); }); - it("Succeed to import key-values file with importMode as All and profile as kvset", async()=>{ + it("Succeed to get configuration changes from key-values file with importMode as All and profile as kvset", async () => { const options = { data: fs.readFileSync(path.join("__dirname", "../tests/sources/kvset.json")).toString(), format: ConfigurationFormat.Json, profile: ConfigurationProfile.KvSet }; - const stringConfigurationSource = new StringConfigurationSettingsSource(options); - let finished = 0; - let total = 0; - const reportImportProgress = (importProgress: ImportProgress) => { - finished = importProgress.successCount; - total = importProgress.importCount; - }; - await appConfigurationImporter.Import(stringConfigurationSource, 3, false, reportImportProgress, ImportMode.All, true); - + const configurationChanges = await appConfigurationImporter.getConfigurationChanges(stringConfigurationSource, false, ImportMode.All); //All key-values in App Configuration will be updated - assert.equal(spy.getCall(1).args[0], "\nThe following settings will be written to App Configuration:"); - assert.equal(spy.getCall(2).args[0], "{\"key\":\".appconfig.featureflag/Test\",\"label\":\"dev\",\"contentType\":\"application/vnd.microsoft.appconfig.ff+json;charset=utf-8\",\"tags\":{}}"); - assert.equal(spy.getCall(3).args[0], "{\"key\":\"Database:ConnectionString\",\"label\":\"test\",\"contentType\":\"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8\",\"tags\":{}}"); - assert.equal(spy.getCall(4).args[0], "{\"key\":\"TestEnv\",\"label\":\"dev\",\"contentType\":null,\"tags\":{\"tag1\":\"value1\",\"tag2\":\"value2\"}}"); - assert.equal(finished, 0); - assert.equal(total, 0); + assert.equal(configurationChanges.Added.length, 2); + assert.equal(configurationChanges.Modified.length, 1); + assert.equal(configurationChanges.Modified[0].key, "TestEnv"); + assert.equal(configurationChanges.Deleted.length, 0); }); - it("Succeed to import key-values and log no matching key values with importMode as IgnoreMatch and profile as kvset", async()=>{ + it("Succeed to get configuration changes and return no matching key values with importMode as IgnoreMatch and profile as kvset", async () => { const options = { data: fs.readFileSync(path.join("__dirname", "../tests/sources/kvset.json")).toString(), format: ConfigurationFormat.Json, profile: ConfigurationProfile.KvSet }; - - const stringConfigurationSource = new StringConfigurationSettingsSource(options); - let finished = 0; - let total = 0; - const reportImportProgress = (importProgress: ImportProgress) => { - finished = importProgress.successCount; - total = importProgress.importCount; - }; - await appConfigurationImporter.Import(stringConfigurationSource, 3, false, reportImportProgress, ImportMode.IgnoreMatch, true); - - //Only keys with no matching key-values in App Configuration will be updated - assert.equal(spy.getCall(1).args[0], "\nThe following settings will be written to App Configuration:"); - assert.equal(spy.getCall(2).args[0], "{\"key\":\"TestEnv\",\"label\":\"dev\",\"contentType\":null,\"tags\":{\"tag1\":\"value1\",\"tag2\":\"value2\"}}"); - assert.equal(finished, 0); - assert.equal(total, 0); + const source = new StringConfigurationSettingsSource(options); + const configurationChanges = await appConfigurationImporter.getConfigurationChanges(source, false, ImportMode.IgnoreMatch); + // Only changed key (TestEnv) should be in Modified + assert.equal(configurationChanges.Added.length, 0); + assert.equal(configurationChanges.Modified.length, 1); + assert.equal(configurationChanges.Modified[0].key, "TestEnv"); + assert.equal(configurationChanges.Deleted.length, 0); }); - it("Fail when an invalid import mode is provided", async()=> { + it("Fail when an invalid import mode is provided", async () => { const options = { data: fs.readFileSync(path.join("__dirname", "../tests/sources/kvset.json")).toString(), format: ConfigurationFormat.Json, @@ -355,7 +312,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () const stringConfigurationSource = new StringConfigurationSettingsSource(options); try { - await appConfigurationImporter.Import(stringConfigurationSource, 3, false, undefined, 9, false); + await appConfigurationImporter.getConfigurationChanges(stringConfigurationSource, false, 9 as unknown as ImportMode); } catch (error) { assert.isTrue(error instanceof ArgumentError); diff --git a/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts b/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts index 944d109..2f5ff98 100644 --- a/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts @@ -190,7 +190,6 @@ describe("Parse kvset format file", () => { }); it("Delete key-values present in the store but not available in the config file", async()=>{ - const spy = sinon.spy(console,"log"); const AppConfigurationClientStub = sinon.createStubInstance(AppConfigurationClient); AppConfigurationClientStub.listConfigurationSettings.returns(listConfigurationSettings()); const appConfigurationImporter = new AppConfigurationImporter(AppConfigurationClientStub); @@ -201,18 +200,15 @@ describe("Parse kvset format file", () => { }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); - await appConfigurationImporter.Import(stringConfigurationSource, 3, true, undefined, ImportMode.All, true); - + const configurationChanges = await appConfigurationImporter.getConfigurationChanges(stringConfigurationSource, true, ImportMode.All); + // The keys present in the store and not in the configuration file are deleted if strict is set to true - assert.equal(spy.getCall(0).args[0], "The following settings will be removed from App Configuration:"); - assert.equal(spy.getCall(1).args[0], "{\"key\":\"app:Settings:FontSize\",\"label\":\"Dev\"}"); - assert.equal(spy.getCall(2).args[0], "{\"key\":\"app:Settings:BackgroundColor\",\"label\":\"Dev\",\"tags\":{}}"); - assert.equal(spy.getCall(3).args[0], "{\"key\":\"app:Settings:FontColor\",\"label\":\"Dev\",\"tags\":{\"tag1\":\"value1\",\"tag2\":\"value2\"}}"); - spy.restore(); + const deletedKeys = configurationChanges.Deleted.map(d => d.key); + assert.equal(configurationChanges.Deleted.length, 3); + assert.includeMembers(deletedKeys, ["app:Settings:FontSize", "app:Settings:BackgroundColor", "app:Settings:FontColor"]); }); it("Delete key-values present in the store but not available in the config file, with similar key but different label", async()=>{ - const spy = sinon.spy(console,"log"); const AppConfigurationClientStub = sinon.createStubInstance(AppConfigurationClient); AppConfigurationClientStub.listConfigurationSettings.returns(listConfigurationSettings()); const appConfigurationImporter = new AppConfigurationImporter(AppConfigurationClientStub); @@ -223,15 +219,17 @@ describe("Parse kvset format file", () => { }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); - await appConfigurationImporter.Import(stringConfigurationSource, 3, true, undefined, ImportMode.All, true); - + const configurationChanges = await appConfigurationImporter.getConfigurationChanges(stringConfigurationSource, true, ImportMode.All); + // The keys present in the store and not in the configuration file are deleted if strict is set to true - assert.equal(spy.getCall(0).args[0], "The following settings will be removed from App Configuration:"); - assert.equal(spy.getCall(1).args[0], "{\"key\":\"app:Settings:FontSize\",\"label\":\"Dev\"}"); - assert.equal(spy.getCall(2).args[0], "{\"key\":\"app:Settings:BackgroundColor\",\"label\":\"Dev\",\"tags\":{}}"); - assert.equal(spy.getCall(3).args[0], "{\"key\":\"app:Settings:FontColor\",\"label\":\"Dev\",\"tags\":{\"tag1\":\"value1\",\"tag2\":\"value2\"}}"); - assert.equal(spy.getCall(4).args[0], "{\"key\":\".appconfig.featureflag/Test\",\"label\":\"dev\",\"contentType\":\"application/vnd.microsoft.appconfig.ff+json;charset=utf-8\",\"tags\":{}}"); - assert.equal(spy.getCall(5).args[0], "{\"key\":\"Database:ConnectionString\",\"label\":\"test\",\"contentType\":\"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8\"}"); - spy.restore(); + const deletedKeys = configurationChanges.Deleted.map(d => `key: ${d.key}, label: ${d.label || ''}`); + assert.equal(configurationChanges.Deleted.length, 5); + assert.includeMembers(deletedKeys, [ + "key: app:Settings:FontSize, label: Dev", + "key: app:Settings:BackgroundColor, label: Dev", + "key: app:Settings:FontColor, label: Dev", + "key: .appconfig.featureflag/Test, label: dev", + "key: Database:ConnectionString, label: test" + ]); }); }); From d0f60b6b3627ab1443f1b5ce9c124101e9344442 Mon Sep 17 00:00:00 2001 From: cwanjau Date: Fri, 7 Nov 2025 11:09:27 +0300 Subject: [PATCH 06/23] Update variable name --- .../src/appConfigurationImporter.ts | 16 ++++++++-------- .../src/index.ts | 2 +- .../src/models.ts | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index 1960b06..34a26e6 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -11,7 +11,7 @@ import { ConfigurationSettingsSource } from "./settingsImport/configurationSetti import { ImportMode } from "./enums"; import { OperationTimeoutError, ArgumentError } from "./errors"; import { AdaptiveTaskManager } from "./internal/adaptiveTaskManager"; -import { ImportProgress, KeyLabelLookup, ConfigurationDiff } from "./models"; +import { ImportProgress, KeyLabelLookup, ConfigurationChanges } from "./models"; import { isConfigSettingEqual } from "./internal/utils"; import { v4 as uuidv4 } from "uuid"; import { Constants } from "./internal/constants"; @@ -51,7 +51,7 @@ export class AppConfigurationImporter { strict = false, progressCallback?: (progress: ImportProgress) => unknown, importMode?: ImportMode - ): Promise { + ): Promise { if (importMode == undefined) { importMode = ImportMode.IgnoreMatch; } @@ -68,18 +68,18 @@ export class AppConfigurationImporter { } }; - const configurationDiff: ConfigurationDiff = await this.getConfigurationChanges(configSettingsSource, strict, importMode, customHeadersOption); + const configurationChanges: ConfigurationChanges = await this.getConfigurationChanges(configSettingsSource, strict, importMode, customHeadersOption); - await this.applyUpdatesToServer([...configurationDiff.Added, ...configurationDiff.Modified], configurationDiff.Deleted, timeout, customHeadersOption, progressCallback); + await this.applyUpdatesToServer([...configurationChanges.Added, ...configurationChanges.Modified], configurationChanges.Deleted, timeout, customHeadersOption, progressCallback); } /** - * Get configuration differences between source settings and Azure App Configuration service without any applying changes + * Get configuration changes between source settings and Azure App Configuration service without any applying changes * * Example usage: * ```ts * const fileData = fs.readFileSync("mylocalPath").toString(); - * const diff = await client.getConfigurationChanges( + * const configurationChanges = await client.getConfigurationChanges( * new StringConfigurationSettingsSource({data:fileData, format: ConfigurationFormat.Json}), * false, * ImportMode.All, @@ -90,14 +90,14 @@ export class AppConfigurationImporter { * @param strict - Use strict mode to delete settings not in source. * @param importMode - Determines the behavior when analyzing key-values. 'All' will include all key-values. 'Ignore-Match' will exclude settings that have matching key-values in App Configuration. * @param customHeadersOption - Custom headers for the operation. - * @returns ConfigurationDiff object containing Added, Modified, and Deleted settings + * @returns ConfigurationChanges object containing Added, Modified, and Deleted settings */ public async getConfigurationChanges( configSettingsSource: ConfigurationSettingsSource, strict: boolean = false, importMode?: ImportMode, customHeadersOption?: OperationOptions - ): Promise { + ): Promise { if (importMode == undefined) { importMode = ImportMode.IgnoreMatch; } diff --git a/libraries/azure-app-configuration-importer/src/index.ts b/libraries/azure-app-configuration-importer/src/index.ts index 792e1db..7258ec6 100644 --- a/libraries/azure-app-configuration-importer/src/index.ts +++ b/libraries/azure-app-configuration-importer/src/index.ts @@ -9,7 +9,7 @@ export { } from "./importOptions"; export * from "./enums"; export * from "./errors"; -export { ImportProgress as ImportResult, ConfigurationDiff } from "./models"; +export { ImportProgress as ImportResult, ConfigurationChanges } from "./models"; export { StringConfigurationSettingsSource } from "./settingsImport/stringConfigurationSettingsSource"; export { ConfigurationSettingsSource } from "./settingsImport/configurationSettingsSource"; export { IterableConfigurationSettingsSource } from "./settingsImport/iterableConfigurationSettingsSource"; diff --git a/libraries/azure-app-configuration-importer/src/models.ts b/libraries/azure-app-configuration-importer/src/models.ts index e020854..cfca08b 100644 --- a/libraries/azure-app-configuration-importer/src/models.ts +++ b/libraries/azure-app-configuration-importer/src/models.ts @@ -51,7 +51,7 @@ export interface KeyLabelLookup { } } -export interface ConfigurationDiff { +export interface ConfigurationChanges { Deleted: ConfigurationSetting[]; Modified: SetConfigurationSettingParam[]; Added: SetConfigurationSettingParam[]; From 0c445190ffb277c4d32e10162352d4d06072fcfa Mon Sep 17 00:00:00 2001 From: cwanjau Date: Fri, 7 Nov 2025 11:15:15 +0300 Subject: [PATCH 07/23] Fix linting issues --- .../src/appConfigurationImporter.ts | 2 +- .../tests/appConfigurationImporter.spec.ts | 12 ++++++------ .../kvSetConfigurationSettingsConverter.spec.ts | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index 34a26e6..4bf5185 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -94,7 +94,7 @@ export class AppConfigurationImporter { */ public async getConfigurationChanges( configSettingsSource: ConfigurationSettingsSource, - strict: boolean = false, + strict = false, importMode?: ImportMode, customHeadersOption?: OperationOptions ): Promise { diff --git a/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts b/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts index 1be2430..552b5b5 100644 --- a/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts @@ -240,7 +240,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () appConfigurationImporter = new AppConfigurationImporter(AppConfigurationClientStub); }); - it("Succeed to get configuration changes with importMode as All, profile as default", async () => { + it("Succeed to get configuration changes with importMode as All, profile as default", async () => { const options = { data: fs.readFileSync(path.join("__dirname", "../tests/sources/default.json")).toString(), format: ConfigurationFormat.Json, @@ -256,7 +256,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () assert.equal(configurationChanges.Modified[0].key, "app:Settings:FontColor"); }); - it("Succeed to get configuration changes and return no matching key values updates with importMode as IgnoreMatch and profile as default", async () => { + it("Succeed to get configuration changes and return no matching key values updates with importMode as IgnoreMatch and profile as default", async () => { const options = { data: fs.readFileSync(path.join("__dirname", "../tests/sources/default.json")).toString(), format: ConfigurationFormat.Json, @@ -273,7 +273,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () assert.equal(configurationChanges.Deleted.length, 0); }); - it("Succeed to get configuration changes from key-values file with importMode as All and profile as kvset", async () => { + it("Succeed to get configuration changes from key-values file with importMode as All and profile as kvset", async () => { const options = { data: fs.readFileSync(path.join("__dirname", "../tests/sources/kvset.json")).toString(), format: ConfigurationFormat.Json, @@ -288,7 +288,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () assert.equal(configurationChanges.Deleted.length, 0); }); - it("Succeed to get configuration changes and return no matching key values with importMode as IgnoreMatch and profile as kvset", async () => { + it("Succeed to get configuration changes and return no matching key values with importMode as IgnoreMatch and profile as kvset", async () => { const options = { data: fs.readFileSync(path.join("__dirname", "../tests/sources/kvset.json")).toString(), format: ConfigurationFormat.Json, @@ -303,14 +303,14 @@ describe("Call Import API to import configuration file to AppConfiguration", () assert.equal(configurationChanges.Deleted.length, 0); }); - it("Fail when an invalid import mode is provided", async () => { + it("Fail when an invalid import mode is provided", async () => { const options = { data: fs.readFileSync(path.join("__dirname", "../tests/sources/kvset.json")).toString(), format: ConfigurationFormat.Json, profile: ConfigurationProfile.KvSet }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); - + try { await appConfigurationImporter.getConfigurationChanges(stringConfigurationSource, false, 9 as unknown as ImportMode); } diff --git a/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts b/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts index 2f5ff98..f3a0c87 100644 --- a/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts @@ -222,7 +222,7 @@ describe("Parse kvset format file", () => { const configurationChanges = await appConfigurationImporter.getConfigurationChanges(stringConfigurationSource, true, ImportMode.All); // The keys present in the store and not in the configuration file are deleted if strict is set to true - const deletedKeys = configurationChanges.Deleted.map(d => `key: ${d.key}, label: ${d.label || ''}`); + const deletedKeys = configurationChanges.Deleted.map(d => `key: ${d.key}, label: ${d.label}`); assert.equal(configurationChanges.Deleted.length, 5); assert.includeMembers(deletedKeys, [ "key: app:Settings:FontSize, label: Dev", From be233835a3743ff2142e8414986cf6d7d6b33e36 Mon Sep 17 00:00:00 2001 From: cwanjau Date: Mon, 10 Nov 2025 10:39:46 +0300 Subject: [PATCH 08/23] Address PR comments --- .../package.json | 4 ++-- .../package.json | 2 +- .../src/appConfigurationImporter.ts | 20 +++++++++---------- .../tests/appConfigurationImporter.spec.ts | 14 ++++++------- ...vSetConfigurationSettingsConverter.spec.ts | 4 ++-- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/libraries/azure-app-configuration-importer-file-source/package.json b/libraries/azure-app-configuration-importer-file-source/package.json index e338ede..6fbd433 100644 --- a/libraries/azure-app-configuration-importer-file-source/package.json +++ b/libraries/azure-app-configuration-importer-file-source/package.json @@ -2,7 +2,7 @@ "name": "@azure/app-configuration-importer-file-source", "author": "Microsoft Corporation", "description": "A client library for importing/exporting key-values between configuration file sources and Azure App Configuration service", - "version": "1.1.3-preview", + "version": "2.1.3-preview", "sdk-type": "client", "keywords": [ "node", @@ -33,7 +33,7 @@ }, "dependencies": { "@azure/app-configuration": "^1.9.0", - "@azure/app-configuration-importer": "1.1.3-preview" + "@azure/app-configuration-importer": "2.1.3-preview" }, "devDependencies": { "@microsoft/api-extractor": "^7.22.2", diff --git a/libraries/azure-app-configuration-importer/package.json b/libraries/azure-app-configuration-importer/package.json index 727fbb8..9832c21 100644 --- a/libraries/azure-app-configuration-importer/package.json +++ b/libraries/azure-app-configuration-importer/package.json @@ -2,7 +2,7 @@ "name": "@azure/app-configuration-importer", "author": "Microsoft Corporation", "description": "A client library for importing/exporting key-values between configuration sources and Azure App Configuration service", - "version": "1.1.3-preview", + "version": "2.1.3-preview", "sdk-type": "client", "keywords": [ "node", diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index 4bf5185..5519dc6 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -52,7 +52,7 @@ export class AppConfigurationImporter { progressCallback?: (progress: ImportProgress) => unknown, importMode?: ImportMode ): Promise { - if (importMode == undefined) { + if (importMode === undefined) { importMode = ImportMode.IgnoreMatch; } @@ -68,7 +68,7 @@ export class AppConfigurationImporter { } }; - const configurationChanges: ConfigurationChanges = await this.getConfigurationChanges(configSettingsSource, strict, importMode, customHeadersOption); + const configurationChanges: ConfigurationChanges = await this.GetConfigurationChanges(configSettingsSource, strict, importMode, customHeadersOption); await this.applyUpdatesToServer([...configurationChanges.Added, ...configurationChanges.Modified], configurationChanges.Deleted, timeout, customHeadersOption, progressCallback); } @@ -88,11 +88,13 @@ export class AppConfigurationImporter { * ``` * @param configSettingsSource - A ConfigurationSettingsSource instance. * @param strict - Use strict mode to delete settings not in source. - * @param importMode - Determines the behavior when analyzing key-values. 'All' will include all key-values. 'Ignore-Match' will exclude settings that have matching key-values in App Configuration. + * @param importMode - Determines the behavior when analyzing key-values. + * 'All' will include all key-values. + * 'Ignore-Match' will exclude settings that have matching key-values in App Configuration. * @param customHeadersOption - Custom headers for the operation. * @returns ConfigurationChanges object containing Added, Modified, and Deleted settings */ - public async getConfigurationChanges( + public async GetConfigurationChanges( configSettingsSource: ConfigurationSettingsSource, strict = false, importMode?: ImportMode, @@ -127,7 +129,7 @@ export class AppConfigurationImporter { } const incoming = configSettings.find(configSetting => configSetting.key == existing.key && - configSetting.label == existing.label); + configSetting.label === existing.label); if (incoming) { const settingsAreEqual: boolean = isConfigSettingEqual(incoming, existing); @@ -135,16 +137,14 @@ export class AppConfigurationImporter { if (!settingsAreEqual) { configurationSettingToModify.push(incoming); // Remove from add list since it's a modification, not an addition - const addIndex: number = configurationSettingToAdd.findIndex(addSetting => - addSetting.key === incoming.key && addSetting.label === incoming.label); + const addIndex = configurationSettingToAdd.indexOf(incoming); if (addIndex !== -1) { configurationSettingToAdd.splice(addIndex, 1); } } - else if (importMode == ImportMode.IgnoreMatch) { + else if (importMode === ImportMode.IgnoreMatch) { // Remove unchanged settings from add list - const addIndex = configurationSettingToAdd.findIndex(addSetting => - addSetting.key === incoming.key && addSetting.label === incoming.label); + const addIndex = configurationSettingToAdd.indexOf(incoming); if (addIndex !== -1) { configurationSettingToAdd.splice(addIndex, 1); } diff --git a/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts b/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts index 552b5b5..ba9d8af 100644 --- a/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts @@ -231,7 +231,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () assert.equal(total, 3); }); - describe("Call getConfigurationChanges API to get configurations changes from file and pass Import mode options", () => { + describe("Call GetConfigurationChanges API to get configurations changes from file and pass Import mode options", () => { let appConfigurationImporter: AppConfigurationImporter; beforeEach(function () { @@ -249,7 +249,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () separator: ":" }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); - const configurationChanges = await appConfigurationImporter.getConfigurationChanges(stringConfigurationSource, false, ImportMode.All); + const configurationChanges = await appConfigurationImporter.GetConfigurationChanges(stringConfigurationSource, false, ImportMode.All); assert.equal(configurationChanges.Added.length, 2); assert.equal(configurationChanges.Modified.length, 1); assert.equal(configurationChanges.Deleted.length, 0); @@ -265,7 +265,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () separator: ":" }; const source = new StringConfigurationSettingsSource(options); - const configurationChanges = await appConfigurationImporter.getConfigurationChanges(source, false, ImportMode.IgnoreMatch); + const configurationChanges = await appConfigurationImporter.GetConfigurationChanges(source, false, ImportMode.IgnoreMatch); // Only keys with no matching key-values in App Configuration will be updated assert.equal(configurationChanges.Added.length, 0); assert.equal(configurationChanges.Modified.length, 1); @@ -280,8 +280,8 @@ describe("Call Import API to import configuration file to AppConfiguration", () profile: ConfigurationProfile.KvSet }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); - const configurationChanges = await appConfigurationImporter.getConfigurationChanges(stringConfigurationSource, false, ImportMode.All); - //All key-values in App Configuration will be updated + const configurationChanges = await appConfigurationImporter.GetConfigurationChanges(stringConfigurationSource, false, ImportMode.All); + // All key-values in App Configuration will be updated assert.equal(configurationChanges.Added.length, 2); assert.equal(configurationChanges.Modified.length, 1); assert.equal(configurationChanges.Modified[0].key, "TestEnv"); @@ -295,7 +295,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () profile: ConfigurationProfile.KvSet }; const source = new StringConfigurationSettingsSource(options); - const configurationChanges = await appConfigurationImporter.getConfigurationChanges(source, false, ImportMode.IgnoreMatch); + const configurationChanges = await appConfigurationImporter.GetConfigurationChanges(source, false, ImportMode.IgnoreMatch); // Only changed key (TestEnv) should be in Modified assert.equal(configurationChanges.Added.length, 0); assert.equal(configurationChanges.Modified.length, 1); @@ -312,7 +312,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () const stringConfigurationSource = new StringConfigurationSettingsSource(options); try { - await appConfigurationImporter.getConfigurationChanges(stringConfigurationSource, false, 9 as unknown as ImportMode); + await appConfigurationImporter.GetConfigurationChanges(stringConfigurationSource, false, 9 as unknown as ImportMode); } catch (error) { assert.isTrue(error instanceof ArgumentError); diff --git a/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts b/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts index f3a0c87..db80a9a 100644 --- a/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts @@ -200,7 +200,7 @@ describe("Parse kvset format file", () => { }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); - const configurationChanges = await appConfigurationImporter.getConfigurationChanges(stringConfigurationSource, true, ImportMode.All); + const configurationChanges = await appConfigurationImporter.GetConfigurationChanges(stringConfigurationSource, true, ImportMode.All); // The keys present in the store and not in the configuration file are deleted if strict is set to true const deletedKeys = configurationChanges.Deleted.map(d => d.key); @@ -219,7 +219,7 @@ describe("Parse kvset format file", () => { }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); - const configurationChanges = await appConfigurationImporter.getConfigurationChanges(stringConfigurationSource, true, ImportMode.All); + const configurationChanges = await appConfigurationImporter.GetConfigurationChanges(stringConfigurationSource, true, ImportMode.All); // The keys present in the store and not in the configuration file are deleted if strict is set to true const deletedKeys = configurationChanges.Deleted.map(d => `key: ${d.key}, label: ${d.label}`); From 4a7085767982df9aee5276fc727bc1b6e7cabf17 Mon Sep 17 00:00:00 2001 From: cwanjau Date: Mon, 10 Nov 2025 10:42:21 +0300 Subject: [PATCH 09/23] Address PR comments --- .../src/appConfigurationImporter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index 5519dc6..a87d761 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -43,7 +43,7 @@ export class AppConfigurationImporter { * @param timeout - Seconds of entire import progress timeout * @param progressCallback - Callback for report the progress of import * @param importMode - Determines the behavior when importing key-values. The default value, 'All' will import all key-values in the input file to App Configuration. 'Ignore-Match' will only import settings that have no matching key-value in App Configuration. - * @returns void + * @returns Promise */ public async Import( configSettingsSource: ConfigurationSettingsSource, From 8d5f1cd7a2918e9cb9cde957d555589ccac2d21c Mon Sep 17 00:00:00 2001 From: cwanjau Date: Mon, 17 Nov 2025 12:09:46 +0300 Subject: [PATCH 10/23] address comments --- .../src/appConfigurationImporter.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index a87d761..c74adeb 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -106,6 +106,18 @@ export class AppConfigurationImporter { this.validateImportMode(importMode); + // Generate correlation ID + if (!customHeadersOption) { + const customCorrelationRequestId: string = uuidv4(); + customHeadersOption = { + requestOptions: { + customHeaders: { + [Constants.CorrelationRequestIdHeader]: customCorrelationRequestId + } + } + }; + } + const configSettings = await configSettingsSource.GetConfigurationSettings(); const configurationSettingToDelete: ConfigurationSetting[] = []; From 04e88e4453fe6403bdbcaae06b2848f861086175 Mon Sep 17 00:00:00 2001 From: cwanjau Date: Thu, 20 Nov 2025 11:24:30 +0300 Subject: [PATCH 11/23] address comments --- .../package.json | 4 ++-- .../azure-app-configuration-importer/package.json | 2 +- .../src/appConfigurationImporter.ts | 12 ++---------- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/libraries/azure-app-configuration-importer-file-source/package.json b/libraries/azure-app-configuration-importer-file-source/package.json index 6fbd433..c9daac6 100644 --- a/libraries/azure-app-configuration-importer-file-source/package.json +++ b/libraries/azure-app-configuration-importer-file-source/package.json @@ -2,7 +2,7 @@ "name": "@azure/app-configuration-importer-file-source", "author": "Microsoft Corporation", "description": "A client library for importing/exporting key-values between configuration file sources and Azure App Configuration service", - "version": "2.1.3-preview", + "version": "2.0.0-preview", "sdk-type": "client", "keywords": [ "node", @@ -33,7 +33,7 @@ }, "dependencies": { "@azure/app-configuration": "^1.9.0", - "@azure/app-configuration-importer": "2.1.3-preview" + "@azure/app-configuration-importer": "2.0.0-preview" }, "devDependencies": { "@microsoft/api-extractor": "^7.22.2", diff --git a/libraries/azure-app-configuration-importer/package.json b/libraries/azure-app-configuration-importer/package.json index 9832c21..a9cafd0 100644 --- a/libraries/azure-app-configuration-importer/package.json +++ b/libraries/azure-app-configuration-importer/package.json @@ -2,7 +2,7 @@ "name": "@azure/app-configuration-importer", "author": "Microsoft Corporation", "description": "A client library for importing/exporting key-values between configuration sources and Azure App Configuration service", - "version": "2.1.3-preview", + "version": "2.0.0-preview", "sdk-type": "client", "keywords": [ "node", diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index c74adeb..71c8a4c 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -50,12 +50,8 @@ export class AppConfigurationImporter { timeout: number, strict = false, progressCallback?: (progress: ImportProgress) => unknown, - importMode?: ImportMode + importMode = ImportMode.IgnoreMatch ): Promise { - if (importMode === undefined) { - importMode = ImportMode.IgnoreMatch; - } - this.validateImportMode(importMode); // Generate correlation ID for operations @@ -97,13 +93,9 @@ export class AppConfigurationImporter { public async GetConfigurationChanges( configSettingsSource: ConfigurationSettingsSource, strict = false, - importMode?: ImportMode, + importMode = ImportMode.IgnoreMatch, customHeadersOption?: OperationOptions ): Promise { - if (importMode == undefined) { - importMode = ImportMode.IgnoreMatch; - } - this.validateImportMode(importMode); // Generate correlation ID From 3c436af4ebc2f5b7462b96bfaa9a05f6a904eb4d Mon Sep 17 00:00:00 2001 From: cwanjau Date: Thu, 20 Nov 2025 11:48:13 +0300 Subject: [PATCH 12/23] Address comments --- .../src/appConfigurationImporter.ts | 26 +++++++------------ .../src/models.ts | 6 ++--- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index 71c8a4c..b20e1d9 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -53,8 +53,8 @@ export class AppConfigurationImporter { importMode = ImportMode.IgnoreMatch ): Promise { this.validateImportMode(importMode); - - // Generate correlation ID for operations + + // Generate correlationRequestId for operations in the same activity const customCorrelationRequestId: string = uuidv4(); const customHeadersOption: OperationOptions = { requestOptions: { @@ -66,11 +66,11 @@ export class AppConfigurationImporter { const configurationChanges: ConfigurationChanges = await this.GetConfigurationChanges(configSettingsSource, strict, importMode, customHeadersOption); - await this.applyUpdatesToServer([...configurationChanges.Added, ...configurationChanges.Modified], configurationChanges.Deleted, timeout, customHeadersOption, progressCallback); + await this.applyUpdatesToServer([...configurationChanges.ToAdd, ...configurationChanges.ToModify], configurationChanges.ToDelete, timeout, customHeadersOption, progressCallback); } /** - * Get configuration changes between source settings and Azure App Configuration service without any applying changes + * Get configuration changes between source settings and existing settings in Azure App Configuration service without applying any changes * * Example usage: * ```ts @@ -98,7 +98,7 @@ export class AppConfigurationImporter { ): Promise { this.validateImportMode(importMode); - // Generate correlation ID + // Generate correlationRequestId for operations in the same activity if (!customHeadersOption) { const customCorrelationRequestId: string = uuidv4(); customHeadersOption = { @@ -141,25 +141,19 @@ export class AppConfigurationImporter { if (!settingsAreEqual) { configurationSettingToModify.push(incoming); // Remove from add list since it's a modification, not an addition - const addIndex = configurationSettingToAdd.indexOf(incoming); - if (addIndex !== -1) { - configurationSettingToAdd.splice(addIndex, 1); - } + configurationSettingToAdd.splice(configurationSettingToAdd.indexOf(incoming), 1); } else if (importMode === ImportMode.IgnoreMatch) { // Remove unchanged settings from add list - const addIndex = configurationSettingToAdd.indexOf(incoming); - if (addIndex !== -1) { - configurationSettingToAdd.splice(addIndex, 1); - } + configurationSettingToAdd.splice(configurationSettingToAdd.indexOf(incoming), 1); } } } return { - Added: configurationSettingToAdd, - Modified: configurationSettingToModify, - Deleted: configurationSettingToDelete + ToAdd: configurationSettingToAdd, + ToModify: configurationSettingToModify, + ToDelete: configurationSettingToDelete }; } diff --git a/libraries/azure-app-configuration-importer/src/models.ts b/libraries/azure-app-configuration-importer/src/models.ts index cfca08b..731a6d5 100644 --- a/libraries/azure-app-configuration-importer/src/models.ts +++ b/libraries/azure-app-configuration-importer/src/models.ts @@ -52,7 +52,7 @@ export interface KeyLabelLookup { } export interface ConfigurationChanges { - Deleted: ConfigurationSetting[]; - Modified: SetConfigurationSettingParam[]; - Added: SetConfigurationSettingParam[]; + ToDelete: ConfigurationSetting[]; + ToModify: SetConfigurationSettingParam[]; + ToAdd: SetConfigurationSettingParam[]; } \ No newline at end of file From bc799ade1cc7a6369f1a103362db385645c0edbe Mon Sep 17 00:00:00 2001 From: cwanjau Date: Fri, 21 Nov 2025 12:52:27 +0300 Subject: [PATCH 13/23] Add Import overload --- .../src/appConfigurationImporter.ts | 56 +++++++++++++++++-- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index b20e1d9..918623d 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -48,11 +48,38 @@ export class AppConfigurationImporter { public async Import( configSettingsSource: ConfigurationSettingsSource, timeout: number, - strict = false, progressCallback?: (progress: ImportProgress) => unknown, - importMode = ImportMode.IgnoreMatch + strict?: boolean, + importMode?: ImportMode + ): Promise; + + /** + * Import pre-calculated configuration changes. + * Use when changes were previously obtained via GetConfigurationChanges(). + * + * Example usage: + * ```ts + * const changes = await importer.GetConfigurationChanges(source); + * then: + * await importer.Import(changes, 60); + * ``` + * @param configurationChanges - Pre-calculated changes object. + * @param timeout - Seconds of entire import progress timeout. + * @param progressCallback - Callback to report progress of import. + */ + public async Import( + configurationChanges: ConfigurationChanges, + timeout: number, + progressCallback?: (progress: ImportProgress) => unknown + ): Promise; + + public async Import( + configuration: ConfigurationSettingsSource | ConfigurationChanges, + timeout: number, + progressCallback?: ((progress: ImportProgress) => unknown), + strict: boolean = false, + importMode: ImportMode = ImportMode.IgnoreMatch ): Promise { - this.validateImportMode(importMode); // Generate correlationRequestId for operations in the same activity const customCorrelationRequestId: string = uuidv4(); @@ -64,9 +91,16 @@ export class AppConfigurationImporter { } }; - const configurationChanges: ConfigurationChanges = await this.GetConfigurationChanges(configSettingsSource, strict, importMode, customHeadersOption); + if (this.isConfigurationChanges(configuration)) { + const configurationChanges = configuration as ConfigurationChanges; + return await this.applyUpdatesToServer([...configurationChanges.ToAdd, ...configurationChanges.ToModify], configurationChanges.ToDelete, timeout, customHeadersOption, progressCallback); + } + + const source = configuration as ConfigurationSettingsSource; + this.validateImportMode(importMode); - await this.applyUpdatesToServer([...configurationChanges.ToAdd, ...configurationChanges.ToModify], configurationChanges.ToDelete, timeout, customHeadersOption, progressCallback); + const configurationChanges: ConfigurationChanges = await this.GetConfigurationChanges(source, strict, importMode, customHeadersOption); + return await this.applyUpdatesToServer([...configurationChanges.ToAdd, ...configurationChanges.ToModify], configurationChanges.ToDelete, timeout, customHeadersOption, progressCallback); } /** @@ -206,4 +240,16 @@ export class AppConfigurationImporter { throw new ArgumentError("Only options supported for Import Mode are 'All' and 'Ignore-Match'."); } } + + /** + * Type guard to detect a ConfigurationChanges object. + * @internal + */ + private isConfigurationChanges(obj: unknown): obj is ConfigurationChanges { + if (obj === null || typeof obj !== "object") { + return false; + } + const configChanges = obj as Partial; + return Array.isArray(configChanges.ToAdd) && Array.isArray(configChanges.ToModify) && Array.isArray(configChanges.ToDelete); + } } From 0b1532125d28e46df9fd5f709c4232e10fbed7dc Mon Sep 17 00:00:00 2001 From: cwanjau Date: Fri, 21 Nov 2025 12:56:48 +0300 Subject: [PATCH 14/23] Fix linting error --- .../src/appConfigurationImporter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index 918623d..cce12ed 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -77,7 +77,7 @@ export class AppConfigurationImporter { configuration: ConfigurationSettingsSource | ConfigurationChanges, timeout: number, progressCallback?: ((progress: ImportProgress) => unknown), - strict: boolean = false, + strict = false, importMode: ImportMode = ImportMode.IgnoreMatch ): Promise { From 5f9c46af9f5b5edfc10a7e2cc0051b37369db4d7 Mon Sep 17 00:00:00 2001 From: cwanjau Date: Fri, 21 Nov 2025 13:48:50 +0300 Subject: [PATCH 15/23] Add tests --- .../tests/appConfigurationImporter.spec.ts | 113 ++++++++++++++---- ...terableConfigurationSettingsSource.spec.ts | 6 +- ...vSetConfigurationSettingsConverter.spec.ts | 12 +- ...eStreamConfigurationSettingsSource.spec.ts | 2 +- 4 files changed, 99 insertions(+), 34 deletions(-) diff --git a/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts b/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts index ba9d8af..e2402b4 100644 --- a/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts @@ -50,7 +50,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () finished = importProgress.successCount; total = importProgress.importCount; }; - await appConfigurationImporter.Import(stringConfigurationSource, 3, false, reportImportProgress); + await appConfigurationImporter.Import(stringConfigurationSource, 3, reportImportProgress, false); assert.equal(finished, 3); assert.equal(total, 3); }); @@ -66,7 +66,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () format: ConfigurationFormat.Json }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); - const importPromise = appConfigurationImporter.Import(stringConfigurationSource, 1, false); + const importPromise = appConfigurationImporter.Import(stringConfigurationSource, 1, undefined, false); importPromise.catch((e) => { expect(e.message).to.eq("server error"); }); @@ -101,7 +101,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); try { - await appConfigurationImporter.Import(stringConfigurationSource, 1, false); + await appConfigurationImporter.Import(stringConfigurationSource, 1, undefined, false); } catch (error) { assert.isTrue(error instanceof OperationTimeoutError); @@ -144,7 +144,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () total = importProgress.importCount; }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); - await appConfigurationImporter.Import(stringConfigurationSource, 10, false, reportImportProgress); + await appConfigurationImporter.Import(stringConfigurationSource, 10, reportImportProgress, false); assert.equal(finished, 3); assert.equal(total, 3); }); @@ -159,7 +159,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () format: ConfigurationFormat.Json }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); - await appConfigurationImporter.Import(stringConfigurationSource, 10, false); + await appConfigurationImporter.Import(stringConfigurationSource, 10, undefined, false); }); it("Try import an empty file, no error", async () => { @@ -172,7 +172,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () format: ConfigurationFormat.Json }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); - await appConfigurationImporter.Import(stringConfigurationSource, 10, false); + await appConfigurationImporter.Import(stringConfigurationSource, 10, undefined, false); }); it("Succeed to import simple key value file in strict mode", async () => { @@ -226,7 +226,72 @@ describe("Call Import API to import configuration file to AppConfiguration", () finished = importProgress.successCount; total = importProgress.importCount; }; - await appConfigurationImporter.Import(stringConfigurationSource, 5, true, reportImportProgress); + await appConfigurationImporter.Import(stringConfigurationSource, 5, reportImportProgress, true); + assert.equal(finished, 3); + assert.equal(total, 3); + }); + + it("Succeed to import configuration changes with Import API", async () => { + const headerLike = sinon.createStubInstance(MockUpHttpHeaderLike); + const resourceLike = sinon.createStubInstance(MockupResourceLike); + const mockedResponse: SetConfigurationSettingResponse = { + key: "fakeKey", + isReadOnly: false, + _response: { + status: 200, + request: resourceLike, + headers: headerLike, + parsedHeaders: {}, + bodyAsText: "fakeBody" + } + }; + const mockedDeleteResponse: DeleteConfigurationSettingResponse = { + _response: { + status: 200, + request: resourceLike, + headers: headerLike, + parsedHeaders: {}, + bodyAsText: "fakeBody" + }, + statusCode: 200 + }; + + const AppConfigurationClientStub = sinon.createStubInstance(AppConfigurationClient); + AppConfigurationClientStub.setConfigurationSetting.resolves(mockedResponse); + AppConfigurationClientStub.deleteConfigurationSetting.resolves(mockedDeleteResponse); + AppConfigurationClientStub.listConfigurationSettings.returns(listConfigurationSettings()); + const appConfigurationImporter = new AppConfigurationImporter(AppConfigurationClientStub); + + const options = { + data: fs.readFileSync(path.join("__dirname", "../tests/sources/default.json")).toString(), + format: ConfigurationFormat.Json, + profile: ConfigurationProfile.Default, + label: "Dev", + separator: ":" + }; + const stringConfigurationSource = new StringConfigurationSettingsSource(options); + + // Call GetConfigurationChanges to get the changes we would import + const configurationChanges = await appConfigurationImporter.GetConfigurationChanges( + stringConfigurationSource, + false, + ImportMode.All + ); + + assert.equal(configurationChanges.ToAdd.length, 2); + assert.equal(configurationChanges.ToModify.length, 1); + assert.equal(configurationChanges.ToDelete.length, 0); + assert.equal(configurationChanges.ToModify[0].key, "app:Settings:FontColor"); + + let finished = 0; + let total = 0; + const reportImportProgress = (importProgress: ImportProgress) => { + finished = importProgress.successCount; + total = importProgress.importCount; + }; + + // Use Import API with pre-calculated changes + await appConfigurationImporter.Import(configurationChanges, 5, reportImportProgress); assert.equal(finished, 3); assert.equal(total, 3); }); @@ -250,10 +315,10 @@ describe("Call Import API to import configuration file to AppConfiguration", () }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); const configurationChanges = await appConfigurationImporter.GetConfigurationChanges(stringConfigurationSource, false, ImportMode.All); - assert.equal(configurationChanges.Added.length, 2); - assert.equal(configurationChanges.Modified.length, 1); - assert.equal(configurationChanges.Deleted.length, 0); - assert.equal(configurationChanges.Modified[0].key, "app:Settings:FontColor"); + assert.equal(configurationChanges.ToAdd.length, 2); + assert.equal(configurationChanges.ToModify.length, 1); + assert.equal(configurationChanges.ToDelete.length, 0); + assert.equal(configurationChanges.ToModify[0].key, "app:Settings:FontColor"); }); it("Succeed to get configuration changes and return no matching key values updates with importMode as IgnoreMatch and profile as default", async () => { @@ -267,10 +332,10 @@ describe("Call Import API to import configuration file to AppConfiguration", () const source = new StringConfigurationSettingsSource(options); const configurationChanges = await appConfigurationImporter.GetConfigurationChanges(source, false, ImportMode.IgnoreMatch); // Only keys with no matching key-values in App Configuration will be updated - assert.equal(configurationChanges.Added.length, 0); - assert.equal(configurationChanges.Modified.length, 1); - assert.equal(configurationChanges.Modified[0].key, "app:Settings:FontColor"); - assert.equal(configurationChanges.Deleted.length, 0); + assert.equal(configurationChanges.ToAdd.length, 0); + assert.equal(configurationChanges.ToModify.length, 1); + assert.equal(configurationChanges.ToModify[0].key, "app:Settings:FontColor"); + assert.equal(configurationChanges.ToDelete.length, 0); }); it("Succeed to get configuration changes from key-values file with importMode as All and profile as kvset", async () => { @@ -282,10 +347,10 @@ describe("Call Import API to import configuration file to AppConfiguration", () const stringConfigurationSource = new StringConfigurationSettingsSource(options); const configurationChanges = await appConfigurationImporter.GetConfigurationChanges(stringConfigurationSource, false, ImportMode.All); // All key-values in App Configuration will be updated - assert.equal(configurationChanges.Added.length, 2); - assert.equal(configurationChanges.Modified.length, 1); - assert.equal(configurationChanges.Modified[0].key, "TestEnv"); - assert.equal(configurationChanges.Deleted.length, 0); + assert.equal(configurationChanges.ToAdd.length, 2); + assert.equal(configurationChanges.ToModify.length, 1); + assert.equal(configurationChanges.ToModify[0].key, "TestEnv"); + assert.equal(configurationChanges.ToDelete.length, 0); }); it("Succeed to get configuration changes and return no matching key values with importMode as IgnoreMatch and profile as kvset", async () => { @@ -296,11 +361,11 @@ describe("Call Import API to import configuration file to AppConfiguration", () }; const source = new StringConfigurationSettingsSource(options); const configurationChanges = await appConfigurationImporter.GetConfigurationChanges(source, false, ImportMode.IgnoreMatch); - // Only changed key (TestEnv) should be in Modified - assert.equal(configurationChanges.Added.length, 0); - assert.equal(configurationChanges.Modified.length, 1); - assert.equal(configurationChanges.Modified[0].key, "TestEnv"); - assert.equal(configurationChanges.Deleted.length, 0); + // Only changed key (TestEnv) should be in ToModify + assert.equal(configurationChanges.ToAdd.length, 0); + assert.equal(configurationChanges.ToModify.length, 1); + assert.equal(configurationChanges.ToModify[0].key, "TestEnv"); + assert.equal(configurationChanges.ToDelete.length, 0); }); it("Fail when an invalid import mode is provided", async () => { diff --git a/libraries/azure-app-configuration-importer/tests/iterableConfigurationSettingsSource.spec.ts b/libraries/azure-app-configuration-importer/tests/iterableConfigurationSettingsSource.spec.ts index dc1f9e1..51a5bf8 100644 --- a/libraries/azure-app-configuration-importer/tests/iterableConfigurationSettingsSource.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/iterableConfigurationSettingsSource.spec.ts @@ -63,7 +63,7 @@ describe("Iterator configuration source test", () => { finished = importProgress.successCount; total = importProgress.importCount; }; - await appConfigurationImporter.Import(iteratorConfigurationSettingsSource, 3, false, reportImportProgress); + await appConfigurationImporter.Import(iteratorConfigurationSettingsSource, 3, reportImportProgress, false); assert.equal(finished, 6); assert.equal(total, 6); }); @@ -100,7 +100,7 @@ describe("Iterator configuration source test", () => { total = importProgress.importCount; }; - await appConfigurationImporter.Import(iteratorConfigurationSettingsSource, 3, false, reportImportProgress, ImportMode.All); + await appConfigurationImporter.Import(iteratorConfigurationSettingsSource, 3, reportImportProgress, false, ImportMode.All); assert.equal(finished, 6); assert.equal(total, 6); }); @@ -137,7 +137,7 @@ describe("Iterator configuration source test", () => { total = importProgress.importCount; }; - await appConfigurationImporter.Import(iteratorConfigurationSettingsSource, 3, false, reportImportProgress, ImportMode.IgnoreMatch); // Ignore-Match is default import mode + await appConfigurationImporter.Import(iteratorConfigurationSettingsSource, 3, reportImportProgress, false, ImportMode.IgnoreMatch); // Ignore-Match is default import mode assert.equal(finished, 0); assert.equal(total, 0); }); diff --git a/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts b/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts index db80a9a..a84d602 100644 --- a/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/kvSetConfigurationSettingsConverter.spec.ts @@ -203,9 +203,9 @@ describe("Parse kvset format file", () => { const configurationChanges = await appConfigurationImporter.GetConfigurationChanges(stringConfigurationSource, true, ImportMode.All); // The keys present in the store and not in the configuration file are deleted if strict is set to true - const deletedKeys = configurationChanges.Deleted.map(d => d.key); - assert.equal(configurationChanges.Deleted.length, 3); - assert.includeMembers(deletedKeys, ["app:Settings:FontSize", "app:Settings:BackgroundColor", "app:Settings:FontColor"]); + const DeleteKeys = configurationChanges.ToDelete.map(d => d.key); + assert.equal(configurationChanges.ToDelete.length, 3); + assert.includeMembers(DeleteKeys, ["app:Settings:FontSize", "app:Settings:BackgroundColor", "app:Settings:FontColor"]); }); it("Delete key-values present in the store but not available in the config file, with similar key but different label", async()=>{ @@ -222,9 +222,9 @@ describe("Parse kvset format file", () => { const configurationChanges = await appConfigurationImporter.GetConfigurationChanges(stringConfigurationSource, true, ImportMode.All); // The keys present in the store and not in the configuration file are deleted if strict is set to true - const deletedKeys = configurationChanges.Deleted.map(d => `key: ${d.key}, label: ${d.label}`); - assert.equal(configurationChanges.Deleted.length, 5); - assert.includeMembers(deletedKeys, [ + const DeleteKeys = configurationChanges.ToDelete.map(d => `key: ${d.key}, label: ${d.label}`); + assert.equal(configurationChanges.ToDelete.length, 5); + assert.includeMembers(DeleteKeys, [ "key: app:Settings:FontSize, label: Dev", "key: app:Settings:BackgroundColor, label: Dev", "key: app:Settings:FontColor, label: Dev", diff --git a/libraries/azure-app-configuration-importer/tests/readableStreamConfigurationSettingsSource.spec.ts b/libraries/azure-app-configuration-importer/tests/readableStreamConfigurationSettingsSource.spec.ts index 6eabceb..8dc0f93 100644 --- a/libraries/azure-app-configuration-importer/tests/readableStreamConfigurationSettingsSource.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/readableStreamConfigurationSettingsSource.spec.ts @@ -161,7 +161,7 @@ describe("Readable stream configuration settings source tests", () => { finished = importProgress.successCount; total = importProgress.importCount; }; - await appConfigurationImporter.Import(readableConfigurationSettingsSource, 3, false, reportImportProgress); + await appConfigurationImporter.Import(readableConfigurationSettingsSource, 3, reportImportProgress, false); assert.equal(finished, 3); assert.equal(total, 3); }); From 0be15396a6968be4d30dce164bd370bc6a725526 Mon Sep 17 00:00:00 2001 From: cwanjau Date: Mon, 24 Nov 2025 10:20:28 +0300 Subject: [PATCH 16/23] Address copilot comments --- .../src/appConfigurationImporter.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index cce12ed..54e9676 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -60,7 +60,7 @@ export class AppConfigurationImporter { * Example usage: * ```ts * const changes = await importer.GetConfigurationChanges(source); - * then: + * Then call Import: * await importer.Import(changes, 60); * ``` * @param configurationChanges - Pre-calculated changes object. @@ -109,7 +109,7 @@ export class AppConfigurationImporter { * Example usage: * ```ts * const fileData = fs.readFileSync("mylocalPath").toString(); - * const configurationChanges = await client.getConfigurationChanges( + * const configurationChanges = await client.GetConfigurationChanges( * new StringConfigurationSettingsSource({data:fileData, format: ConfigurationFormat.Json}), * false, * ImportMode.All, @@ -166,7 +166,7 @@ export class AppConfigurationImporter { configurationSettingToDelete.push(existing); } - const incoming = configSettings.find(configSetting => configSetting.key == existing.key && + const incoming = configSettings.find(configSetting => configSetting.key === existing.key && configSetting.label === existing.label); if (incoming) { From f2c0fc59ed7800054bc8c3bae6de07a71880d24f Mon Sep 17 00:00:00 2001 From: cwanjau Date: Mon, 24 Nov 2025 10:44:17 +0300 Subject: [PATCH 17/23] Address copilot comments --- .../src/appConfigurationImporter.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index 54e9676..a35ff23 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -158,7 +158,7 @@ export class AppConfigurationImporter { srcKeyLabelLookUp[config.key][config.label || ""] = true; }); - configurationSettingToAdd.push(...configSettings); + const settingsToRemove = new Set(); for await (const existing of this.configurationClient.listConfigurationSettings({...configSettingsSource.FilterOptions, ...customHeadersOption})) { const isKeyLabelPresent: boolean = srcKeyLabelLookUp[existing.key] && srcKeyLabelLookUp[existing.key][existing.label || ""]; @@ -174,15 +174,17 @@ export class AppConfigurationImporter { if (!settingsAreEqual) { configurationSettingToModify.push(incoming); - // Remove from add list since it's a modification, not an addition - configurationSettingToAdd.splice(configurationSettingToAdd.indexOf(incoming), 1); - } - else if (importMode === ImportMode.IgnoreMatch) { - // Remove unchanged settings from add list - configurationSettingToAdd.splice(configurationSettingToAdd.indexOf(incoming), 1); + // Mark for removal from add list since it's a modification, not an addition + settingsToRemove.add(incoming); + } else if (importMode === ImportMode.IgnoreMatch) { + // Mark unchanged settings for removal from add list + settingsToRemove.add(incoming); } } } + + // Filter out items marked for removal + configurationSettingToAdd.push(...configSettings.filter(item => !settingsToRemove.has(item))); return { ToAdd: configurationSettingToAdd, From 30f8995f56098a14414d57b82a8458374a46c882 Mon Sep 17 00:00:00 2001 From: cwanjau Date: Mon, 24 Nov 2025 10:49:44 +0300 Subject: [PATCH 18/23] fix linting issues --- .../src/appConfigurationImporter.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index a35ff23..5a18f2f 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -171,12 +171,13 @@ export class AppConfigurationImporter { if (incoming) { const settingsAreEqual: boolean = isConfigSettingEqual(incoming, existing); - + if (!settingsAreEqual) { configurationSettingToModify.push(incoming); // Mark for removal from add list since it's a modification, not an addition settingsToRemove.add(incoming); - } else if (importMode === ImportMode.IgnoreMatch) { + } + else if (importMode === ImportMode.IgnoreMatch) { // Mark unchanged settings for removal from add list settingsToRemove.add(incoming); } From 7b7c06df2ee5c1465aa88fe5fd57cfc50a19fabd Mon Sep 17 00:00:00 2001 From: cwanjau Date: Tue, 25 Nov 2025 10:50:53 +0300 Subject: [PATCH 19/23] Address comments --- .../src/appConfigurationImporter.ts | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index 5a18f2f..37ef246 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -91,16 +91,19 @@ export class AppConfigurationImporter { } }; + this.validateImportMode(importMode); + + let configurationChangesToApply: ConfigurationChanges; + if (this.isConfigurationChanges(configuration)) { - const configurationChanges = configuration as ConfigurationChanges; - return await this.applyUpdatesToServer([...configurationChanges.ToAdd, ...configurationChanges.ToModify], configurationChanges.ToDelete, timeout, customHeadersOption, progressCallback); + configurationChangesToApply = configuration as ConfigurationChanges; + } + else { + const source = configuration as ConfigurationSettingsSource; + configurationChangesToApply = await this.GetConfigurationChanges(source, strict, importMode, customHeadersOption); } - const source = configuration as ConfigurationSettingsSource; - this.validateImportMode(importMode); - - const configurationChanges: ConfigurationChanges = await this.GetConfigurationChanges(source, strict, importMode, customHeadersOption); - return await this.applyUpdatesToServer([...configurationChanges.ToAdd, ...configurationChanges.ToModify], configurationChanges.ToDelete, timeout, customHeadersOption, progressCallback); + return await this.applyUpdatesToServer([...configurationChangesToApply.ToAdd, ...configurationChangesToApply.ToModify], configurationChangesToApply.ToDelete, timeout, customHeadersOption, progressCallback); } /** @@ -158,34 +161,30 @@ export class AppConfigurationImporter { srcKeyLabelLookUp[config.key][config.label || ""] = true; }); - const settingsToRemove = new Set(); + configurationSettingToAdd.push(...configSettings); for await (const existing of this.configurationClient.listConfigurationSettings({...configSettingsSource.FilterOptions, ...customHeadersOption})) { const isKeyLabelPresent: boolean = srcKeyLabelLookUp[existing.key] && srcKeyLabelLookUp[existing.key][existing.label || ""]; if (strict && !isKeyLabelPresent) { configurationSettingToDelete.push(existing); } - - const incoming = configSettings.find(configSetting => configSetting.key === existing.key && - configSetting.label === existing.label); - + + const incoming = configSettings.find(configSetting => configSetting.key == existing.key && configSetting.label === existing.label); + if (incoming) { const settingsAreEqual: boolean = isConfigSettingEqual(incoming, existing); if (!settingsAreEqual) { configurationSettingToModify.push(incoming); - // Mark for removal from add list since it's a modification, not an addition - settingsToRemove.add(incoming); + // Remove from add list since it's a modification, not an addition + configurationSettingToAdd.splice(configurationSettingToAdd.indexOf(incoming), 1); } else if (importMode === ImportMode.IgnoreMatch) { - // Mark unchanged settings for removal from add list - settingsToRemove.add(incoming); + // Remove unchanged settings from add list + configurationSettingToAdd.splice(configurationSettingToAdd.indexOf(incoming), 1); } } } - - // Filter out items marked for removal - configurationSettingToAdd.push(...configSettings.filter(item => !settingsToRemove.has(item))); return { ToAdd: configurationSettingToAdd, @@ -195,7 +194,7 @@ export class AppConfigurationImporter { } private async applyUpdatesToServer( - settingsToAdd: SetConfigurationSettingParam[], + settingsToPut: SetConfigurationSettingParam[], settingsToDelete: ConfigurationSetting[], timeout: number, options: OperationOptions, @@ -208,7 +207,7 @@ export class AppConfigurationImporter { const deleteTimeConsumed = (endTime - startTime) / 1000; timeout -= deleteTimeConsumed; - const importTaskManager = this.newAdaptiveTaskManager((setting) => this.configurationClient.setConfigurationSetting(setting, options), settingsToAdd); + const importTaskManager = this.newAdaptiveTaskManager((setting) => this.configurationClient.setConfigurationSetting(setting, options), settingsToPut); await this.executeTasksWithTimeout(importTaskManager, timeout, progressCallback); } From 2abfdbd2b6db0eb4e587de1fec5d8d66c2febc8d Mon Sep 17 00:00:00 2001 From: cwanjau Date: Wed, 26 Nov 2025 10:41:26 +0300 Subject: [PATCH 20/23] Update have changes as a source --- .../src/appConfigurationImporter.ts | 38 ++++++++++--------- .../src/index.ts | 1 + .../configurationChangesSource.ts | 25 ++++++++++++ .../configurationSettingsSource.ts | 7 ++-- .../tests/appConfigurationImporter.spec.ts | 25 +++++++++++- 5 files changed, 75 insertions(+), 21 deletions(-) create mode 100644 libraries/azure-app-configuration-importer/src/settingsImport/configurationChangesSource.ts diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index 37ef246..abd7bd3 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -8,6 +8,7 @@ import { FeatureFlagValue, SecretReferenceValue } from "@azure/app-configuration"; import { ConfigurationSettingsSource } from "./settingsImport/configurationSettingsSource"; +import { ConfigurationChangesSource } from "./settingsImport/configurationChangesSource"; import { ImportMode } from "./enums"; import { OperationTimeoutError, ArgumentError } from "./errors"; import { AdaptiveTaskManager } from "./internal/adaptiveTaskManager"; @@ -60,7 +61,7 @@ export class AppConfigurationImporter { * Example usage: * ```ts * const changes = await importer.GetConfigurationChanges(source); - * Then call Import: + * // Then call Import: * await importer.Import(changes, 60); * ``` * @param configurationChanges - Pre-calculated changes object. @@ -68,19 +69,18 @@ export class AppConfigurationImporter { * @param progressCallback - Callback to report progress of import. */ public async Import( - configurationChanges: ConfigurationChanges, + configurationChangesSource: ConfigurationChangesSource, timeout: number, progressCallback?: (progress: ImportProgress) => unknown ): Promise; public async Import( - configuration: ConfigurationSettingsSource | ConfigurationChanges, + configurationSettingsSource: ConfigurationSettingsSource, timeout: number, progressCallback?: ((progress: ImportProgress) => unknown), strict = false, importMode: ImportMode = ImportMode.IgnoreMatch ): Promise { - // Generate correlationRequestId for operations in the same activity const customCorrelationRequestId: string = uuidv4(); const customHeadersOption: OperationOptions = { @@ -93,17 +93,15 @@ export class AppConfigurationImporter { this.validateImportMode(importMode); - let configurationChangesToApply: ConfigurationChanges; - - if (this.isConfigurationChanges(configuration)) { - configurationChangesToApply = configuration as ConfigurationChanges; - } - else { - const source = configuration as ConfigurationSettingsSource; - configurationChangesToApply = await this.GetConfigurationChanges(source, strict, importMode, customHeadersOption); + if (configurationSettingsSource instanceof ConfigurationChangesSource) { + // When using ConfigurationChanges, strict and importMode parameters are not applicable + if (strict !== false || importMode !== ImportMode.IgnoreMatch) { + throw new ArgumentError("Parameters 'strict' and 'importMode' are not applicable when importing pre-calculated changes."); + } } - return await this.applyUpdatesToServer([...configurationChangesToApply.ToAdd, ...configurationChangesToApply.ToModify], configurationChangesToApply.ToDelete, timeout, customHeadersOption, progressCallback); + const configurationChanges = await this.GetConfigurationChanges(configurationSettingsSource, strict, importMode, customHeadersOption); + return await this.applyUpdatesToServer([...configurationChanges.ToAdd, ...configurationChanges.ToModify], configurationChanges.ToDelete, timeout, customHeadersOption, progressCallback); } /** @@ -147,8 +145,15 @@ export class AppConfigurationImporter { }; } - const configSettings = await configSettingsSource.GetConfigurationSettings(); - + const configSettingsResult = await configSettingsSource.GetConfigurationSettings(); + + // If the source returns ConfigurationChanges (e.g., ConfigurationChangesSource), + // return them directly without further processing since changes are already calculated + if (this.isConfigurationChanges(configSettingsResult)) { + return configSettingsResult as ConfigurationChanges; + } + + const configSettings = configSettingsResult as Array>; const configurationSettingToDelete: ConfigurationSetting[] = []; const configurationSettingToModify: SetConfigurationSettingParam[] = []; const configurationSettingToAdd: SetConfigurationSettingParam[] = []; @@ -172,9 +177,8 @@ export class AppConfigurationImporter { const incoming = configSettings.find(configSetting => configSetting.key == existing.key && configSetting.label === existing.label); if (incoming) { - const settingsAreEqual: boolean = isConfigSettingEqual(incoming, existing); - if (!settingsAreEqual) { + if (!isConfigSettingEqual(incoming, existing)) { configurationSettingToModify.push(incoming); // Remove from add list since it's a modification, not an addition configurationSettingToAdd.splice(configurationSettingToAdd.indexOf(incoming), 1); diff --git a/libraries/azure-app-configuration-importer/src/index.ts b/libraries/azure-app-configuration-importer/src/index.ts index 7258ec6..a54e37f 100644 --- a/libraries/azure-app-configuration-importer/src/index.ts +++ b/libraries/azure-app-configuration-importer/src/index.ts @@ -12,5 +12,6 @@ export * from "./errors"; export { ImportProgress as ImportResult, ConfigurationChanges } from "./models"; export { StringConfigurationSettingsSource } from "./settingsImport/stringConfigurationSettingsSource"; export { ConfigurationSettingsSource } from "./settingsImport/configurationSettingsSource"; +export { ConfigurationChangesSource } from "./settingsImport/configurationChangesSource"; export { IterableConfigurationSettingsSource } from "./settingsImport/iterableConfigurationSettingsSource"; export { ReadableStreamConfigurationSettingsSource } from "./settingsImport/readableStreamConfigurationSettingsSource"; \ No newline at end of file diff --git a/libraries/azure-app-configuration-importer/src/settingsImport/configurationChangesSource.ts b/libraries/azure-app-configuration-importer/src/settingsImport/configurationChangesSource.ts new file mode 100644 index 0000000..f5c03b5 --- /dev/null +++ b/libraries/azure-app-configuration-importer/src/settingsImport/configurationChangesSource.ts @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { ConfigurationSettingsSource } from "./configurationSettingsSource"; +import { ConfigurationChanges } from "../models"; +import { ListConfigurationSettingsOptions } from "@azure/app-configuration"; +import { ArgumentError } from "../errors"; + +export class ConfigurationChangesSource implements ConfigurationSettingsSource { + private readonly configurationChanges: ConfigurationChanges; + + constructor(configurationChanges: ConfigurationChanges, filterOptions?: ListConfigurationSettingsOptions) { + if (filterOptions && Object.keys(filterOptions).length > 0) { + throw new ArgumentError("FilterOptions are not supported for ConfigurationChangesSource."); + } + this.configurationChanges = configurationChanges; + } + + /** + * @inheritdoc + */ + public async GetConfigurationSettings(): Promise { + return this.configurationChanges; + } +} \ No newline at end of file diff --git a/libraries/azure-app-configuration-importer/src/settingsImport/configurationSettingsSource.ts b/libraries/azure-app-configuration-importer/src/settingsImport/configurationSettingsSource.ts index 4e921a9..51a1822 100644 --- a/libraries/azure-app-configuration-importer/src/settingsImport/configurationSettingsSource.ts +++ b/libraries/azure-app-configuration-importer/src/settingsImport/configurationSettingsSource.ts @@ -7,6 +7,7 @@ import { SecretReferenceValue, SetConfigurationSettingParam } from "@azure/app-configuration"; +import { ConfigurationChanges } from "../models"; /** * Interface of all ConfigurationSettingsSource @@ -15,14 +16,14 @@ export interface ConfigurationSettingsSource { /** * Get ConfigurationSettings collection from source. * - * @returns Collection of ConfigurationSettings + * @returns Collection of ConfigurationSettings or ConfigurationChanges */ - GetConfigurationSettings(): Promise>>; + GetConfigurationSettings(): Promise> | ConfigurationChanges>; /** * Get label and prefix filter * * @returns label and prefix */ - FilterOptions: ListConfigurationSettingsOptions; + FilterOptions?: ListConfigurationSettingsOptions; } diff --git a/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts b/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts index e2402b4..3b73edd 100644 --- a/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts @@ -6,6 +6,7 @@ import * as path from "path"; import * as fs from "fs"; import { ConfigurationFormat, ConfigurationProfile, ImportMode } from "../src/enums"; import { StringConfigurationSettingsSource } from "../src/settingsImport/stringConfigurationSettingsSource"; +import { ConfigurationChangesSource } from "../src/settingsImport/configurationChangesSource"; import { AppConfigurationClient, ConfigurationSetting, @@ -291,11 +292,33 @@ describe("Call Import API to import configuration file to AppConfiguration", () }; // Use Import API with pre-calculated changes - await appConfigurationImporter.Import(configurationChanges, 5, reportImportProgress); + const changesSourceForTest = new ConfigurationChangesSource(configurationChanges); + await appConfigurationImporter.Import(changesSourceForTest, 5, reportImportProgress); assert.equal(finished, 3); assert.equal(total, 3); }); + it("Fail to import ConfigurationChangesSource when both strict and importMode parameters are provided", async () => { + const AppConfigurationClientStub = sinon.createStubInstance(AppConfigurationClient); + const appConfigurationImporter = new AppConfigurationImporter(AppConfigurationClientStub); + + const configurationChanges = { + ToAdd: [{ key: "testKey", value: "testValue" }], + ToModify: [], + ToDelete: [] + }; + + const changesSource = new ConfigurationChangesSource(configurationChanges); + + try { + await appConfigurationImporter.Import(changesSource, 5, undefined, true, ImportMode.All); + } + catch (error) { + expect(error).to.be.instanceOf(ArgumentError); + expect((error as ArgumentError).message).to.contain("Parameters 'strict' and 'importMode' are not applicable when importing pre-calculated changes"); + } + }); + describe("Call GetConfigurationChanges API to get configurations changes from file and pass Import mode options", () => { let appConfigurationImporter: AppConfigurationImporter; From 7740afe16fdf56c9347933f320b4fca9113429ee Mon Sep 17 00:00:00 2001 From: cwanjau Date: Mon, 1 Dec 2025 11:49:32 +0300 Subject: [PATCH 21/23] Address comments --- .../src/appConfigurationImporter.ts | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index abd7bd3..36831f0 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -78,9 +78,27 @@ export class AppConfigurationImporter { configurationSettingsSource: ConfigurationSettingsSource, timeout: number, progressCallback?: ((progress: ImportProgress) => unknown), - strict = false, - importMode: ImportMode = ImportMode.IgnoreMatch + strict?: boolean, + importMode?: ImportMode ): Promise { + + if (configurationSettingsSource instanceof ConfigurationChangesSource) { + // When using ConfigurationChanges, strict and importMode parameters are not applicable + if (strict || importMode) { + throw new ArgumentError("Parameters 'strict' and 'importMode' are not applicable when importing pre-calculated changes."); + } + } + else { + if (importMode === undefined) { + importMode = ImportMode.IgnoreMatch; + } + + this.validateImportMode(importMode); + if (strict === undefined) { + strict = false; + } + } + // Generate correlationRequestId for operations in the same activity const customCorrelationRequestId: string = uuidv4(); const customHeadersOption: OperationOptions = { @@ -91,15 +109,6 @@ export class AppConfigurationImporter { } }; - this.validateImportMode(importMode); - - if (configurationSettingsSource instanceof ConfigurationChangesSource) { - // When using ConfigurationChanges, strict and importMode parameters are not applicable - if (strict !== false || importMode !== ImportMode.IgnoreMatch) { - throw new ArgumentError("Parameters 'strict' and 'importMode' are not applicable when importing pre-calculated changes."); - } - } - const configurationChanges = await this.GetConfigurationChanges(configurationSettingsSource, strict, importMode, customHeadersOption); return await this.applyUpdatesToServer([...configurationChanges.ToAdd, ...configurationChanges.ToModify], configurationChanges.ToDelete, timeout, customHeadersOption, progressCallback); } From 3fbdab5ea5ae27ea322366fb824ff5652225b7c2 Mon Sep 17 00:00:00 2001 From: cwanjau Date: Mon, 1 Dec 2025 12:31:49 +0300 Subject: [PATCH 22/23] Add import options --- .../src/appConfigurationImporter.ts | 25 +++++-------------- .../src/importOptions.ts | 20 +++++++++++++-- .../src/index.ts | 3 ++- .../tests/appConfigurationImporter.spec.ts | 16 ++++++------ ...terableConfigurationSettingsSource.spec.ts | 6 ++--- ...eStreamConfigurationSettingsSource.spec.ts | 2 +- 6 files changed, 38 insertions(+), 34 deletions(-) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index 36831f0..6394477 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -17,6 +17,7 @@ import { isConfigSettingEqual } from "./internal/utils"; import { v4 as uuidv4 } from "uuid"; import { Constants } from "./internal/constants"; import { OperationOptions } from "@azure/core-client"; +import { ImportOptions } from "./importOptions"; /** * Entrypoint class for sync configuration @@ -40,18 +41,16 @@ export class AppConfigurationImporter { * const result = await asyncClient.Import(new StringConfigurationSettingsSource({data:fileData, format: ConfigurationFormat.Json})); * ``` * @param configSettingsSource - A ConfigurationSettingsSource instance. - * @param strict - Use strict mode or not. * @param timeout - Seconds of entire import progress timeout * @param progressCallback - Callback for report the progress of import - * @param importMode - Determines the behavior when importing key-values. The default value, 'All' will import all key-values in the input file to App Configuration. 'Ignore-Match' will only import settings that have no matching key-value in App Configuration. + * @param options - Import options which include strict and import mode * @returns Promise */ public async Import( configSettingsSource: ConfigurationSettingsSource, timeout: number, progressCallback?: (progress: ImportProgress) => unknown, - strict?: boolean, - importMode?: ImportMode + options?: ImportOptions ): Promise; /** @@ -78,26 +77,14 @@ export class AppConfigurationImporter { configurationSettingsSource: ConfigurationSettingsSource, timeout: number, progressCallback?: ((progress: ImportProgress) => unknown), - strict?: boolean, - importMode?: ImportMode + options?: ImportOptions ): Promise { - if (configurationSettingsSource instanceof ConfigurationChangesSource) { // When using ConfigurationChanges, strict and importMode parameters are not applicable - if (strict || importMode) { + if (options?.strict || options?.importMode) { throw new ArgumentError("Parameters 'strict' and 'importMode' are not applicable when importing pre-calculated changes."); } } - else { - if (importMode === undefined) { - importMode = ImportMode.IgnoreMatch; - } - - this.validateImportMode(importMode); - if (strict === undefined) { - strict = false; - } - } // Generate correlationRequestId for operations in the same activity const customCorrelationRequestId: string = uuidv4(); @@ -109,7 +96,7 @@ export class AppConfigurationImporter { } }; - const configurationChanges = await this.GetConfigurationChanges(configurationSettingsSource, strict, importMode, customHeadersOption); + const configurationChanges = await this.GetConfigurationChanges(configurationSettingsSource, options?.strict, options?.importMode, customHeadersOption); return await this.applyUpdatesToServer([...configurationChanges.ToAdd, ...configurationChanges.ToModify], configurationChanges.ToDelete, timeout, customHeadersOption, progressCallback); } diff --git a/libraries/azure-app-configuration-importer/src/importOptions.ts b/libraries/azure-app-configuration-importer/src/importOptions.ts index df0636a..912e267 100644 --- a/libraries/azure-app-configuration-importer/src/importOptions.ts +++ b/libraries/azure-app-configuration-importer/src/importOptions.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { PagedAsyncIterableIterator, PageSettings } from "@azure/core-paging"; -import { ConfigurationFormat, ConfigurationProfile } from "./enums"; +import { ConfigurationFormat, ConfigurationProfile, ImportMode } from "./enums"; import { Tags } from "./models"; import { ConfigurationSetting, ListConfigurationSettingPage } from "@azure/app-configuration"; @@ -32,4 +32,20 @@ export type SourceOptions = { */ export type StringSourceOptions = SourceOptions & {data: string; }; export type IterableSourceOptions = Options & { data: PagedAsyncIterableIterator, ListConfigurationSettingPage, PageSettings>; trimPrefix?: string; }; -export type ReadableStreamSourceOptions = SourceOptions & { data: ReadableStream | NodeJS.ReadableStream }; \ No newline at end of file +export type ReadableStreamSourceOptions = SourceOptions & { data: ReadableStream | NodeJS.ReadableStream }; + +/** + * Options for importing configuration settings + */ +export interface ImportOptions { + /** + * Use strict mode or not. + */ + strict?: boolean; + /** + * Determines the behavior when importing key-values. + * The default value, 'All' will import all key-values in the input file to App Configuration. + * 'Ignore-Match' will only import settings that have no matching key-value in App Configuration. + */ + importMode?: ImportMode; +} \ No newline at end of file diff --git a/libraries/azure-app-configuration-importer/src/index.ts b/libraries/azure-app-configuration-importer/src/index.ts index a54e37f..3f45b43 100644 --- a/libraries/azure-app-configuration-importer/src/index.ts +++ b/libraries/azure-app-configuration-importer/src/index.ts @@ -5,7 +5,8 @@ export { AppConfigurationImporter } from "./appConfigurationImporter"; export { StringSourceOptions, IterableSourceOptions, - ReadableStreamSourceOptions + ReadableStreamSourceOptions, + ImportOptions } from "./importOptions"; export * from "./enums"; export * from "./errors"; diff --git a/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts b/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts index 3b73edd..1a0b481 100644 --- a/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/appConfigurationImporter.spec.ts @@ -51,7 +51,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () finished = importProgress.successCount; total = importProgress.importCount; }; - await appConfigurationImporter.Import(stringConfigurationSource, 3, reportImportProgress, false); + await appConfigurationImporter.Import(stringConfigurationSource, 3, reportImportProgress, { strict: false }); assert.equal(finished, 3); assert.equal(total, 3); }); @@ -67,7 +67,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () format: ConfigurationFormat.Json }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); - const importPromise = appConfigurationImporter.Import(stringConfigurationSource, 1, undefined, false); + const importPromise = appConfigurationImporter.Import(stringConfigurationSource, 1, undefined, { strict: false }); importPromise.catch((e) => { expect(e.message).to.eq("server error"); }); @@ -102,7 +102,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); try { - await appConfigurationImporter.Import(stringConfigurationSource, 1, undefined, false); + await appConfigurationImporter.Import(stringConfigurationSource, 1, undefined, { strict: false }); } catch (error) { assert.isTrue(error instanceof OperationTimeoutError); @@ -145,7 +145,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () total = importProgress.importCount; }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); - await appConfigurationImporter.Import(stringConfigurationSource, 10, reportImportProgress, false); + await appConfigurationImporter.Import(stringConfigurationSource, 10, reportImportProgress, { strict: false }); assert.equal(finished, 3); assert.equal(total, 3); }); @@ -160,7 +160,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () format: ConfigurationFormat.Json }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); - await appConfigurationImporter.Import(stringConfigurationSource, 10, undefined, false); + await appConfigurationImporter.Import(stringConfigurationSource, 10, undefined, { strict: false }); }); it("Try import an empty file, no error", async () => { @@ -173,7 +173,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () format: ConfigurationFormat.Json }; const stringConfigurationSource = new StringConfigurationSettingsSource(options); - await appConfigurationImporter.Import(stringConfigurationSource, 10, undefined, false); + await appConfigurationImporter.Import(stringConfigurationSource, 10, undefined, { strict: false }); }); it("Succeed to import simple key value file in strict mode", async () => { @@ -227,7 +227,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () finished = importProgress.successCount; total = importProgress.importCount; }; - await appConfigurationImporter.Import(stringConfigurationSource, 5, reportImportProgress, true); + await appConfigurationImporter.Import(stringConfigurationSource, 10, reportImportProgress, { strict: false }); assert.equal(finished, 3); assert.equal(total, 3); }); @@ -311,7 +311,7 @@ describe("Call Import API to import configuration file to AppConfiguration", () const changesSource = new ConfigurationChangesSource(configurationChanges); try { - await appConfigurationImporter.Import(changesSource, 5, undefined, true, ImportMode.All); + await appConfigurationImporter.Import(changesSource, 5, undefined, { strict: true, importMode: ImportMode.All }); } catch (error) { expect(error).to.be.instanceOf(ArgumentError); diff --git a/libraries/azure-app-configuration-importer/tests/iterableConfigurationSettingsSource.spec.ts b/libraries/azure-app-configuration-importer/tests/iterableConfigurationSettingsSource.spec.ts index 51a5bf8..2833b95 100644 --- a/libraries/azure-app-configuration-importer/tests/iterableConfigurationSettingsSource.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/iterableConfigurationSettingsSource.spec.ts @@ -63,7 +63,7 @@ describe("Iterator configuration source test", () => { finished = importProgress.successCount; total = importProgress.importCount; }; - await appConfigurationImporter.Import(iteratorConfigurationSettingsSource, 3, reportImportProgress, false); + await appConfigurationImporter.Import(iteratorConfigurationSettingsSource, 3, reportImportProgress, { strict: false }); assert.equal(finished, 6); assert.equal(total, 6); }); @@ -100,7 +100,7 @@ describe("Iterator configuration source test", () => { total = importProgress.importCount; }; - await appConfigurationImporter.Import(iteratorConfigurationSettingsSource, 3, reportImportProgress, false, ImportMode.All); + await appConfigurationImporter.Import(iteratorConfigurationSettingsSource, 3, reportImportProgress, { strict: false, importMode: ImportMode.All }); assert.equal(finished, 6); assert.equal(total, 6); }); @@ -137,7 +137,7 @@ describe("Iterator configuration source test", () => { total = importProgress.importCount; }; - await appConfigurationImporter.Import(iteratorConfigurationSettingsSource, 3, reportImportProgress, false, ImportMode.IgnoreMatch); // Ignore-Match is default import mode + await appConfigurationImporter.Import(iteratorConfigurationSettingsSource, 3, reportImportProgress, { strict: false, importMode: ImportMode.IgnoreMatch }); // Ignore-Match is default import mode assert.equal(finished, 0); assert.equal(total, 0); }); diff --git a/libraries/azure-app-configuration-importer/tests/readableStreamConfigurationSettingsSource.spec.ts b/libraries/azure-app-configuration-importer/tests/readableStreamConfigurationSettingsSource.spec.ts index 8dc0f93..fd7ca73 100644 --- a/libraries/azure-app-configuration-importer/tests/readableStreamConfigurationSettingsSource.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/readableStreamConfigurationSettingsSource.spec.ts @@ -161,7 +161,7 @@ describe("Readable stream configuration settings source tests", () => { finished = importProgress.successCount; total = importProgress.importCount; }; - await appConfigurationImporter.Import(readableConfigurationSettingsSource, 3, reportImportProgress, false); + await appConfigurationImporter.Import(readableConfigurationSettingsSource, 3, reportImportProgress, { strict: false }); assert.equal(finished, 3); assert.equal(total, 3); }); From e76d439294f1873b6f72f9fa03c534f9b7f2635c Mon Sep 17 00:00:00 2001 From: cwanjau Date: Tue, 2 Dec 2025 09:33:14 +0300 Subject: [PATCH 23/23] rename file to options --- .../src/appConfigurationImporter.ts | 2 +- libraries/azure-app-configuration-importer/src/index.ts | 2 +- .../src/internal/parsers/configurationSettingsConverter.ts | 2 +- .../internal/parsers/defaultConfigurationSettingsConverter.ts | 2 +- .../azure-app-configuration-importer/src/internal/utils.ts | 2 +- .../src/{importOptions.ts => options.ts} | 0 .../src/settingsImport/iterableConfigurationSettingsSource.ts | 2 +- .../settingsImport/readableStreamConfigurationSettingsSource.ts | 2 +- .../src/settingsImport/stringConfigurationSettingsSource.ts | 2 +- .../tests/featureFlagConfigurationSettingsConverter.spec.ts | 2 +- .../tests/readableStreamConfigurationSettingsSource.spec.ts | 2 +- 11 files changed, 10 insertions(+), 10 deletions(-) rename libraries/azure-app-configuration-importer/src/{importOptions.ts => options.ts} (100%) diff --git a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts index 6394477..6a12bda 100644 --- a/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts +++ b/libraries/azure-app-configuration-importer/src/appConfigurationImporter.ts @@ -17,7 +17,7 @@ import { isConfigSettingEqual } from "./internal/utils"; import { v4 as uuidv4 } from "uuid"; import { Constants } from "./internal/constants"; import { OperationOptions } from "@azure/core-client"; -import { ImportOptions } from "./importOptions"; +import { ImportOptions } from "./options"; /** * Entrypoint class for sync configuration diff --git a/libraries/azure-app-configuration-importer/src/index.ts b/libraries/azure-app-configuration-importer/src/index.ts index 3f45b43..d14f807 100644 --- a/libraries/azure-app-configuration-importer/src/index.ts +++ b/libraries/azure-app-configuration-importer/src/index.ts @@ -7,7 +7,7 @@ export { IterableSourceOptions, ReadableStreamSourceOptions, ImportOptions -} from "./importOptions"; +} from "./options"; export * from "./enums"; export * from "./errors"; export { ImportProgress as ImportResult, ConfigurationChanges } from "./models"; diff --git a/libraries/azure-app-configuration-importer/src/internal/parsers/configurationSettingsConverter.ts b/libraries/azure-app-configuration-importer/src/internal/parsers/configurationSettingsConverter.ts index d138065..f111563 100644 --- a/libraries/azure-app-configuration-importer/src/internal/parsers/configurationSettingsConverter.ts +++ b/libraries/azure-app-configuration-importer/src/internal/parsers/configurationSettingsConverter.ts @@ -6,7 +6,7 @@ import { FeatureFlagValue, SecretReferenceValue } from "@azure/app-configuration"; -import { SourceOptions } from "../../importOptions"; +import { SourceOptions } from "../../options"; /** * ConfigurationSettings converter for different configuration content. diff --git a/libraries/azure-app-configuration-importer/src/internal/parsers/defaultConfigurationSettingsConverter.ts b/libraries/azure-app-configuration-importer/src/internal/parsers/defaultConfigurationSettingsConverter.ts index e050819..a4c1582 100644 --- a/libraries/azure-app-configuration-importer/src/internal/parsers/defaultConfigurationSettingsConverter.ts +++ b/libraries/azure-app-configuration-importer/src/internal/parsers/defaultConfigurationSettingsConverter.ts @@ -7,7 +7,7 @@ import { featureFlagContentType, SecretReferenceValue } from "@azure/app-configuration"; -import { SourceOptions } from "../../importOptions"; +import { SourceOptions } from "../../options"; import { ConfigurationSettingsConverter } from "./configurationSettingsConverter"; import { AjvValidationError, ArgumentError } from "../../errors"; import { ClientFilter } from "../../models"; diff --git a/libraries/azure-app-configuration-importer/src/internal/utils.ts b/libraries/azure-app-configuration-importer/src/internal/utils.ts index 4081604..9667276 100644 --- a/libraries/azure-app-configuration-importer/src/internal/utils.ts +++ b/libraries/azure-app-configuration-importer/src/internal/utils.ts @@ -9,7 +9,7 @@ import { SecretReferenceValue } from "@azure/app-configuration"; import { isEmpty, isEqual } from "lodash"; import { Tags, FeatureFlagClientFilters } from "../models"; -import { SourceOptions } from "../importOptions"; +import { SourceOptions } from "../options"; import { ConfigurationFormat, ConfigurationProfile } from "../enums"; import { ArgumentError, ArgumentNullError } from "../errors"; import { Constants } from "../internal/constants"; diff --git a/libraries/azure-app-configuration-importer/src/importOptions.ts b/libraries/azure-app-configuration-importer/src/options.ts similarity index 100% rename from libraries/azure-app-configuration-importer/src/importOptions.ts rename to libraries/azure-app-configuration-importer/src/options.ts diff --git a/libraries/azure-app-configuration-importer/src/settingsImport/iterableConfigurationSettingsSource.ts b/libraries/azure-app-configuration-importer/src/settingsImport/iterableConfigurationSettingsSource.ts index 270ca21..df9f637 100644 --- a/libraries/azure-app-configuration-importer/src/settingsImport/iterableConfigurationSettingsSource.ts +++ b/libraries/azure-app-configuration-importer/src/settingsImport/iterableConfigurationSettingsSource.ts @@ -13,7 +13,7 @@ import { featureFlagContentType, featureFlagPrefix, secretReferenceContentType} from "@azure/app-configuration"; -import { IterableSourceOptions } from "../importOptions"; +import { IterableSourceOptions } from "../options"; import { ArgumentError } from "../errors"; export class IterableConfigurationSettingsSource implements ConfigurationSettingsSource { diff --git a/libraries/azure-app-configuration-importer/src/settingsImport/readableStreamConfigurationSettingsSource.ts b/libraries/azure-app-configuration-importer/src/settingsImport/readableStreamConfigurationSettingsSource.ts index 837272b..2d67619 100644 --- a/libraries/azure-app-configuration-importer/src/settingsImport/readableStreamConfigurationSettingsSource.ts +++ b/libraries/azure-app-configuration-importer/src/settingsImport/readableStreamConfigurationSettingsSource.ts @@ -3,7 +3,7 @@ import { SetConfigurationSettingParam, FeatureFlagValue, SecretReferenceValue, ListConfigurationSettingsOptions } from "@azure/app-configuration"; import { toWebStream } from "../internal/stream"; -import { ReadableStreamSourceOptions, SourceOptions } from "../importOptions"; +import { ReadableStreamSourceOptions, SourceOptions } from "../options"; import { ConfigurationSettingsSource } from "./configurationSettingsSource"; import { ConfigurationProfile } from "../enums"; import { StringConfigurationSettingsSource } from "./stringConfigurationSettingsSource"; diff --git a/libraries/azure-app-configuration-importer/src/settingsImport/stringConfigurationSettingsSource.ts b/libraries/azure-app-configuration-importer/src/settingsImport/stringConfigurationSettingsSource.ts index 62be11f..40d489e 100644 --- a/libraries/azure-app-configuration-importer/src/settingsImport/stringConfigurationSettingsSource.ts +++ b/libraries/azure-app-configuration-importer/src/settingsImport/stringConfigurationSettingsSource.ts @@ -10,7 +10,7 @@ import { import * as jsyaml from "js-yaml"; import stripJSONComments from "strip-json-comments"; import { getProperties } from "properties-file"; -import { SourceOptions, StringSourceOptions } from "../importOptions"; +import { SourceOptions, StringSourceOptions } from "../options"; import { ConfigurationSettingsSource } from "./configurationSettingsSource"; import { ConfigurationFormat, ConfigurationProfile } from "../enums"; import { ArgumentError, ParseError } from "../errors"; diff --git a/libraries/azure-app-configuration-importer/tests/featureFlagConfigurationSettingsConverter.spec.ts b/libraries/azure-app-configuration-importer/tests/featureFlagConfigurationSettingsConverter.spec.ts index 7a7c653..6ef6aa2 100644 --- a/libraries/azure-app-configuration-importer/tests/featureFlagConfigurationSettingsConverter.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/featureFlagConfigurationSettingsConverter.spec.ts @@ -11,7 +11,7 @@ import * as fs from "fs"; import { ConfigurationFormat } from "../src/enums"; import { ArgumentError } from "../src/errors"; import { StringConfigurationSettingsSource } from "../src/settingsImport/stringConfigurationSettingsSource"; -import { StringSourceOptions } from "../src/importOptions"; +import { StringSourceOptions } from "../src/options"; import { assertThrowAsync } from "./utlis"; import { MsFeatureFlagValue } from "../src/featureFlag"; diff --git a/libraries/azure-app-configuration-importer/tests/readableStreamConfigurationSettingsSource.spec.ts b/libraries/azure-app-configuration-importer/tests/readableStreamConfigurationSettingsSource.spec.ts index fd7ca73..dd2e6f5 100644 --- a/libraries/azure-app-configuration-importer/tests/readableStreamConfigurationSettingsSource.spec.ts +++ b/libraries/azure-app-configuration-importer/tests/readableStreamConfigurationSettingsSource.spec.ts @@ -11,7 +11,7 @@ import { AppConfigurationImporter } from "../src/appConfigurationImporter"; import { MockUpHttpHeaderLike, MockupResourceLike } from "./appConfigurationImporter.spec"; import { ConfigurationFormat, ConfigurationProfile } from "../src/enums"; import { ReadableStreamConfigurationSettingsSource } from "../src/settingsImport/readableStreamConfigurationSettingsSource"; -import { ReadableStreamSourceOptions } from "../src/importOptions"; +import { ReadableStreamSourceOptions } from "../src/options"; import { assertThrowAsync, listConfigurationSettings } from "./utlis"; import { ParseError } from "../src/errors";