@azure/app-configuration-importer 1.0.0-preview → 1.0.1-preview
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +67 -0
- package/dist/index.js +77 -10
- package/dist/index.js.map +1 -1
- package/dist-esm/src/appConfigurationImporter.js +157 -0
- package/dist-esm/src/appConfigurationImporter.js.map +1 -0
- package/dist-esm/src/enums.js +28 -0
- package/dist-esm/src/enums.js.map +1 -0
- package/dist-esm/src/errors.js +35 -0
- package/dist-esm/src/errors.js.map +1 -0
- package/dist-esm/src/importOptions.js +4 -0
- package/dist-esm/src/importOptions.js.map +1 -0
- package/dist-esm/src/index.js +9 -0
- package/dist-esm/src/index.js.map +1 -0
- package/dist-esm/src/internal/adaptiveTaskManager.js +69 -0
- package/dist-esm/src/internal/adaptiveTaskManager.js.map +1 -0
- package/dist-esm/src/internal/constants.js +29 -0
- package/dist-esm/src/internal/constants.js.map +1 -0
- package/dist-esm/src/internal/parsers/configurationSettingsConverter.js +4 -0
- package/dist-esm/src/internal/parsers/configurationSettingsConverter.js.map +1 -0
- package/dist-esm/src/internal/parsers/defaultConfigurationSettingsConverter.js +200 -0
- package/dist-esm/src/internal/parsers/defaultConfigurationSettingsConverter.js.map +1 -0
- package/dist-esm/src/internal/parsers/kvSetConfigurationSettingsConverter.js +66 -0
- package/dist-esm/src/internal/parsers/kvSetConfigurationSettingsConverter.js.map +1 -0
- package/dist-esm/src/internal/stream.browser.js +16 -0
- package/dist-esm/src/internal/stream.browser.js.map +1 -0
- package/dist-esm/src/internal/stream.js +15 -0
- package/dist-esm/src/internal/stream.js.map +1 -0
- package/dist-esm/src/internal/utils.js +129 -0
- package/dist-esm/src/internal/utils.js.map +1 -0
- package/dist-esm/src/models.js +4 -0
- package/dist-esm/src/models.js.map +1 -0
- package/dist-esm/src/settingsImport/configurationSettingsSource.js +4 -0
- package/dist-esm/src/settingsImport/configurationSettingsSource.js.map +1 -0
- package/dist-esm/src/settingsImport/iterableConfigurationSettingsSource.js +83 -0
- package/dist-esm/src/settingsImport/iterableConfigurationSettingsSource.js.map +1 -0
- package/dist-esm/src/settingsImport/readableStreamConfigurationSettingsSource.js +49 -0
- package/dist-esm/src/settingsImport/readableStreamConfigurationSettingsSource.js.map +1 -0
- package/dist-esm/src/settingsImport/stringConfigurationSettingsSource.js +92 -0
- package/dist-esm/src/settingsImport/stringConfigurationSettingsSource.js.map +1 -0
- package/package.json +19 -5
- package/types/src/appConfigurationImporter.d.ts +37 -0
- package/types/src/appConfigurationImporter.d.ts.map +1 -0
- package/types/src/enums.d.ts +23 -0
- package/types/src/enums.d.ts.map +1 -0
- package/types/src/errors.d.ts +25 -0
- package/types/src/errors.d.ts.map +1 -0
- package/types/src/importOptions.d.ts +39 -0
- package/types/src/importOptions.d.ts.map +1 -0
- package/types/src/index.d.ts +10 -0
- package/types/src/index.d.ts.map +1 -0
- package/types/src/internal/adaptiveTaskManager.d.ts +20 -0
- package/types/src/internal/adaptiveTaskManager.d.ts.map +1 -0
- package/types/src/internal/constants.d.ts +8 -0
- package/types/src/internal/constants.d.ts.map +1 -0
- package/types/src/internal/parsers/configurationSettingsConverter.d.ts +11 -0
- package/types/src/internal/parsers/configurationSettingsConverter.d.ts.map +1 -0
- package/types/src/internal/parsers/defaultConfigurationSettingsConverter.d.ts +20 -0
- package/types/src/internal/parsers/defaultConfigurationSettingsConverter.d.ts.map +1 -0
- package/types/src/internal/parsers/kvSetConfigurationSettingsConverter.d.ts +15 -0
- package/types/src/internal/parsers/kvSetConfigurationSettingsConverter.d.ts.map +1 -0
- package/types/src/internal/stream.browser.d.ts +4 -0
- package/types/src/internal/stream.browser.d.ts.map +1 -0
- package/types/src/internal/stream.d.ts +4 -0
- package/types/src/internal/stream.d.ts.map +1 -0
- package/types/src/internal/utils.d.ts +17 -0
- package/types/src/internal/utils.d.ts.map +1 -0
- package/types/src/models.d.ts +42 -0
- package/types/src/models.d.ts.map +1 -0
- package/types/src/settingsImport/configurationSettingsSource.d.ts +19 -0
- package/types/src/settingsImport/configurationSettingsSource.d.ts.map +1 -0
- package/types/src/settingsImport/iterableConfigurationSettingsSource.d.ts +14 -0
- package/types/src/settingsImport/iterableConfigurationSettingsSource.d.ts.map +1 -0
- package/types/src/settingsImport/readableStreamConfigurationSettingsSource.d.ts +11 -0
- package/types/src/settingsImport/readableStreamConfigurationSettingsSource.d.ts.map +1 -0
- package/types/src/settingsImport/stringConfigurationSettingsSource.d.ts +24 -0
- package/types/src/settingsImport/stringConfigurationSettingsSource.d.ts.map +1 -0
- package/NOTICE.txt +0 -370
- package/app-configuration-importer.test.log +0 -164
- package/dist/app-configuration-importer.api.md +0 -136
- package/examples/README.md +0 -59
- package/examples/package.json +0 -32
- package/examples/sample.env +0 -6
- package/examples/testFiles/default.json +0 -24
- package/examples/testFiles/kvset.json +0 -35
- package/types/tsdoc-metadata.json +0 -11
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
4
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
5
|
+
var m = o[Symbol.asyncIterator], i;
|
|
6
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
7
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
8
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
9
|
+
};
|
|
10
|
+
import { ImportMode } from "./enums";
|
|
11
|
+
import { OperationTimeoutError, ArgumentError } from "./errors";
|
|
12
|
+
import { AdaptiveTaskManager } from "./internal/adaptiveTaskManager";
|
|
13
|
+
import { isConfigSettingEqual } from "./internal/utils";
|
|
14
|
+
import { v4 as uuidv4 } from "uuid";
|
|
15
|
+
import { Constants } from "./internal/constants";
|
|
16
|
+
/**
|
|
17
|
+
* Entrypoint class for sync configuration
|
|
18
|
+
*/
|
|
19
|
+
export class AppConfigurationImporter {
|
|
20
|
+
/**
|
|
21
|
+
* Initializes a new instance of the AppConfigurationSync class.
|
|
22
|
+
* @param configurationClient - App configuration client for manipulate the target App Configuration.
|
|
23
|
+
*/
|
|
24
|
+
constructor(configurationClient) {
|
|
25
|
+
this.configurationClient = configurationClient;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Import source settings into the Azure App Configuration service
|
|
29
|
+
*
|
|
30
|
+
* Example usage:
|
|
31
|
+
* ```ts
|
|
32
|
+
* const fileData = fs.readFileSync("mylocalPath").toString();
|
|
33
|
+
* const result = await asyncClient.Import(new StringConfigurationSettingsSource({data:fileData, format: ConfigurationFormat.Json}));
|
|
34
|
+
* ```
|
|
35
|
+
* @param configSettingsSource - A ConfigurationSettingsSource instance.
|
|
36
|
+
* @param strict - Use strict mode or not.
|
|
37
|
+
* @param timeout - Seconds of entire import progress timeout
|
|
38
|
+
* @param progressCallback - Callback for report the progress of import
|
|
39
|
+
* @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.
|
|
40
|
+
* @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
|
|
41
|
+
*/
|
|
42
|
+
async Import(configSettingsSource, timeout, strict = false, progressCallback, importMode, dryRun) {
|
|
43
|
+
var _a, e_1, _b, _c;
|
|
44
|
+
if (importMode == undefined) {
|
|
45
|
+
importMode = ImportMode.IgnoreMatch;
|
|
46
|
+
}
|
|
47
|
+
if (dryRun == undefined) {
|
|
48
|
+
dryRun = false;
|
|
49
|
+
}
|
|
50
|
+
this.validateImportMode(importMode);
|
|
51
|
+
const configSettings = await configSettingsSource.GetConfigurationSettings();
|
|
52
|
+
const configurationSettingToDelete = [];
|
|
53
|
+
const srcKeyLabelLookUp = {};
|
|
54
|
+
configSettings.forEach((config) => {
|
|
55
|
+
if (!srcKeyLabelLookUp[config.key]) {
|
|
56
|
+
srcKeyLabelLookUp[config.key] = {};
|
|
57
|
+
}
|
|
58
|
+
srcKeyLabelLookUp[config.key][config.label || ""] = true;
|
|
59
|
+
});
|
|
60
|
+
// generate correlationRequestId for operations in the same activity
|
|
61
|
+
const customCorrelationRequestId = uuidv4();
|
|
62
|
+
const customHeadersOption = {
|
|
63
|
+
requestOptions: {
|
|
64
|
+
customHeaders: {
|
|
65
|
+
[Constants.CorrelationRequestIdHeader]: customCorrelationRequestId
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
if (strict || importMode == ImportMode.IgnoreMatch) {
|
|
70
|
+
try {
|
|
71
|
+
for (var _d = true, _e = __asyncValues(this.configurationClient.listConfigurationSettings(Object.assign(Object.assign({}, configSettingsSource.FilterOptions), customHeadersOption))), _f; _f = await _e.next(), _a = _f.done, !_a;) {
|
|
72
|
+
_c = _f.value;
|
|
73
|
+
_d = false;
|
|
74
|
+
try {
|
|
75
|
+
const existing = _c;
|
|
76
|
+
const isKeyLabelPresent = srcKeyLabelLookUp[existing.key] && srcKeyLabelLookUp[existing.key][existing.label || ""];
|
|
77
|
+
if (strict && !isKeyLabelPresent) {
|
|
78
|
+
configurationSettingToDelete.push(existing);
|
|
79
|
+
}
|
|
80
|
+
if (importMode == ImportMode.IgnoreMatch) {
|
|
81
|
+
const incoming = configSettings.find(configSetting => configSetting.key == existing.key &&
|
|
82
|
+
configSetting.label == existing.label);
|
|
83
|
+
if (incoming && isConfigSettingEqual(incoming, existing)) {
|
|
84
|
+
configSettings.splice(configSettings.indexOf(incoming), 1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
_d = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
94
|
+
finally {
|
|
95
|
+
try {
|
|
96
|
+
if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
|
|
97
|
+
}
|
|
98
|
+
finally { if (e_1) throw e_1.error; }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (dryRun) {
|
|
102
|
+
this.printUpdatesToConsole(configSettings, configurationSettingToDelete);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
await this.applyUpdatesToServer(configSettings, configurationSettingToDelete, timeout, customHeadersOption, progressCallback);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
printUpdatesToConsole(settingsToAdd, settingsToDelete) {
|
|
109
|
+
console.log("The following settings will be removed from App Configuration:");
|
|
110
|
+
for (const setting of settingsToDelete) {
|
|
111
|
+
console.log(JSON.stringify({ key: setting.key, label: setting.label, contentType: setting.contentType, tags: setting.tags }));
|
|
112
|
+
}
|
|
113
|
+
console.log("\nThe following settings will be written to App Configuration:");
|
|
114
|
+
for (const setting of settingsToAdd) {
|
|
115
|
+
console.log(JSON.stringify({ key: setting.key, label: setting.label, contentType: setting.contentType, tags: setting.tags }));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async applyUpdatesToServer(settingsToAdd, settingsToDelete, timeout, options, progressCallback) {
|
|
119
|
+
const deleteTaskManager = this.newAdaptiveTaskManager((setting) => this.configurationClient.deleteConfigurationSetting(setting, options), settingsToDelete);
|
|
120
|
+
const startTime = Date.now();
|
|
121
|
+
await this.executeTasksWithTimeout(deleteTaskManager, timeout);
|
|
122
|
+
const endTime = Date.now();
|
|
123
|
+
const deleteTimeConsumed = (endTime - startTime) / 1000;
|
|
124
|
+
timeout -= deleteTimeConsumed;
|
|
125
|
+
const importTaskManager = this.newAdaptiveTaskManager((setting) => this.configurationClient.setConfigurationSetting(setting, options), settingsToAdd);
|
|
126
|
+
await this.executeTasksWithTimeout(importTaskManager, timeout, progressCallback);
|
|
127
|
+
}
|
|
128
|
+
newAdaptiveTaskManager(task, configurationSettings) {
|
|
129
|
+
let index = 0;
|
|
130
|
+
return new AdaptiveTaskManager(() => {
|
|
131
|
+
if (index == configurationSettings.length) {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
const configSet = configurationSettings[index++];
|
|
135
|
+
return async () => {
|
|
136
|
+
return task(configSet);
|
|
137
|
+
};
|
|
138
|
+
}, configurationSettings.length);
|
|
139
|
+
}
|
|
140
|
+
async executeTasksWithTimeout(taskManager, timeInSeconds, callback) {
|
|
141
|
+
let timer;
|
|
142
|
+
const taskPromise = taskManager.Start(callback);
|
|
143
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
144
|
+
timer = setTimeout(() => reject(new OperationTimeoutError()), timeInSeconds * 1000);
|
|
145
|
+
});
|
|
146
|
+
await Promise.race([taskPromise, timeoutPromise]).finally(() => {
|
|
147
|
+
clearTimeout(timer); // clear timeout when importPromise successfully resolve or faultily reject.
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
validateImportMode(importMode) {
|
|
151
|
+
if (importMode && !(importMode == ImportMode.IgnoreMatch ||
|
|
152
|
+
importMode == ImportMode.All)) {
|
|
153
|
+
throw new ArgumentError("Only options supported for Import Mode are 'All' and 'Ignore-Match'.");
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=appConfigurationImporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appConfigurationImporter.js","sourceRoot":"","sources":["../../src/appConfigurationImporter.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { \n AppConfigurationClient, \n ConfigurationSetting, \n SetConfigurationSettingParam, \n FeatureFlagValue, \n SecretReferenceValue } from \"@azure/app-configuration\";\nimport { ConfigurationSettingsSource } from \"./settingsImport/configurationSettingsSource\";\nimport { ImportMode } from \"./enums\";\nimport { OperationTimeoutError, ArgumentError } from \"./errors\";\nimport { AdaptiveTaskManager } from \"./internal/adaptiveTaskManager\";\nimport { ImportProgress, KeyLabelLookup } from \"./models\";\nimport { isConfigSettingEqual } from \"./internal/utils\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport { Constants } from \"./internal/constants\";\nimport { OperationOptions } from \"@azure/core-client\";\n\n/**\n * Entrypoint class for sync configuration\n */\nexport class AppConfigurationImporter {\n private configurationClient: AppConfigurationClient;\n /**\n * Initializes a new instance of the AppConfigurationSync class.\n * @param configurationClient - App configuration client for manipulate the target App Configuration.\n */\n constructor(configurationClient: AppConfigurationClient) {\n this.configurationClient = configurationClient;\n }\n\n /**\n * Import source settings into the Azure App Configuration service\n *\n * Example usage:\n * ```ts\n * const fileData = fs.readFileSync(\"mylocalPath\").toString();\n * const result = await asyncClient.Import(new StringConfigurationSettingsSource({data:fileData, format: ConfigurationFormat.Json}));\n * ```\n * @param configSettingsSource - A ConfigurationSettingsSource instance.\n * @param strict - Use strict mode or not.\n * @param timeout - Seconds of entire import progress timeout\n * @param progressCallback - Callback for report the progress of import\n * @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.\n * @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\n */\n public async Import(\n configSettingsSource: ConfigurationSettingsSource,\n timeout: number,\n strict = false,\n progressCallback?: (progress: ImportProgress) => unknown,\n importMode?: ImportMode,\n dryRun?: boolean\n ) {\n if (importMode == undefined) {\n importMode = ImportMode.IgnoreMatch;\n }\n if (dryRun == undefined) {\n dryRun = false;\n }\n this.validateImportMode(importMode);\n\n const configSettings = await configSettingsSource.GetConfigurationSettings();\n\n const configurationSettingToDelete: ConfigurationSetting<string>[] = [];\n const srcKeyLabelLookUp: KeyLabelLookup = {};\n \n configSettings.forEach((config: SetConfigurationSettingParam<string | FeatureFlagValue | SecretReferenceValue>) => {\n if (!srcKeyLabelLookUp[config.key]) {\n srcKeyLabelLookUp[config.key] = {};\n }\n srcKeyLabelLookUp[config.key][config.label || \"\"] = true;\n });\n\n // generate correlationRequestId for operations in the same activity\n const customCorrelationRequestId: string = uuidv4();\n const customHeadersOption: OperationOptions = {\n requestOptions: {\n customHeaders: {\n [Constants.CorrelationRequestIdHeader]: customCorrelationRequestId\n }\n }\n };\n\n if (strict || importMode == ImportMode.IgnoreMatch) {\n for await (const existing of this.configurationClient.listConfigurationSettings({...configSettingsSource.FilterOptions, ...customHeadersOption})) {\n\n const isKeyLabelPresent: boolean = srcKeyLabelLookUp[existing.key] && srcKeyLabelLookUp[existing.key][existing.label || \"\"];\n \n if (strict && !isKeyLabelPresent) {\n configurationSettingToDelete.push(existing);\n }\n \n if (importMode == ImportMode.IgnoreMatch) {\n const incoming = configSettings.find(configSetting => configSetting.key == existing.key && \n configSetting.label == existing.label);\n \n if (incoming && isConfigSettingEqual(incoming, existing)) {\n configSettings.splice(configSettings.indexOf(incoming), 1);\n }\n }\n }\n }\n \n if (dryRun) {\n this.printUpdatesToConsole(configSettings, configurationSettingToDelete);\n }\n else {\n await this.applyUpdatesToServer(configSettings, configurationSettingToDelete, timeout, customHeadersOption, progressCallback);\n }\n }\n\n private printUpdatesToConsole(\n settingsToAdd: SetConfigurationSettingParam<string | FeatureFlagValue | SecretReferenceValue>[], \n settingsToDelete: ConfigurationSetting<string>[]\n ): void {\n console.log(\"The following settings will be removed from App Configuration:\");\n for (const setting of settingsToDelete) {\n\n console.log(JSON.stringify({key: setting.key, label: setting.label, contentType: setting.contentType, tags: setting.tags}));\n } \n\n console.log(\"\\nThe following settings will be written to App Configuration:\");\n for (const setting of settingsToAdd) {\n\n console.log(JSON.stringify({key: setting.key, label: setting.label, contentType: setting.contentType, tags: setting.tags}));\n }\n }\n\n private async applyUpdatesToServer(\n settingsToAdd: SetConfigurationSettingParam<string | FeatureFlagValue | SecretReferenceValue>[], \n settingsToDelete: ConfigurationSetting<string>[],\n timeout: number,\n options: OperationOptions,\n progressCallback?: (progress: ImportProgress) => unknown | undefined\n ): Promise<void> {\n const deleteTaskManager = this.newAdaptiveTaskManager((setting) => this.configurationClient.deleteConfigurationSetting(setting, options), settingsToDelete);\n const startTime = Date.now();\n await this.executeTasksWithTimeout(deleteTaskManager, timeout);\n const endTime = Date.now();\n const deleteTimeConsumed = (endTime - startTime) / 1000;\n timeout -= deleteTimeConsumed;\n\n const importTaskManager = this.newAdaptiveTaskManager((setting) => this.configurationClient.setConfigurationSetting(setting, options), settingsToAdd);\n await this.executeTasksWithTimeout(importTaskManager, timeout, progressCallback);\n }\n\n private newAdaptiveTaskManager<T>(task: (setting: T) => Promise<any>, configurationSettings: Array<T>) {\n let index = 0;\n return new AdaptiveTaskManager(() => {\n if (index == configurationSettings.length) {\n return undefined;\n }\n const configSet = configurationSettings[index++];\n\n return async () => {\n return task(configSet);\n };\n }, configurationSettings.length);\n }\n\n private async executeTasksWithTimeout<T>(taskManager: AdaptiveTaskManager<T>, timeInSeconds: number, callback?: (progress: ImportProgress) => unknown) {\n let timer: NodeJS.Timeout;\n const taskPromise = taskManager.Start(callback);\n const timeoutPromise = new Promise((_, reject) => {\n timer = setTimeout(() => reject(new OperationTimeoutError()), timeInSeconds * 1000);\n });\n await Promise.race([taskPromise, timeoutPromise]).finally(() => {\n clearTimeout(timer); // clear timeout when importPromise successfully resolve or faultily reject.\n });\n }\n\n private validateImportMode(importMode: ImportMode): void {\n if (importMode && !(importMode == ImportMode.IgnoreMatch || \n importMode == ImportMode.All)) {\n throw new ArgumentError(\"Only options supported for Import Mode are 'All' and 'Ignore-Match'.\");\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
/**
|
|
4
|
+
* Enums of configuration Format.
|
|
5
|
+
*/
|
|
6
|
+
export var ConfigurationFormat;
|
|
7
|
+
(function (ConfigurationFormat) {
|
|
8
|
+
ConfigurationFormat[ConfigurationFormat["Json"] = 0] = "Json";
|
|
9
|
+
ConfigurationFormat[ConfigurationFormat["Properties"] = 1] = "Properties";
|
|
10
|
+
ConfigurationFormat[ConfigurationFormat["Yaml"] = 2] = "Yaml";
|
|
11
|
+
})(ConfigurationFormat || (ConfigurationFormat = {}));
|
|
12
|
+
/**
|
|
13
|
+
* Enums of configuration Profile.
|
|
14
|
+
*/
|
|
15
|
+
export var ConfigurationProfile;
|
|
16
|
+
(function (ConfigurationProfile) {
|
|
17
|
+
ConfigurationProfile[ConfigurationProfile["Default"] = 0] = "Default";
|
|
18
|
+
ConfigurationProfile[ConfigurationProfile["KvSet"] = 1] = "KvSet";
|
|
19
|
+
})(ConfigurationProfile || (ConfigurationProfile = {}));
|
|
20
|
+
/**
|
|
21
|
+
* Enums of import mode.
|
|
22
|
+
*/
|
|
23
|
+
export var ImportMode;
|
|
24
|
+
(function (ImportMode) {
|
|
25
|
+
ImportMode[ImportMode["All"] = 0] = "All";
|
|
26
|
+
ImportMode[ImportMode["IgnoreMatch"] = 1] = "IgnoreMatch";
|
|
27
|
+
})(ImportMode || (ImportMode = {}));
|
|
28
|
+
//# sourceMappingURL=enums.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enums.js","sourceRoot":"","sources":["../../src/enums.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC;;GAEG;AACH,MAAM,CAAN,IAAY,mBAIX;AAJD,WAAY,mBAAmB;IAC7B,6DAAI,CAAA;IACJ,yEAAU,CAAA;IACV,6DAAI,CAAA;AACN,CAAC,EAJW,mBAAmB,KAAnB,mBAAmB,QAI9B;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,oBAGX;AAHD,WAAY,oBAAoB;IAC9B,qEAAO,CAAA;IACP,iEAAK,CAAA;AACP,CAAC,EAHW,oBAAoB,KAApB,oBAAoB,QAG/B;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,UAGX;AAHD,WAAY,UAAU;IACpB,yCAAG,CAAA;IACH,yDAAW,CAAA;AACb,CAAC,EAHW,UAAU,KAAV,UAAU,QAGrB","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/**\n * Enums of configuration Format.\n */\nexport enum ConfigurationFormat {\n Json,\n Properties,\n Yaml,\n}\n\n/**\n * Enums of configuration Profile.\n */\nexport enum ConfigurationProfile {\n Default,\n KvSet,\n}\n\n/**\n * Enums of import mode.\n */\nexport enum ImportMode {\n All,\n IgnoreMatch\n}"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
/**
|
|
4
|
+
* Custom error type used during input validation
|
|
5
|
+
*/
|
|
6
|
+
export class ArgumentError extends Error {
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super();
|
|
9
|
+
this.message = message;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Custom error type used during configuration file parsing
|
|
14
|
+
*/
|
|
15
|
+
export class ParseError extends Error {
|
|
16
|
+
constructor(message) {
|
|
17
|
+
super();
|
|
18
|
+
this.message = message;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Custom error type used for set configuration timeout error
|
|
23
|
+
*/
|
|
24
|
+
export class OperationTimeoutError extends Error {
|
|
25
|
+
constructor() {
|
|
26
|
+
super();
|
|
27
|
+
this.message = "The operation failed to complete within the specified time limit.";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Custom error type used when null arguments are passed
|
|
32
|
+
*/
|
|
33
|
+
export class ArgumentNullError extends Error {
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC;;GAEG;AACH,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAe;QACzB,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IAGnC,YAAY,OAAe;QACzB,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9C;QACE,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,GAAG,mEAAmE,CAAC;IACrF,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;CAAG","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/**\n * Custom error type used during input validation\n */\nexport class ArgumentError extends Error {\n constructor(message: string) {\n super();\n\n this.message = message;\n }\n}\n\n/**\n * Custom error type used during configuration file parsing\n */\nexport class ParseError extends Error {\n public message: string;\n\n constructor(message: string) {\n super();\n\n this.message = message;\n }\n}\n\n/**\n * Custom error type used for set configuration timeout error\n */\nexport class OperationTimeoutError extends Error {\n constructor() {\n super();\n this.message = \"The operation failed to complete within the specified time limit.\";\n }\n}\n\n/**\n * Custom error type used when null arguments are passed\n */\nexport class ArgumentNullError extends Error {}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"importOptions.js","sourceRoot":"","sources":["../../src/importOptions.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport { PagedAsyncIterableIterator, PageSettings } from \"@azure/core-paging\";\nimport { ConfigurationFormat, ConfigurationProfile } from \"./enums\";\nimport { Tags } from \"./models\";\nimport { ConfigurationSetting, ListConfigurationSettingPage } from \"@azure/app-configuration\";\n\ntype Options = {\n label?: string;\n skipFeatureFlags?: boolean;\n prefix?: string;\n contentType?: string;\n tags?: Tags;\n}\n\n/**\n * Base options for configuration import\n *\n * @internal\n */\nexport type SourceOptions = {\n format: ConfigurationFormat;\n separator?: string;\n depth?: number;\n profile?: ConfigurationProfile;\n} & Options;\n\n/**\n * Provides options for importing from string data source\n *\n */\nexport type StringSourceOptions = SourceOptions & {data: string; };\nexport type IterableSourceOptions = Options & { data: PagedAsyncIterableIterator<ConfigurationSetting<string>, ListConfigurationSettingPage, PageSettings>; trimPrefix?: string; };\nexport type ReadableStreamSourceOptions = SourceOptions & { data: ReadableStream<Uint8Array> | NodeJS.ReadableStream };"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
export { AppConfigurationImporter } from "./appConfigurationImporter";
|
|
4
|
+
export * from "./enums";
|
|
5
|
+
export * from "./errors";
|
|
6
|
+
export { StringConfigurationSettingsSource } from "./settingsImport/stringConfigurationSettingsSource";
|
|
7
|
+
export { IterableConfigurationSettingsSource } from "./settingsImport/iterableConfigurationSettingsSource";
|
|
8
|
+
export { ReadableStreamConfigurationSettingsSource } from "./settingsImport/readableStreamConfigurationSettingsSource";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAMtE,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AAEzB,OAAO,EAAE,iCAAiC,EAAE,MAAM,oDAAoD,CAAC;AAEvG,OAAO,EAAE,mCAAmC,EAAE,MAAM,sDAAsD,CAAC;AAC3G,OAAO,EAAE,yCAAyC,EAAE,MAAM,4DAA4D,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nexport { AppConfigurationImporter } from \"./appConfigurationImporter\";\nexport {\n StringSourceOptions,\n IterableSourceOptions,\n ReadableStreamSourceOptions\n} from \"./importOptions\";\nexport * from \"./enums\";\nexport * from \"./errors\";\nexport { ImportProgress as ImportResult } from \"./models\";\nexport { StringConfigurationSettingsSource } from \"./settingsImport/stringConfigurationSettingsSource\";\nexport { ConfigurationSettingsSource } from \"./settingsImport/configurationSettingsSource\";\nexport { IterableConfigurationSettingsSource } from \"./settingsImport/iterableConfigurationSettingsSource\";\nexport { ReadableStreamConfigurationSettingsSource } from \"./settingsImport/readableStreamConfigurationSettingsSource\";"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
import { BehaviorSubject } from "rxjs";
|
|
4
|
+
/** @internal */
|
|
5
|
+
export class AdaptiveTaskManager {
|
|
6
|
+
constructor(fn, size) {
|
|
7
|
+
this.end = false;
|
|
8
|
+
this.currentMax = 1;
|
|
9
|
+
this.limitation = 50;
|
|
10
|
+
this.finishedCount = 0;
|
|
11
|
+
this.throttledTasks = [];
|
|
12
|
+
this.runningTasks = new Map();
|
|
13
|
+
this.taskWrapper = fn;
|
|
14
|
+
this.observable = new BehaviorSubject(0);
|
|
15
|
+
this.importProgress = {
|
|
16
|
+
importCount: size,
|
|
17
|
+
successCount: 0
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
async Start(callback) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
this.observable.subscribe((value) => {
|
|
23
|
+
if (this.end && this.throttledTasks.length == 0) {
|
|
24
|
+
if (this.runningTasks.size == 0)
|
|
25
|
+
resolve(this.finishedCount);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
while (this.runningTasks.size < this.currentMax) {
|
|
29
|
+
const task = this.throttledTasks.length > 0 ? this.throttledTasks.pop() : this.taskWrapper();
|
|
30
|
+
if (!task) {
|
|
31
|
+
this.end = true;
|
|
32
|
+
this.observable.next(++value);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const runningPromise = task();
|
|
36
|
+
runningPromise.then((value) => {
|
|
37
|
+
if (value) {
|
|
38
|
+
this.importProgress.successCount = ++this.finishedCount;
|
|
39
|
+
// If import can complete successfully and it didn't hit the limitation, increase currentMax.
|
|
40
|
+
if (this.currentMax < this.limitation && this.runningTasks.size <= this.currentMax)
|
|
41
|
+
this.currentMax++;
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
.catch((reason) => {
|
|
45
|
+
if (reason.statusCode === 429) {
|
|
46
|
+
// Once have any request be throttled, relegate the queue max size to 1, and push it into throttled collection.
|
|
47
|
+
this.currentMax = 1;
|
|
48
|
+
this.throttledTasks.push(this.runningTasks.get(runningPromise));
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
reject(reason);
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
.finally(() => {
|
|
55
|
+
// remove the promise from the running collection, no matter it's success or not
|
|
56
|
+
if (this.runningTasks.has(runningPromise)) {
|
|
57
|
+
this.runningTasks.delete(runningPromise);
|
|
58
|
+
}
|
|
59
|
+
if (callback)
|
|
60
|
+
callback(this.importProgress);
|
|
61
|
+
this.observable.next(++value); // trigger next round
|
|
62
|
+
});
|
|
63
|
+
this.runningTasks.set(runningPromise, task);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=adaptiveTaskManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adaptiveTaskManager.js","sourceRoot":"","sources":["../../../src/internal/adaptiveTaskManager.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport { BehaviorSubject } from \"rxjs\";\nimport { ImportProgress } from \"../models\";\nimport { RestError } from \"@azure/core-rest-pipeline\";\n\n/** @internal */\nexport class AdaptiveTaskManager<T> {\n private end = false;\n private currentMax = 1;\n private limitation = 50;\n private finishedCount = 0;\n private throttledTasks: IFunction<Promise<T>>[] = [];\n private runningTasks: Map<Promise<T>, IFunction<Promise<T>>> = new Map<\n Promise<T>,\n IFunction<Promise<T>>\n >();\n private taskWrapper: IFunction<IFunction<Promise<T>> | undefined>;\n private observable: BehaviorSubject<number>;\n private importProgress: ImportProgress;\n\n constructor(fn: IFunction<IFunction<Promise<T>> | undefined>, size: number) {\n this.taskWrapper = fn;\n this.observable = new BehaviorSubject(0);\n this.importProgress = {\n importCount: size,\n successCount: 0\n };\n }\n\n public async Start(callback?: (result: ImportProgress) => void) {\n return new Promise((resolve, reject) => {\n this.observable.subscribe((value: any) => {\n if (this.end && this.throttledTasks.length == 0) {\n if (this.runningTasks.size == 0) resolve(this.finishedCount);\n return;\n }\n while (this.runningTasks.size < this.currentMax) {\n const task: IFunction<Promise<T>> | undefined = this.throttledTasks.length > 0 ? this.throttledTasks.pop() : this.taskWrapper();\n\n if (!task) {\n this.end = true;\n this.observable.next(++value);\n return;\n }\n const runningPromise = task();\n runningPromise.then((value: any): void => {\n if (value) {\n this.importProgress.successCount = ++this.finishedCount;\n // If import can complete successfully and it didn't hit the limitation, increase currentMax.\n if (this.currentMax < this.limitation && this.runningTasks.size <= this.currentMax) this.currentMax++;\n }\n })\n .catch((reason) => {\n if ((reason as RestError).statusCode === 429) {\n // Once have any request be throttled, relegate the queue max size to 1, and push it into throttled collection.\n this.currentMax = 1;\n this.throttledTasks.push(this.runningTasks.get(runningPromise) as IFunction<Promise<T>>);\n }\n else {\n reject(reason);\n }\n })\n .finally((): void => {\n // remove the promise from the running collection, no matter it's success or not\n if (this.runningTasks.has(runningPromise)) {\n this.runningTasks.delete(runningPromise);\n }\n if (callback) callback(this.importProgress);\n this.observable.next(++value); // trigger next round\n });\n this.runningTasks.set(runningPromise, task);\n }\n });\n });\n }\n}\n\n/** @internal */\nexport interface IFunction<T> {\n (): T;\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
/** @internal*/
|
|
4
|
+
export const Constants = {
|
|
5
|
+
FeatureManagementKeyWords: [
|
|
6
|
+
"FeatureManagement",
|
|
7
|
+
"featureManagement",
|
|
8
|
+
"feature_management",
|
|
9
|
+
"feature-management"
|
|
10
|
+
],
|
|
11
|
+
EnabledForKeyWords: [
|
|
12
|
+
"EnabledFor",
|
|
13
|
+
"enabledFor",
|
|
14
|
+
"enabled_for",
|
|
15
|
+
"enabled-for"
|
|
16
|
+
],
|
|
17
|
+
Separators: [
|
|
18
|
+
".",
|
|
19
|
+
",",
|
|
20
|
+
";",
|
|
21
|
+
"-",
|
|
22
|
+
"_",
|
|
23
|
+
"__",
|
|
24
|
+
"/",
|
|
25
|
+
":"
|
|
26
|
+
],
|
|
27
|
+
CorrelationRequestIdHeader: "x-ms-correlation-request-id"
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/internal/constants.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,eAAe;AACf,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,yBAAyB,EAAE;QACzB,mBAAmB;QACnB,mBAAmB;QACnB,oBAAoB;QACpB,oBAAoB;KACrB;IACD,kBAAkB,EAAE;QAClB,YAAY;QACZ,YAAY;QACZ,aAAa;QACb,aAAa;KACd;IACD,UAAU,EAAE;QACV,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,IAAI;QACJ,GAAG;QACH,GAAG;KACJ;IACD,0BAA0B,EAAE,6BAA6B;CAC1D,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/** @internal*/\nexport const Constants = {\n FeatureManagementKeyWords: [\n \"FeatureManagement\",\n \"featureManagement\",\n \"feature_management\",\n \"feature-management\"\n ],\n EnabledForKeyWords: [\n \"EnabledFor\",\n \"enabledFor\",\n \"enabled_for\",\n \"enabled-for\"\n ],\n Separators: [\n \".\",\n \",\",\n \";\",\n \"-\",\n \"_\",\n \"__\",\n \"/\",\n \":\"\n ],\n CorrelationRequestIdHeader: \"x-ms-correlation-request-id\"\n};\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configurationSettingsConverter.js","sourceRoot":"","sources":["../../../../src/internal/parsers/configurationSettingsConverter.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport {\n SetConfigurationSettingParam,\n FeatureFlagValue,\n SecretReferenceValue\n} from \"@azure/app-configuration\";\nimport { SourceOptions } from \"../../importOptions\";\n\n/**\n * ConfigurationSettings converter for different configuration content.\n *\n * @internal\n * */\nexport interface ConfigurationSettingsConverter {\n Convert(\n rawConfig: object,\n options?: SourceOptions\n ): Array<SetConfigurationSettingParam<string | FeatureFlagValue | SecretReferenceValue>>;\n}\n"]}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
import { featureFlagPrefix, featureFlagContentType } from "@azure/app-configuration";
|
|
4
|
+
import { Constants } from "../constants";
|
|
5
|
+
import { ArgumentError } from "../../errors";
|
|
6
|
+
import * as flat from "flat";
|
|
7
|
+
import { ConfigurationFormat } from "../../enums";
|
|
8
|
+
import { isJsonContentType } from "../utils";
|
|
9
|
+
/**
|
|
10
|
+
* Format Parser for common key-value configuration.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
* */
|
|
14
|
+
export class DefaultConfigurationSettingsConverter {
|
|
15
|
+
/**
|
|
16
|
+
* Parse the raw configuration object to collection of ConfigurationSettings
|
|
17
|
+
*
|
|
18
|
+
* @param config Raw configuration object
|
|
19
|
+
* @param options Configuration options to dictate how the configuration be parsed.
|
|
20
|
+
* */
|
|
21
|
+
Convert(config, options) {
|
|
22
|
+
let configurationSettings = new Array();
|
|
23
|
+
let featureFlagsConfigSettings = new Array();
|
|
24
|
+
let foundFeatureManagement = false;
|
|
25
|
+
let featureFlags = {};
|
|
26
|
+
let featureManagementKeyWord = "";
|
|
27
|
+
let enabledForKeyWord = "";
|
|
28
|
+
if (options.format == ConfigurationFormat.Properties && !options.skipFeatureFlags) {
|
|
29
|
+
this.checkFeatureManagementExist(config);
|
|
30
|
+
}
|
|
31
|
+
for (let i = 0; i < Constants.FeatureManagementKeyWords.length; i++) {
|
|
32
|
+
if (Constants.FeatureManagementKeyWords[i] in config) {
|
|
33
|
+
if (foundFeatureManagement) {
|
|
34
|
+
throw new ArgumentError("Unable to proceed because data contains multiple sections corresponding to Feature Management.");
|
|
35
|
+
}
|
|
36
|
+
featureManagementKeyWord = Constants.FeatureManagementKeyWords[i];
|
|
37
|
+
enabledForKeyWord = Constants.EnabledForKeyWords[i];
|
|
38
|
+
const featureManagementKey = featureManagementKeyWord;
|
|
39
|
+
featureFlags = config[featureManagementKey];
|
|
40
|
+
delete config[featureManagementKey];
|
|
41
|
+
foundFeatureManagement = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (foundFeatureManagement && !options.skipFeatureFlags) {
|
|
45
|
+
const featureFlagConverter = new FeatureFlagConfigurationSettingsConverter(featureManagementKeyWord, enabledForKeyWord);
|
|
46
|
+
featureFlagsConfigSettings = featureFlagConverter.Convert(featureFlags, options);
|
|
47
|
+
}
|
|
48
|
+
const flattenedConfigurationSettings = flat.flatten(config, {
|
|
49
|
+
delimiter: options.separator,
|
|
50
|
+
maxDepth: options.depth,
|
|
51
|
+
safe: isJsonContentType(options.contentType) // preserve array for json content type
|
|
52
|
+
});
|
|
53
|
+
configurationSettings = this.toConfigurationSettings(flattenedConfigurationSettings, options);
|
|
54
|
+
configurationSettings = configurationSettings.concat(featureFlagsConfigSettings);
|
|
55
|
+
return configurationSettings;
|
|
56
|
+
}
|
|
57
|
+
checkFeatureManagementExist(configurations) {
|
|
58
|
+
const keys = Object.keys(configurations);
|
|
59
|
+
for (const featureFlagKeyWord of Constants.FeatureManagementKeyWords) {
|
|
60
|
+
if (keys.find(key => key.startsWith(featureFlagKeyWord))) {
|
|
61
|
+
throw new ArgumentError("Importing feature flag in Properties format is not supported. Anything in the Properties format being imported will be treated as key value.");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
toConfigurationSettings(flatted, options) {
|
|
66
|
+
const result = new Array();
|
|
67
|
+
const isJsonType = isJsonContentType(options.contentType);
|
|
68
|
+
for (const element in flatted) {
|
|
69
|
+
let generateKey = element;
|
|
70
|
+
if (options.prefix) {
|
|
71
|
+
generateKey = options.prefix + element;
|
|
72
|
+
}
|
|
73
|
+
if (generateKey === "." || generateKey === ".." || generateKey.indexOf("%") > -1) {
|
|
74
|
+
throw new ArgumentError("Key cannot be a '.' or '..', or contain the '%' character.");
|
|
75
|
+
}
|
|
76
|
+
if (generateKey.startsWith(featureFlagPrefix)) {
|
|
77
|
+
throw new ArgumentError("Key cannot start with the reserved prefix for feature flags.");
|
|
78
|
+
}
|
|
79
|
+
// Ignore key-value with empty array/object value when content-type is not json, instead of pushing a kv with an empty array/obj
|
|
80
|
+
// For null value it will be treated as "null" string.
|
|
81
|
+
if (!isJsonType && flatted[element] && typeof flatted[element] === "object" && Object.keys(flatted[element]).length == 0) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const setting = {
|
|
85
|
+
label: options.label,
|
|
86
|
+
tags: options.tags,
|
|
87
|
+
key: generateKey,
|
|
88
|
+
contentType: options.contentType,
|
|
89
|
+
value: typeof flatted[element] === "object" || isJsonType
|
|
90
|
+
? JSON.stringify(flatted[element])
|
|
91
|
+
: flatted[element].toString()
|
|
92
|
+
};
|
|
93
|
+
result.push(setting);
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* ConfigurationSettings converter for feature flag.
|
|
100
|
+
*
|
|
101
|
+
* @internal
|
|
102
|
+
* */
|
|
103
|
+
class FeatureFlagConfigurationSettingsConverter {
|
|
104
|
+
constructor(featureManagementKeyWord, enabledForKeyWord) {
|
|
105
|
+
this.featureManagementKeyWord = featureManagementKeyWord;
|
|
106
|
+
this.enabledForKeyWord = enabledForKeyWord;
|
|
107
|
+
if (!this.featureManagementKeyWord || !this.enabledForKeyWord) {
|
|
108
|
+
throw new ArgumentError("No feature management was found");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* @inheritdoc
|
|
113
|
+
* */
|
|
114
|
+
Convert(config, options) {
|
|
115
|
+
var _a;
|
|
116
|
+
const settings = new Array();
|
|
117
|
+
for (const featureFlag in config) {
|
|
118
|
+
if (!this.validateFeatureName(featureFlag)) {
|
|
119
|
+
throw new ArgumentError(`Feature flag ${featureFlag} contains invalid character,'%' and ':' are not allowed in feature name. Please provide valid feature name.`);
|
|
120
|
+
}
|
|
121
|
+
const prefix = (_a = options.prefix) !== null && _a !== void 0 ? _a : "";
|
|
122
|
+
settings.push({
|
|
123
|
+
key: featureFlagPrefix + prefix + featureFlag,
|
|
124
|
+
label: options.label,
|
|
125
|
+
value: this.getFeatureFlagValue(featureFlag, config, this.enabledForKeyWord),
|
|
126
|
+
contentType: featureFlagContentType,
|
|
127
|
+
tags: options.tags
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return settings;
|
|
131
|
+
}
|
|
132
|
+
validateFeatureName(featureName) {
|
|
133
|
+
if (featureName) {
|
|
134
|
+
const valid = /^[^:%]*$/;
|
|
135
|
+
return valid.test(featureName);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
getFeatureFlagValue(featureFlagName, featureData, enabledForKeyWord) {
|
|
142
|
+
const defaultFeatureConditions = { clientFilters: [] };
|
|
143
|
+
const featureFlagValue = {
|
|
144
|
+
id: featureFlagName,
|
|
145
|
+
description: "",
|
|
146
|
+
enabled: false,
|
|
147
|
+
conditions: {
|
|
148
|
+
clientFilters: []
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
if (typeof featureData[featureFlagName] == "object") {
|
|
152
|
+
const filters = featureData[featureFlagName][enabledForKeyWord];
|
|
153
|
+
if (!filters) {
|
|
154
|
+
throw new ArgumentError(`Data contains feature flags in invalid format. Feature flag '${featureFlagName}' must contain '${enabledForKeyWord}' definition or have a true/false value.`);
|
|
155
|
+
}
|
|
156
|
+
if (filters.length != 0) {
|
|
157
|
+
featureFlagValue.enabled = true;
|
|
158
|
+
featureFlagValue.conditions.clientFilters = filters;
|
|
159
|
+
for (let i = 0; i < featureFlagValue.conditions.clientFilters.length; i++) {
|
|
160
|
+
const parameters = {};
|
|
161
|
+
//
|
|
162
|
+
// Converting client_filter keys to lower case
|
|
163
|
+
const lowerCaseFilters = this.getLowerCaseFilters(featureFlagValue.conditions.clientFilters[i]);
|
|
164
|
+
const filtersName = lowerCaseFilters["name"];
|
|
165
|
+
const filtersParameters = lowerCaseFilters["parameters"];
|
|
166
|
+
if (filtersName) {
|
|
167
|
+
if (filtersName.toLowerCase() == "alwayson") {
|
|
168
|
+
featureFlagValue.conditions = defaultFeatureConditions;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
parameters.name = filtersName;
|
|
172
|
+
if (filtersParameters) {
|
|
173
|
+
parameters.parameters = filtersParameters;
|
|
174
|
+
}
|
|
175
|
+
featureFlagValue.conditions.clientFilters[i] = parameters;
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
throw new ArgumentError(`This feature flag '${featureFlagName}' has a filter without the required 'name' property.`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else if (typeof featureData[featureFlagName] == "boolean") {
|
|
184
|
+
featureFlagValue.enabled = featureData[featureFlagName];
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
throw new ArgumentError(`Data contains feature flags in invalid format. The type of ${featureFlagName} should be either boolean or dictionary.`);
|
|
188
|
+
}
|
|
189
|
+
return featureFlagValue;
|
|
190
|
+
}
|
|
191
|
+
getLowerCaseFilters(clientFilter) {
|
|
192
|
+
const keys = Object.keys(clientFilter);
|
|
193
|
+
const lowerCaseFilters = {};
|
|
194
|
+
for (let i = 0; i < keys.length; i++) {
|
|
195
|
+
lowerCaseFilters[keys[i].toLowerCase()] = clientFilter[keys[i]];
|
|
196
|
+
}
|
|
197
|
+
return lowerCaseFilters;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=defaultConfigurationSettingsConverter.js.map
|