From 8e0da71bb74e9b2e22a603f7b21f4f1bb94df670 Mon Sep 17 00:00:00 2001 From: Richard Park Date: Fri, 12 Mar 2021 18:25:21 -0800 Subject: [PATCH 01/50] Getting there - need to check all the flows but, for the most part, the only signature that actually needs to change is setConfigurationSetting, and even then just to express that there are additional types that you can send in. --- .../review/app-configuration.api.md | 39 ++++- .../src/appConfigurationClient.ts | 56 +++---- .../app-configuration/src/featureFlag.ts | 147 ++++++++++++++++++ .../app-configuration/src/index.ts | 8 + .../app-configuration/src/internal/helpers.ts | 48 +++++- .../src/internal/jsonModels.ts | 92 +++++++++++ .../src/keyvaultReference.ts | 18 +++ .../app-configuration/src/models.ts | 5 +- .../test/internal/helpers.spec.ts | 2 +- 9 files changed, 380 insertions(+), 35 deletions(-) create mode 100644 sdk/appconfiguration/app-configuration/src/featureFlag.ts create mode 100644 sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts create mode 100644 sdk/appconfiguration/app-configuration/src/keyvaultReference.ts diff --git a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md index d4e7d36e40e6..ca26610ea7ac 100644 --- a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md +++ b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md @@ -26,7 +26,11 @@ export interface AddConfigurationSettingResponse extends ConfigurationSetting, S export class AppConfigurationClient { constructor(connectionString: string, options?: AppConfigurationClientOptions); constructor(endpoint: string, tokenCredential: TokenCredential, options?: AppConfigurationClientOptions); - addConfigurationSetting(configurationSetting: AddConfigurationSettingParam, options?: AddConfigurationSettingOptions): Promise; + addConfigurationSetting(configurationSetting: AddConfigurationSettingParam & ({ + keyVault: KeyVaultReference; + } | { + featureFlag: FeatureFlag; + } | {}), options?: AddConfigurationSettingOptions): Promise; deleteConfigurationSetting(id: ConfigurationSettingId, options?: DeleteConfigurationSettingOptions): Promise; getConfigurationSetting(id: ConfigurationSettingId, options?: GetConfigurationSettingOptions): Promise; listConfigurationSettings(options?: ListConfigurationSettingsOptions): PagedAsyncIterableIterator; @@ -73,6 +77,21 @@ export interface DeleteConfigurationSettingOptions extends HttpOnlyIfUnchangedFi export interface DeleteConfigurationSettingResponse extends SyncTokenHeaderField, HttpResponseFields, HttpResponseField { } +// @public +export interface FeatureFlag { + conditions: any[]; + description: string; + displayName: string; + enabled: boolean; + id: string; +} + +// @public (undocumented) +export const FeatureFlagContentType = "application/vnd.microsoft.appconfig.ff+json;charset=utf-8"; + +// @public (undocumented) +export const FeatureFlagPrefix = ".appconfig.featureflag/"; + // @public export interface GetConfigurationHeaders extends SyncTokenHeaderField { } @@ -109,6 +128,24 @@ export interface HttpResponseFields { statusCode: number; } +// @public (undocumented) +export function isFeatureFlag(value: ConfigurationSetting): value is ConfigurationSetting & { + featureFlag: FeatureFlag; +}; + +// @public (undocumented) +export function isKeyVaultReference(value: ConfigurationSetting): value is ConfigurationSetting & { + keyVault: KeyVaultReference; +}; + +// @public +export interface KeyVaultReference { + uri: string; +} + +// @public (undocumented) +export const KeyVaultReferenceContentType = "application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8"; + // @public export interface ListConfigurationSettingPage extends HttpResponseField { items: ConfigurationSetting[]; diff --git a/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts b/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts index 398421e03289..654f29da1396 100644 --- a/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts +++ b/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts @@ -15,7 +15,7 @@ import { ServiceClientCredentials, UserAgentOptions, getDefaultUserAgentValue as getCoreHttpDefaultUserAgentValue, - userAgentPolicy + userAgentPolicy, } from "@azure/core-http"; import { throttlingRetryPolicy } from "./policies/throttlingRetryPolicy"; import { TokenCredential } from "@azure/identity"; @@ -28,10 +28,13 @@ import { AddConfigurationSettingResponse, ConfigurationSetting, ConfigurationSettingId, + ConfigurationSettingParam, DeleteConfigurationSettingOptions, DeleteConfigurationSettingResponse, + FeatureFlag, GetConfigurationSettingOptions, GetConfigurationSettingResponse, + KeyVaultReference, ListConfigurationSettingPage, ListConfigurationSettingsOptions, ListRevisionsOptions, @@ -40,7 +43,7 @@ import { SetConfigurationSettingParam, SetConfigurationSettingResponse, SetReadOnlyOptions, - SetReadOnlyResponse + SetReadOnlyResponse, } from "./models"; import { checkAndFormatIfAndIfNoneMatch, @@ -51,15 +54,16 @@ import { transformKeyValueResponseWithStatusCode, transformKeyValue, formatAcceptDateTime, - formatFieldsForSelect + formatFieldsForSelect, } from "./internal/helpers"; import { tracingPolicy } from "@azure/core-http"; import { Spanner } from "./internal/tracingHelpers"; import { AppConfigurationGetKeyValuesResponse, - AppConfigurationOptionalParams as GeneratedAppConfigurationClientOptions + AppConfigurationOptionalParams as GeneratedAppConfigurationClientOptions, } from "./generated/src/models"; import { syncTokenPolicy, SyncTokens } from "./internal/synctokenpolicy"; +import { FeatureFlagParam } from "./featureFlag"; const packageName = "azsdk-js-app-configuration"; @@ -77,8 +81,8 @@ const deserializationContentTypes = { "application/vnd.microsoft.appconfig.kv+json", "application/vnd.microsoft.appconfig.kvs+json", "application/vnd.microsoft.appconfig.keyset+json", - "application/vnd.microsoft.appconfig.revs+json" - ] + "application/vnd.microsoft.appconfig.revs+json", + ], }; /** @@ -197,7 +201,7 @@ export class AppConfigurationClient { ifNoneMatch: "*", label: configurationSetting.label, entity: configurationSetting, - ...newOptions + ...newOptions, }); return transformKeyValueResponse(originalResponse); @@ -223,7 +227,7 @@ export class AppConfigurationClient { const originalResponse = await this.client.deleteKeyValue(id.key, { label: id.label, ...newOptions, - ...checkAndFormatIfAndIfNoneMatch(id, options) + ...checkAndFormatIfAndIfNoneMatch(id, options), }); return transformKeyValueResponseWithStatusCode(originalResponse); @@ -254,7 +258,7 @@ export class AppConfigurationClient { label: id.label, select: formatFieldsForSelect(options.fields), ...formatAcceptDateTime(options), - ...checkAndFormatIfAndIfNoneMatch(id, options) + ...checkAndFormatIfAndIfNoneMatch(id, options), }); const response: GetConfigurationSettingResponse = transformKeyValueResponseWithStatusCode( @@ -303,7 +307,7 @@ export class AppConfigurationClient { // The appconfig service doesn't currently support letting you select a page size // so we're ignoring their setting for now. return this.listConfigurationSettingsByPage(options); - } + }, }; } @@ -328,7 +332,7 @@ export class AppConfigurationClient { const response = await this.client.getKeyValues({ ...newOptions, ...formatAcceptDateTime(options), - ...formatFiltersAndSelect(options) + ...formatFiltersAndSelect(options), }); return response; @@ -347,7 +351,7 @@ export class AppConfigurationClient { ...newOptions, ...formatAcceptDateTime(options), ...formatFiltersAndSelect(options), - after: extractAfterTokenFromNextLink(currentResponse.nextLink!) + after: extractAfterTokenFromNextLink(currentResponse.nextLink!), }); return response; @@ -367,7 +371,7 @@ export class AppConfigurationClient { ) { yield { ...currentResponse, - items: currentResponse.items != null ? currentResponse.items.map(transformKeyValue) : [] + items: currentResponse.items != null ? currentResponse.items.map(transformKeyValue) : [], }; } @@ -397,7 +401,7 @@ export class AppConfigurationClient { // The appconfig service doesn't currently support letting you select a page size // so we're ignoring their setting for now. return this.listRevisionsByPage(options); - } + }, }; } @@ -422,7 +426,7 @@ export class AppConfigurationClient { const response = await this.client.getRevisions({ ...newOptions, ...formatAcceptDateTime(options), - ...formatFiltersAndSelect(newOptions) + ...formatFiltersAndSelect(newOptions), }); return response; @@ -431,7 +435,7 @@ export class AppConfigurationClient { yield { ...currentResponse, - items: currentResponse.items != null ? currentResponse.items.map(transformKeyValue) : [] + items: currentResponse.items != null ? currentResponse.items.map(transformKeyValue) : [], }; while (currentResponse.nextLink) { @@ -440,7 +444,7 @@ export class AppConfigurationClient { ...newOptions, ...formatAcceptDateTime(options), ...formatFiltersAndSelect(options), - after: extractAfterTokenFromNextLink(currentResponse.nextLink!) + after: extractAfterTokenFromNextLink(currentResponse.nextLink!), }); }); @@ -450,7 +454,7 @@ export class AppConfigurationClient { yield { ...currentResponse, - items: currentResponse.items != null ? currentResponse.items.map(transformKeyValue) : [] + items: currentResponse.items != null ? currentResponse.items.map(transformKeyValue) : [], }; } } @@ -467,7 +471,7 @@ export class AppConfigurationClient { * ``` */ async setConfigurationSetting( - configurationSetting: SetConfigurationSettingParam, + configurationSetting: SetConfigurationSettingParam | KeyVaultReferenceParam | FeatureFlagParam, options: SetConfigurationSettingOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); @@ -480,7 +484,7 @@ export class AppConfigurationClient { ...newOptions, label: configurationSetting.label, entity: configurationSetting, - ...checkAndFormatIfAndIfNoneMatch(configurationSetting, options) + ...checkAndFormatIfAndIfNoneMatch(configurationSetting, options), }); return transformKeyValueResponse(response); @@ -504,7 +508,7 @@ export class AppConfigurationClient { const response = await this.client.putLock(id.key, { ...newOptions, label: id.label, - ...checkAndFormatIfAndIfNoneMatch(id, options) + ...checkAndFormatIfAndIfNoneMatch(id, options), }); return transformKeyValueResponse(response); @@ -512,7 +516,7 @@ export class AppConfigurationClient { const response = await this.client.deleteLock(id.key, { ...newOptions, label: id.label, - ...checkAndFormatIfAndIfNoneMatch(id, options) + ...checkAndFormatIfAndIfNoneMatch(id, options), }); return transformKeyValueResponse(response); @@ -533,12 +537,12 @@ export function getGeneratedClientOptions( const retryPolicies = [ exponentialRetryPolicy(), systemErrorRetryPolicy(), - throttlingRetryPolicy() + throttlingRetryPolicy(), ]; const userAgent = getUserAgentPrefix( internalAppConfigOptions.userAgentOptions && - internalAppConfigOptions.userAgentOptions.userAgentPrefix + internalAppConfigOptions.userAgentOptions.userAgentPrefix ); return { @@ -551,9 +555,9 @@ export function getGeneratedClientOptions( syncTokenPolicy(syncTokens), userAgentPolicy({ value: userAgent }), ...retryPolicies, - ...defaults + ...defaults, ], - generateClientRequestIdHeader: true + generateClientRequestIdHeader: true, }; } diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts new file mode 100644 index 000000000000..ee80027823c5 --- /dev/null +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -0,0 +1,147 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { isJsonFeatureFlagPercentageClientFilter, isJsonFeatureFlagTargetingClientFilter, isJsonFeatureFlagTimeWindowClientFilter, JsonFeatureFlag } from "./internal/jsonModels"; +import { ConfigurationSetting, ConfigurationSettingParam } from "./models"; + +/** + * The prefix for feature flags. + */ +export const featureFlagPrefix = ".appconfig.featureflag/"; + +/** + * The content type for a FeatureFlag + */ +export const featureFlagContentType = "application/vnd.microsoft.appconfig.ff+json;charset=utf-8"; + +export interface FeatureFlagParam extends ConfigurationSettingParam { + // TODO: can we hoist 'clientFilters' higher and avoid this sub-field? Need to talk to team. + conditions: { + clientFilters: ( + | FeatureFlagTargetingClientFilter + | FeatureFlagTimeWindowClientFilter + | FeatureFlagPercentageClientFilter + | object + )[]; + }; + description?: string; + displayName: string; + enabled: boolean; +}; + +export interface FeatureFlag extends FeatureFlagParam, ConfigurationSetting {} + +export interface FeatureFlagTargetingClientFilter { + name: "Microsoft.Targeting"; + parameters: { // TODO: can we hoist these values up directly into the filter, rather than having this 'parameters' intermediary. + audience: { + users: string[]; + groups: { + name: string; + rolloutPercentage: number; + }[]; + }; + defaultRolloutPercentage: number; + + // here to avoid potential slicing of underlying attributes as objects expand + // in the future. + [key: string]: any; + }; +} + +export interface FeatureFlagTimeWindowClientFilter { + name: "Microsoft.TimeWindow"; + parameters: { + start: string; + end: string; + [key: string]: any; + }; +} + +export interface FeatureFlagPercentageClientFilter { + name: "Microsoft.Percentage"; + parameters: { + [key: string]: any; + }; +} + +export function isFeatureFlag( + setting: ConfigurationSetting | FeatureFlag +): setting is FeatureFlag { + return setting.contentType === featureFlagContentType; +} + +export function deserializeFeatureFlag(setting: ConfigurationSetting): FeatureFlag | undefined { + if (!isFeatureFlag(setting) || !setting.value) { + return undefined; + } + + let jsonFeatureFlag: JsonFeatureFlag + + try { + jsonFeatureFlag = JSON.parse(setting.value) as JsonFeatureFlag; + } catch (err) { + // best effort - if it doesn't deserialize properly we'll just let it "degrade" + // to be treated as a ConfigurationSetting. + return undefined; + } + + // update (in-place) the feature flag specific properties + const featureFlag = setting as Omit; + featureFlag.conditions = convertJsonConditions(jsonFeatureFlag.conditions); + featureFlag.enabled = jsonFeatureFlag.enabled; + featureFlag.description = jsonFeatureFlag.description; + + return setting; +} + +export function serializeFeatureFlag(_setting: FeatureFlag): ConfigurationSetting { + throw new Error("Not implemented") +} + +/** + * @internal + */ +export function convertJsonConditions(conditions: JsonFeatureFlag['conditions']): FeatureFlag['conditions'] { + const clientFilters = conditions.client_filters.map((jsonFilter) => { + if (isJsonFeatureFlagTargetingClientFilter(jsonFilter)) { + // try not to slice any unknown attributes + const filter: FeatureFlagTargetingClientFilter = { + name: jsonFilter.name, + parameters: { + audience: { + groups: jsonFilter.parameters.Audience?.Groups.map((grp) => ({ + name: grp.Name, + rolloutPercentage: grp.RolloutPercentage + })) || [], + users: jsonFilter.parameters.Audience?.Users || [], + }, + defaultRolloutPercentage: jsonFilter.parameters.DefaultRolloutPercentage + } + }; + + return filter; + } else if (isJsonFeatureFlagTimeWindowClientFilter(jsonFilter)) { + // try not to slice any unknown attributes + const filter: FeatureFlagTimeWindowClientFilter = { + name: jsonFilter.name, + parameters: { + start: jsonFilter.parameters.Start, + end: jsonFilter.parameters.End + } + }; + + return filter; + } else if (isJsonFeatureFlagPercentageClientFilter(jsonFilter)) { + // TODO: this is only working because I left the filter so unspecified in the type. Need to do + // a little more research into what that filter's actual type is. + return jsonFilter; + } else { + return jsonFilter as object; + } + }); + + return { + clientFilters + }; +} \ No newline at end of file diff --git a/sdk/appconfiguration/app-configuration/src/index.ts b/sdk/appconfiguration/app-configuration/src/index.ts index f865b791cd6c..b9b223eee2c0 100644 --- a/sdk/appconfiguration/app-configuration/src/index.ts +++ b/sdk/appconfiguration/app-configuration/src/index.ts @@ -3,3 +3,11 @@ export * from "./models"; export { AppConfigurationClientOptions, AppConfigurationClient } from "./appConfigurationClient"; + +export { + KeyVaultReference, KeyVaultReferenceParam, isKeyVaultReference, + keyVaultReferenceContentType } from "./keyvaultReference"; + +export { + FeatureFlag, FeatureFlagParam, FeatureFlagPercentageClientFilter, FeatureFlagTargetingClientFilter, FeatureFlagTimeWindowClientFilter, featureFlagContentType, featureFlagPrefix, isFeatureFlag +} from "./featureFlag"; \ No newline at end of file diff --git a/sdk/appconfiguration/app-configuration/src/internal/helpers.ts b/sdk/appconfiguration/app-configuration/src/internal/helpers.ts index 4c7132da1178..d39894028ed2 100644 --- a/sdk/appconfiguration/app-configuration/src/internal/helpers.ts +++ b/sdk/appconfiguration/app-configuration/src/internal/helpers.ts @@ -10,9 +10,14 @@ import { HttpResponseField, HttpResponseFields, HttpOnlyIfChangedField, - HttpOnlyIfUnchangedField + HttpOnlyIfUnchangedField, + isKeyVaultReference, + KeyVaultReferenceContentType, + KeyVaultReference, } from "../models"; import { AppConfigurationGetKeyValuesOptionalParams, KeyValue } from "../generated/src/models"; +import { deserializeFeatureFlag, featureFlagContentType } from "../featureFlag"; +import { JsonKeyVaultReference } from "./jsonModels"; /** * Formats the etag so it can be used with a If-Match/If-None-Match header @@ -151,10 +156,43 @@ export function makeConfigurationSettingEmpty( * @internal */ export function transformKeyValue(kvp: KeyValue): ConfigurationSetting { - const obj: ConfigurationSetting & KeyValue = { - ...kvp, - isReadOnly: !!kvp.locked - }; + const setting = transformKeyValue(kvp); + + switch (setting.contentType) { + case featureFlagContentType: { + return deserializeFeatureFlag(setting) ?? setting; + } + case KeyVaultReferenceContentType: { + if (!setting.value) { + return setting; + } + + const jsonKeyVaultRef = JSON.parse(setting.value) as JsonKeyVaultReference; + const keyVaultRef: KeyVaultReference = { + + } + } + } + + + } else if (isKeyVaultReference(setting)) { + const keyValueReference = ; + } + + if (obj.value != null && (isKeyVaultReference(obj) || isFeatureFlag(obj))) { + let parsedValue; + try { + + } catch (_err) { + parsedValue = undefined; + } + + if (isKeyVaultReference(obj)) { + obj.keyVault = parsedValue; + } else { + obj.featureFlag = parsedValue; + } + } delete obj.locked; return obj; diff --git a/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts new file mode 100644 index 000000000000..bbfdee2be125 --- /dev/null +++ b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * These interfaces map to the actual JSON representation that's needed when serializing to/from the string .value of a ConfigurationSetting. + */ + +/** + * @internal + */ +export type JsonFeatureFlag = { + conditions: { + client_filters: ( + | JsonFeatureFlagTargetingClientFilter + | JsonFeatureFlagTimeWindowClientFilter + | JsonFeatureFlagPercentageClientFilter + | object + )[]; + }; + description?: string; + enabled: true; +}; + +/** + * @internal + */ +export interface JsonFeatureFlagTargetingClientFilter { + name: "Microsoft.Targeting"; + parameters: { + Audience?: { + Users: string[]; + Groups: { + Name: string; + RolloutPercentage: number; + }[]; + }; + DefaultRolloutPercentage: number; + [key: string]: any; + }; +} + +/** + * @internal + */ +export interface JsonFeatureFlagTimeWindowClientFilter { + name: "Microsoft.TimeWindow"; + parameters: { + Start: string; + End: string; + [key: string]: any; + }; +} + +/** + * @internal + */ +export interface JsonFeatureFlagPercentageClientFilter { + name: "Microsoft.Percentage"; + parameters: { + [key: string]: any; + }; +} + +/** + * @internal + */ +export function isJsonFeatureFlagTargetingClientFilter(clientFilter: any): clientFilter is JsonFeatureFlagTargetingClientFilter { + return clientFilter.name === "Microsoft.Targeting"; +} + +/** + * @internal + */ +export function isJsonFeatureFlagTimeWindowClientFilter(clientFilter: any): clientFilter is JsonFeatureFlagTimeWindowClientFilter { + return clientFilter.name === "Microsoft.TimeWindow"; +} + + /** + * @internal + */ +export function isJsonFeatureFlagPercentageClientFilter(clientFilter: any): clientFilter is JsonFeatureFlagPercentageClientFilter { + return clientFilter.name === "Microsoft.Percentage"; +} + +// keyvault reference + +/** + * @internal + */ +export interface JsonKeyVaultReference { + uri: string; +} diff --git a/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts new file mode 100644 index 000000000000..92e9bbfebbec --- /dev/null +++ b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { ConfigurationSetting, ConfigurationSettingParam } from "./models"; + +export const keyVaultReferenceContentType = + "application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8"; + +export interface KeyVaultReferenceParam extends ConfigurationSettingParam { + keyVaultSecretUri: string; +} + +export interface KeyVaultReference extends KeyVaultReferenceParam, ConfigurationSetting { +} + +export function isKeyVaultReference(setting: ConfigurationSetting): setting is KeyVaultReference { + return setting.contentType === keyVaultReferenceContentType; +} \ No newline at end of file diff --git a/sdk/appconfiguration/app-configuration/src/models.ts b/sdk/appconfiguration/app-configuration/src/models.ts index 3649b02a33be..9fb297269c66 100644 --- a/sdk/appconfiguration/app-configuration/src/models.ts +++ b/sdk/appconfiguration/app-configuration/src/models.ts @@ -8,7 +8,8 @@ import { OperationOptions, HttpResponse } from "@azure/core-http"; */ export interface ConfigurationSettingId { /** - * The key for this setting + * The key for this setting. + * Feature flags must be prefixed with `.appconfig.featureflag/`. */ key: string; @@ -42,7 +43,7 @@ export interface ConfigurationSettingParam extends ConfigurationSettingId { * Tags for this key */ tags?: { [propertyName: string]: string }; -} +}; /** * Configuration setting with extra metadata from the server, indicating diff --git a/sdk/appconfiguration/app-configuration/test/internal/helpers.spec.ts b/sdk/appconfiguration/app-configuration/test/internal/helpers.spec.ts index 3da8cdadcc73..c39a855e6199 100644 --- a/sdk/appconfiguration/app-configuration/test/internal/helpers.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/internal/helpers.spec.ts @@ -259,7 +259,7 @@ describe("helper methods", () => { lastModified: new Date(), isReadOnly: true, tags: {}, - value: "" + value: "", }; return Object.keys(configObjectWithAllFieldsRequired).sort() as (keyof ConfigurationSetting)[]; From 99924261f62f5ba8fc5f2dc262116b152c106d21 Mon Sep 17 00:00:00 2001 From: Richard Park Date: Mon, 15 Mar 2021 11:46:12 -0700 Subject: [PATCH 02/50] Surface changes to support: - Users adding in their own sync tokens (received from EventGrid) - Deserializing and working with FeatureFlags or KeyVaultReferences --- .../review/app-configuration.api.md | 107 ++++++++++++++---- .../src/appConfigurationClient.ts | 15 +-- .../app-configuration/src/featureFlag.ts | 21 ++++ .../app-configuration/src/index.ts | 3 +- .../app-configuration/src/internal/helpers.ts | 48 ++------ .../src/keyvaultReference.ts | 22 ++++ .../app-configuration/src/models.ts | 2 +- .../test/samplesForApiReview.ts | 106 +++++++++++++++++ 8 files changed, 255 insertions(+), 69 deletions(-) create mode 100644 sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts diff --git a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md index ca26610ea7ac..a037d6f5bd68 100644 --- a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md +++ b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md @@ -26,18 +26,16 @@ export interface AddConfigurationSettingResponse extends ConfigurationSetting, S export class AppConfigurationClient { constructor(connectionString: string, options?: AppConfigurationClientOptions); constructor(endpoint: string, tokenCredential: TokenCredential, options?: AppConfigurationClientOptions); - addConfigurationSetting(configurationSetting: AddConfigurationSettingParam & ({ - keyVault: KeyVaultReference; - } | { - featureFlag: FeatureFlag; - } | {}), options?: AddConfigurationSettingOptions): Promise; + addConfigurationSetting(configurationSetting: AddConfigurationSettingParam, options?: AddConfigurationSettingOptions): Promise; deleteConfigurationSetting(id: ConfigurationSettingId, options?: DeleteConfigurationSettingOptions): Promise; getConfigurationSetting(id: ConfigurationSettingId, options?: GetConfigurationSettingOptions): Promise; listConfigurationSettings(options?: ListConfigurationSettingsOptions): PagedAsyncIterableIterator; listRevisions(options?: ListRevisionsOptions): PagedAsyncIterableIterator; setConfigurationSetting(configurationSetting: SetConfigurationSettingParam, options?: SetConfigurationSettingOptions): Promise; setReadOnly(id: ConfigurationSettingId, readOnly: boolean, options?: SetReadOnlyOptions): Promise; - } + // (undocumented) + updateSyncToken(syncToken: string): void; +} // @public export interface AppConfigurationClientOptions { @@ -77,20 +75,69 @@ export interface DeleteConfigurationSettingOptions extends HttpOnlyIfUnchangedFi export interface DeleteConfigurationSettingResponse extends SyncTokenHeaderField, HttpResponseFields, HttpResponseField { } +// @public (undocumented) +export interface FeatureFlag extends FeatureFlagParam, ConfigurationSetting { +} + // @public -export interface FeatureFlag { - conditions: any[]; - description: string; +export const featureFlagContentType = "application/vnd.microsoft.appconfig.ff+json;charset=utf-8"; + +// @public (undocumented) +export interface FeatureFlagParam extends ConfigurationSettingParam { + // (undocumented) + conditions: { + clientFilters: (FeatureFlagTargetingClientFilter | FeatureFlagTimeWindowClientFilter | FeatureFlagPercentageClientFilter | object)[]; + }; + // (undocumented) + description?: string; + // (undocumented) displayName: string; + // (undocumented) enabled: boolean; - id: string; } // @public (undocumented) -export const FeatureFlagContentType = "application/vnd.microsoft.appconfig.ff+json;charset=utf-8"; +export interface FeatureFlagPercentageClientFilter { + // (undocumented) + name: "Microsoft.Percentage"; + // (undocumented) + parameters: { + [key: string]: any; + }; +} + +// @public +export const featureFlagPrefix = ".appconfig.featureflag/"; // @public (undocumented) -export const FeatureFlagPrefix = ".appconfig.featureflag/"; +export interface FeatureFlagTargetingClientFilter { + // (undocumented) + name: "Microsoft.Targeting"; + // (undocumented) + parameters: { + audience: { + users: string[]; + groups: { + name: string; + rolloutPercentage: number; + }[]; + }; + defaultRolloutPercentage: number; + [key: string]: any; + }; +} + +// @public (undocumented) +export interface FeatureFlagTimeWindowClientFilter { + // (undocumented) + name: "Microsoft.TimeWindow"; + // (undocumented) + parameters: { + start: string; + end: string; + [key: string]: any; + }; +} // @public export interface GetConfigurationHeaders extends SyncTokenHeaderField { @@ -129,22 +176,38 @@ export interface HttpResponseFields { } // @public (undocumented) -export function isFeatureFlag(value: ConfigurationSetting): value is ConfigurationSetting & { - featureFlag: FeatureFlag; -}; +export function isFeatureFlag(setting: ConfigurationSetting | FeatureFlag): setting is FeatureFlag; + +// Warning: (ae-internal-missing-underscore) The name "isFeatureFlagPercentageClientFilter" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export function isFeatureFlagPercentageClientFilter(clientFilter: any): clientFilter is FeatureFlagPercentageClientFilter; + +// Warning: (ae-internal-missing-underscore) The name "isFeatureFlagTargetingClientFilter" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export function isFeatureFlagTargetingClientFilter(clientFilter: any): clientFilter is FeatureFlagTargetingClientFilter; + +// Warning: (ae-internal-missing-underscore) The name "isFeatureFlagTimeWindowClientFilter" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export function isFeatureFlagTimeWindowClientFilter(clientFilter: any): clientFilter is FeatureFlagTimeWindowClientFilter; // @public (undocumented) -export function isKeyVaultReference(value: ConfigurationSetting): value is ConfigurationSetting & { - keyVault: KeyVaultReference; -}; +export function isKeyVaultReference(setting: ConfigurationSetting): setting is KeyVaultReference; -// @public -export interface KeyVaultReference { - uri: string; +// @public (undocumented) +export interface KeyVaultReference extends KeyVaultReferenceParam, ConfigurationSetting { } // @public (undocumented) -export const KeyVaultReferenceContentType = "application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8"; +export const keyVaultReferenceContentType = "application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8"; + +// @public (undocumented) +export interface KeyVaultReferenceParam extends ConfigurationSettingParam { + // (undocumented) + keyVaultSecretUri: string; +} // @public export interface ListConfigurationSettingPage extends HttpResponseField { diff --git a/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts b/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts index 654f29da1396..0beeac29b78b 100644 --- a/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts +++ b/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts @@ -28,13 +28,10 @@ import { AddConfigurationSettingResponse, ConfigurationSetting, ConfigurationSettingId, - ConfigurationSettingParam, DeleteConfigurationSettingOptions, DeleteConfigurationSettingResponse, - FeatureFlag, GetConfigurationSettingOptions, GetConfigurationSettingResponse, - KeyVaultReference, ListConfigurationSettingPage, ListConfigurationSettingsOptions, ListRevisionsOptions, @@ -63,7 +60,6 @@ import { AppConfigurationOptionalParams as GeneratedAppConfigurationClientOptions, } from "./generated/src/models"; import { syncTokenPolicy, SyncTokens } from "./internal/synctokenpolicy"; -import { FeatureFlagParam } from "./featureFlag"; const packageName = "azsdk-js-app-configuration"; @@ -121,6 +117,7 @@ export interface InternalAppConfigurationClientOptions extends AppConfigurationC export class AppConfigurationClient { private client: AppConfiguration; private spanner: Spanner; + private _syncTokens: SyncTokens; /** * Initializes a new instance of the AppConfigurationClient class. @@ -168,13 +165,13 @@ export class AppConfigurationClient { } } - const syncTokens = appConfigOptions.syncTokens || new SyncTokens(); + this._syncTokens = appConfigOptions.syncTokens || new SyncTokens(); this.client = new AppConfiguration( appConfigCredential, appConfigEndpoint, apiVersion, - getGeneratedClientOptions(appConfigEndpoint, syncTokens, appConfigOptions) + getGeneratedClientOptions(appConfigEndpoint, this._syncTokens, appConfigOptions) ); this.spanner = new Spanner("Azure.Data.AppConfiguration"); @@ -471,7 +468,7 @@ export class AppConfigurationClient { * ``` */ async setConfigurationSetting( - configurationSetting: SetConfigurationSettingParam | KeyVaultReferenceParam | FeatureFlagParam, + configurationSetting: SetConfigurationSettingParam, options: SetConfigurationSettingOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); @@ -523,6 +520,10 @@ export class AppConfigurationClient { } }); } + + updateSyncToken(syncToken: string): void { + this._syncTokens.addSyncTokenFromHeaderValue(syncToken); + } } /** diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index ee80027823c5..67bdc775d7ba 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -71,6 +71,27 @@ export function isFeatureFlag( return setting.contentType === featureFlagContentType; } +/** + * @internal + */ + export function isFeatureFlagTargetingClientFilter(clientFilter: any): clientFilter is FeatureFlagTargetingClientFilter { + return clientFilter.name === "Microsoft.Targeting"; +} + +/** + * @internal + */ +export function isFeatureFlagTimeWindowClientFilter(clientFilter: any): clientFilter is FeatureFlagTimeWindowClientFilter { + return clientFilter.name === "Microsoft.TimeWindow"; +} + + /** + * @internal + */ +export function isFeatureFlagPercentageClientFilter(clientFilter: any): clientFilter is FeatureFlagPercentageClientFilter { + return clientFilter.name === "Microsoft.Percentage"; +} + export function deserializeFeatureFlag(setting: ConfigurationSetting): FeatureFlag | undefined { if (!isFeatureFlag(setting) || !setting.value) { return undefined; diff --git a/sdk/appconfiguration/app-configuration/src/index.ts b/sdk/appconfiguration/app-configuration/src/index.ts index b9b223eee2c0..02797496dbeb 100644 --- a/sdk/appconfiguration/app-configuration/src/index.ts +++ b/sdk/appconfiguration/app-configuration/src/index.ts @@ -9,5 +9,4 @@ export { keyVaultReferenceContentType } from "./keyvaultReference"; export { - FeatureFlag, FeatureFlagParam, FeatureFlagPercentageClientFilter, FeatureFlagTargetingClientFilter, FeatureFlagTimeWindowClientFilter, featureFlagContentType, featureFlagPrefix, isFeatureFlag -} from "./featureFlag"; \ No newline at end of file + FeatureFlag, FeatureFlagParam, FeatureFlagPercentageClientFilter, FeatureFlagTargetingClientFilter, FeatureFlagTimeWindowClientFilter, featureFlagContentType, featureFlagPrefix, isFeatureFlag, isFeatureFlagPercentageClientFilter, isFeatureFlagTargetingClientFilter, isFeatureFlagTimeWindowClientFilter} from "./featureFlag"; \ No newline at end of file diff --git a/sdk/appconfiguration/app-configuration/src/internal/helpers.ts b/sdk/appconfiguration/app-configuration/src/internal/helpers.ts index d39894028ed2..22347bd2edef 100644 --- a/sdk/appconfiguration/app-configuration/src/internal/helpers.ts +++ b/sdk/appconfiguration/app-configuration/src/internal/helpers.ts @@ -11,13 +11,10 @@ import { HttpResponseFields, HttpOnlyIfChangedField, HttpOnlyIfUnchangedField, - isKeyVaultReference, - KeyVaultReferenceContentType, - KeyVaultReference, } from "../models"; import { AppConfigurationGetKeyValuesOptionalParams, KeyValue } from "../generated/src/models"; import { deserializeFeatureFlag, featureFlagContentType } from "../featureFlag"; -import { JsonKeyVaultReference } from "./jsonModels"; +import { deserializeKeyVaultReference, keyVaultReferenceContentType } from "../keyvaultReference"; /** * Formats the etag so it can be used with a If-Match/If-None-Match header @@ -156,46 +153,23 @@ export function makeConfigurationSettingEmpty( * @internal */ export function transformKeyValue(kvp: KeyValue): ConfigurationSetting { - const setting = transformKeyValue(kvp); + const setting: ConfigurationSetting & KeyValue = { + ...kvp, + isReadOnly: !!kvp.locked + }; + + delete setting.locked; switch (setting.contentType) { case featureFlagContentType: { return deserializeFeatureFlag(setting) ?? setting; } - case KeyVaultReferenceContentType: { - if (!setting.value) { - return setting; - } - - const jsonKeyVaultRef = JSON.parse(setting.value) as JsonKeyVaultReference; - const keyVaultRef: KeyVaultReference = { - - } + case keyVaultReferenceContentType: { + return deserializeKeyVaultReference(setting) ?? setting; } + default: + return setting; } - - - } else if (isKeyVaultReference(setting)) { - const keyValueReference = ; - } - - if (obj.value != null && (isKeyVaultReference(obj) || isFeatureFlag(obj))) { - let parsedValue; - try { - - } catch (_err) { - parsedValue = undefined; - } - - if (isKeyVaultReference(obj)) { - obj.keyVault = parsedValue; - } else { - obj.featureFlag = parsedValue; - } - } - - delete obj.locked; - return obj; } /** diff --git a/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts index 92e9bbfebbec..c723ff5d1f1f 100644 --- a/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts +++ b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import { JsonKeyVaultReference } from "./internal/jsonModels"; import { ConfigurationSetting, ConfigurationSettingParam } from "./models"; export const keyVaultReferenceContentType = @@ -15,4 +16,25 @@ export interface KeyVaultReference extends KeyVaultReferenceParam, Configuration export function isKeyVaultReference(setting: ConfigurationSetting): setting is KeyVaultReference { return setting.contentType === keyVaultReferenceContentType; +} + +/** + * @internal + */ +export function deserializeKeyVaultReference(setting: ConfigurationSetting): KeyVaultReference | undefined { + if (!setting.value) { + return undefined; + } + + try { + const jsonKeyVaultRef = JSON.parse(setting.value) as JsonKeyVaultReference; + const keyVaultRef: KeyVaultReference = { + ...setting, + keyVaultSecretUri: jsonKeyVaultRef.uri + }; + + return keyVaultRef; + } catch (err) { + return undefined; + } } \ No newline at end of file diff --git a/sdk/appconfiguration/app-configuration/src/models.ts b/sdk/appconfiguration/app-configuration/src/models.ts index 9fb297269c66..4c2f841bebdd 100644 --- a/sdk/appconfiguration/app-configuration/src/models.ts +++ b/sdk/appconfiguration/app-configuration/src/models.ts @@ -81,7 +81,7 @@ export interface AddConfigurationSettingParam extends ConfigurationSettingParam /** * Parameters for creating or updating a new configuration setting */ -export interface SetConfigurationSettingParam extends ConfigurationSettingParam {} +export interface SetConfigurationSettingParam extends ConfigurationSettingParam { } /** * Standard base response for getting, deleting or updating a configuration setting diff --git a/sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts b/sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts new file mode 100644 index 000000000000..6245fc42a7f3 --- /dev/null +++ b/sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts @@ -0,0 +1,106 @@ +import { AppConfigurationClient, isFeatureFlag, isFeatureFlagPercentageClientFilter, isFeatureFlagTargetingClientFilter, isFeatureFlagTimeWindowClientFilter, isKeyVaultReference } from "../src"; + +const appConfigClient = new AppConfigurationClient("connection-string"); + +export async function sampleSyncTokenExample() { + // get sync token from EventGrid event (example event shown, based on .net sample) + const eventGridEvent = { + key: "key for setting", + label: "label for setting", + syncToken: "opaque sync token" + } + + // user treats token as an opaque value (note - this _does_ mutate the appconfigclient) + // however, this mutations happens regardless since the system can and does return + // sync token headers during normal operation. + appConfigClient.updateSyncToken(eventGridEvent.syncToken); + + // and now retrieving the value should be consistent with what happened + // in EventGrid. + const retrievedSetting = await appConfigClient.getConfigurationSetting({ + key: eventGridEvent.key, + label: eventGridEvent.label + }); + + console.log(retrievedSetting); +} + +export async function sampleKeyVaultReference() { + const secretClient: SecretClient = {} as any; // An actual KeyVault SecretClient gotten from "somewhere" else + + const setting = await appConfigClient.getConfigurationSetting({ + key: "my keyvault reference" + }); + + if (isKeyVaultReference(setting)) { + // setting is a `KeyVaultReference` + + // use KeyVault to parse secret ID and retrieve it. + const parsedSecretId = parseKeyVaultSecretId(setting.keyVaultSecretUri); + const actualSecret = await secretClient.getSecret(parsedSecretId.name); + + console.log(`Retrieved secret value: ${actualSecret.value}`) + } else { + // otherwise it's potentially a feature flag or just a plain ConfigurationSetting + } +} + +export async function sampleFeatureFlag() { + const setting = await appConfigClient.getConfigurationSetting({ + key: "my feature flag" + }); + + if (isFeatureFlag(setting)) { + // setting is a `FeatureFlag` + + const conditions = setting.conditions; + + // FeatureFlag specific properties: + // + // setting.displayName + // setting.enabled + + // + // the client filters are the real meat of the FeatureFlag. + // + for (const clientFilter of conditions.clientFilters) { + if (isFeatureFlagTargetingClientFilter(clientFilter)) { + // some of the fields: + // clientFilter.parameters.audience + // clientFilter.parameters.audience.groups[0].name + // clientFilter.parameters.audience.groups[0].rolloutPercentage + // clientFilter.parameters.audience.users[0] // string + // clientFilter.parameters.defaultRolloutPercentage + } else if (isFeatureFlagTimeWindowClientFilter(clientFilter)) { + // clientFilter.parameters.end; + // clientFilter.parameters.start; + } else if (isFeatureFlagPercentageClientFilter(clientFilter)) { + // TODO: didn't analyze this one. Just comes back as key/value pairs, perhaps with a + // numeric constraint? + } else { + // we return a generic object and they get whatever was deserialized. + } + } + } else { + // otherwise it's a KeyVaultReference or just a ConfigurationSetting + } +} + +/** BEGIN: Interfaces from KeyVault */ +export interface SecretClient { + getSecret( + secretName: string, + ): Promise; +} + +export function parseKeyVaultSecretId(_id: string): KeyVaultSecretId { throw new Error("For sample only") } + +export interface KeyVaultSecretId { + /** other fields elided ... */ + name: string; +} +/** END: Interfaces from KeyVault */ + +export interface KeyVaultSecret { + value?: string; +} From e4a36bf7dcf9d1c3ace5f51b9168303e60ae1c95 Mon Sep 17 00:00:00 2001 From: Richard Park Date: Mon, 15 Mar 2021 14:30:30 -0700 Subject: [PATCH 03/50] Update to SecretReference naming for consistency --- .../review/app-configuration.api.md | 28 +++++++++---------- .../app-configuration/src/index.ts | 4 +-- .../app-configuration/src/internal/helpers.ts | 6 ++-- .../src/keyvaultReference.ts | 18 ++++++------ .../test/samplesForApiReview.ts | 6 ++-- 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md index a037d6f5bd68..13465bf3ce8c 100644 --- a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md +++ b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md @@ -194,20 +194,7 @@ export function isFeatureFlagTargetingClientFilter(clientFilter: any): clientFil export function isFeatureFlagTimeWindowClientFilter(clientFilter: any): clientFilter is FeatureFlagTimeWindowClientFilter; // @public (undocumented) -export function isKeyVaultReference(setting: ConfigurationSetting): setting is KeyVaultReference; - -// @public (undocumented) -export interface KeyVaultReference extends KeyVaultReferenceParam, ConfigurationSetting { -} - -// @public (undocumented) -export const keyVaultReferenceContentType = "application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8"; - -// @public (undocumented) -export interface KeyVaultReferenceParam extends ConfigurationSettingParam { - // (undocumented) - keyVaultSecretUri: string; -} +export function isSecretReference(setting: ConfigurationSetting): setting is SecretReference; // @public export interface ListConfigurationSettingPage extends HttpResponseField { @@ -239,6 +226,19 @@ export interface OptionalFields { fields?: (keyof ConfigurationSetting)[]; } +// @public (undocumented) +export interface SecretReference extends SecretReferenceParam, ConfigurationSetting { +} + +// @public (undocumented) +export const secretReferenceContentType = "application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8"; + +// @public (undocumented) +export interface SecretReferenceParam extends ConfigurationSettingParam { + // (undocumented) + secretId: string; +} + // @public export interface SetConfigurationSettingOptions extends HttpOnlyIfUnchangedField, OperationOptions { } diff --git a/sdk/appconfiguration/app-configuration/src/index.ts b/sdk/appconfiguration/app-configuration/src/index.ts index 02797496dbeb..6f4ef5d81084 100644 --- a/sdk/appconfiguration/app-configuration/src/index.ts +++ b/sdk/appconfiguration/app-configuration/src/index.ts @@ -5,8 +5,8 @@ export * from "./models"; export { AppConfigurationClientOptions, AppConfigurationClient } from "./appConfigurationClient"; export { - KeyVaultReference, KeyVaultReferenceParam, isKeyVaultReference, - keyVaultReferenceContentType } from "./keyvaultReference"; + SecretReference, SecretReferenceParam, isSecretReference, + secretReferenceContentType } from "./keyvaultReference"; export { FeatureFlag, FeatureFlagParam, FeatureFlagPercentageClientFilter, FeatureFlagTargetingClientFilter, FeatureFlagTimeWindowClientFilter, featureFlagContentType, featureFlagPrefix, isFeatureFlag, isFeatureFlagPercentageClientFilter, isFeatureFlagTargetingClientFilter, isFeatureFlagTimeWindowClientFilter} from "./featureFlag"; \ No newline at end of file diff --git a/sdk/appconfiguration/app-configuration/src/internal/helpers.ts b/sdk/appconfiguration/app-configuration/src/internal/helpers.ts index 22347bd2edef..5b28dc03a364 100644 --- a/sdk/appconfiguration/app-configuration/src/internal/helpers.ts +++ b/sdk/appconfiguration/app-configuration/src/internal/helpers.ts @@ -14,7 +14,7 @@ import { } from "../models"; import { AppConfigurationGetKeyValuesOptionalParams, KeyValue } from "../generated/src/models"; import { deserializeFeatureFlag, featureFlagContentType } from "../featureFlag"; -import { deserializeKeyVaultReference, keyVaultReferenceContentType } from "../keyvaultReference"; +import { deserializeSecretReference, secretReferenceContentType } from "../keyvaultReference"; /** * Formats the etag so it can be used with a If-Match/If-None-Match header @@ -164,8 +164,8 @@ export function transformKeyValue(kvp: KeyValue): ConfigurationSetting { case featureFlagContentType: { return deserializeFeatureFlag(setting) ?? setting; } - case keyVaultReferenceContentType: { - return deserializeKeyVaultReference(setting) ?? setting; + case secretReferenceContentType: { + return deserializeSecretReference(setting) ?? setting; } default: return setting; diff --git a/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts index c723ff5d1f1f..f46e3ca8ab61 100644 --- a/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts +++ b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts @@ -4,33 +4,33 @@ import { JsonKeyVaultReference } from "./internal/jsonModels"; import { ConfigurationSetting, ConfigurationSettingParam } from "./models"; -export const keyVaultReferenceContentType = +export const secretReferenceContentType = "application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8"; -export interface KeyVaultReferenceParam extends ConfigurationSettingParam { - keyVaultSecretUri: string; +export interface SecretReferenceParam extends ConfigurationSettingParam { + secretId: string; } -export interface KeyVaultReference extends KeyVaultReferenceParam, ConfigurationSetting { +export interface SecretReference extends SecretReferenceParam, ConfigurationSetting { } -export function isKeyVaultReference(setting: ConfigurationSetting): setting is KeyVaultReference { - return setting.contentType === keyVaultReferenceContentType; +export function isSecretReference(setting: ConfigurationSetting): setting is SecretReference { + return setting.contentType === secretReferenceContentType; } /** * @internal */ -export function deserializeKeyVaultReference(setting: ConfigurationSetting): KeyVaultReference | undefined { +export function deserializeSecretReference(setting: ConfigurationSetting): SecretReference | undefined { if (!setting.value) { return undefined; } try { const jsonKeyVaultRef = JSON.parse(setting.value) as JsonKeyVaultReference; - const keyVaultRef: KeyVaultReference = { + const keyVaultRef: SecretReference = { ...setting, - keyVaultSecretUri: jsonKeyVaultRef.uri + secretId: jsonKeyVaultRef.uri }; return keyVaultRef; diff --git a/sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts b/sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts index 6245fc42a7f3..01b705879969 100644 --- a/sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts +++ b/sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts @@ -1,4 +1,4 @@ -import { AppConfigurationClient, isFeatureFlag, isFeatureFlagPercentageClientFilter, isFeatureFlagTargetingClientFilter, isFeatureFlagTimeWindowClientFilter, isKeyVaultReference } from "../src"; +import { AppConfigurationClient, isFeatureFlag, isFeatureFlagPercentageClientFilter, isFeatureFlagTargetingClientFilter, isFeatureFlagTimeWindowClientFilter, isSecretReference } from "../src"; const appConfigClient = new AppConfigurationClient("connection-string"); @@ -32,11 +32,11 @@ export async function sampleKeyVaultReference() { key: "my keyvault reference" }); - if (isKeyVaultReference(setting)) { + if (isSecretReference(setting)) { // setting is a `KeyVaultReference` // use KeyVault to parse secret ID and retrieve it. - const parsedSecretId = parseKeyVaultSecretId(setting.keyVaultSecretUri); + const parsedSecretId = parseKeyVaultSecretId(setting.secretId); const actualSecret = await secretClient.getSecret(parsedSecretId.name); console.log(`Retrieved secret value: ${actualSecret.value}`) From 4153a919f1bc7b17bf68f8d079c0561a886ef1a3 Mon Sep 17 00:00:00 2001 From: Richard Park Date: Mon, 15 Mar 2021 14:47:00 -0700 Subject: [PATCH 04/50] Remove [key:string]: any's. --- .../app-configuration/review/app-configuration.api.md | 2 -- sdk/appconfiguration/app-configuration/src/featureFlag.ts | 6 ++---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md index 13465bf3ce8c..06fe98d9a843 100644 --- a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md +++ b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md @@ -123,7 +123,6 @@ export interface FeatureFlagTargetingClientFilter { }[]; }; defaultRolloutPercentage: number; - [key: string]: any; }; } @@ -135,7 +134,6 @@ export interface FeatureFlagTimeWindowClientFilter { parameters: { start: string; end: string; - [key: string]: any; }; } diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index 67bdc775d7ba..04a6d046a341 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -43,9 +43,7 @@ export interface FeatureFlagTargetingClientFilter { }; defaultRolloutPercentage: number; - // here to avoid potential slicing of underlying attributes as objects expand - // in the future. - [key: string]: any; + // [key: string]: any; }; } @@ -54,7 +52,7 @@ export interface FeatureFlagTimeWindowClientFilter { parameters: { start: string; end: string; - [key: string]: any; + // [key: string]: any; }; } From 5a178ed45cb4f0cca9ac4fa65e874043cd2b7f2a Mon Sep 17 00:00:00 2001 From: richardpark-msft Date: Tue, 16 Mar 2021 13:24:48 -0700 Subject: [PATCH 05/50] Make isFeatureFlagClientFilter() handle multiple types of client filters with a string union as the parameter. --- .../src/appConfigurationClient.ts | 50 +++++------ .../app-configuration/src/featureFlag.ts | 88 +++++++++++++------ .../app-configuration/src/index.ts | 18 +++- .../app-configuration/src/internal/helpers.ts | 2 +- .../src/internal/jsonModels.ts | 16 ++-- .../src/keyvaultReference.ts | 9 +- .../app-configuration/src/models.ts | 4 +- .../test/internal/helpers.spec.ts | 2 +- .../test/samplesForApiReview.ts | 33 +++---- 9 files changed, 136 insertions(+), 86 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts b/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts index 0beeac29b78b..384751ede9b7 100644 --- a/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts +++ b/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts @@ -15,7 +15,7 @@ import { ServiceClientCredentials, UserAgentOptions, getDefaultUserAgentValue as getCoreHttpDefaultUserAgentValue, - userAgentPolicy, + userAgentPolicy } from "@azure/core-http"; import { throttlingRetryPolicy } from "./policies/throttlingRetryPolicy"; import { TokenCredential } from "@azure/identity"; @@ -40,7 +40,7 @@ import { SetConfigurationSettingParam, SetConfigurationSettingResponse, SetReadOnlyOptions, - SetReadOnlyResponse, + SetReadOnlyResponse } from "./models"; import { checkAndFormatIfAndIfNoneMatch, @@ -51,13 +51,13 @@ import { transformKeyValueResponseWithStatusCode, transformKeyValue, formatAcceptDateTime, - formatFieldsForSelect, + formatFieldsForSelect } from "./internal/helpers"; import { tracingPolicy } from "@azure/core-http"; import { Spanner } from "./internal/tracingHelpers"; import { AppConfigurationGetKeyValuesResponse, - AppConfigurationOptionalParams as GeneratedAppConfigurationClientOptions, + AppConfigurationOptionalParams as GeneratedAppConfigurationClientOptions } from "./generated/src/models"; import { syncTokenPolicy, SyncTokens } from "./internal/synctokenpolicy"; @@ -77,8 +77,8 @@ const deserializationContentTypes = { "application/vnd.microsoft.appconfig.kv+json", "application/vnd.microsoft.appconfig.kvs+json", "application/vnd.microsoft.appconfig.keyset+json", - "application/vnd.microsoft.appconfig.revs+json", - ], + "application/vnd.microsoft.appconfig.revs+json" + ] }; /** @@ -198,7 +198,7 @@ export class AppConfigurationClient { ifNoneMatch: "*", label: configurationSetting.label, entity: configurationSetting, - ...newOptions, + ...newOptions }); return transformKeyValueResponse(originalResponse); @@ -224,7 +224,7 @@ export class AppConfigurationClient { const originalResponse = await this.client.deleteKeyValue(id.key, { label: id.label, ...newOptions, - ...checkAndFormatIfAndIfNoneMatch(id, options), + ...checkAndFormatIfAndIfNoneMatch(id, options) }); return transformKeyValueResponseWithStatusCode(originalResponse); @@ -255,7 +255,7 @@ export class AppConfigurationClient { label: id.label, select: formatFieldsForSelect(options.fields), ...formatAcceptDateTime(options), - ...checkAndFormatIfAndIfNoneMatch(id, options), + ...checkAndFormatIfAndIfNoneMatch(id, options) }); const response: GetConfigurationSettingResponse = transformKeyValueResponseWithStatusCode( @@ -304,7 +304,7 @@ export class AppConfigurationClient { // The appconfig service doesn't currently support letting you select a page size // so we're ignoring their setting for now. return this.listConfigurationSettingsByPage(options); - }, + } }; } @@ -329,7 +329,7 @@ export class AppConfigurationClient { const response = await this.client.getKeyValues({ ...newOptions, ...formatAcceptDateTime(options), - ...formatFiltersAndSelect(options), + ...formatFiltersAndSelect(options) }); return response; @@ -348,7 +348,7 @@ export class AppConfigurationClient { ...newOptions, ...formatAcceptDateTime(options), ...formatFiltersAndSelect(options), - after: extractAfterTokenFromNextLink(currentResponse.nextLink!), + after: extractAfterTokenFromNextLink(currentResponse.nextLink!) }); return response; @@ -368,7 +368,7 @@ export class AppConfigurationClient { ) { yield { ...currentResponse, - items: currentResponse.items != null ? currentResponse.items.map(transformKeyValue) : [], + items: currentResponse.items != null ? currentResponse.items.map(transformKeyValue) : [] }; } @@ -398,7 +398,7 @@ export class AppConfigurationClient { // The appconfig service doesn't currently support letting you select a page size // so we're ignoring their setting for now. return this.listRevisionsByPage(options); - }, + } }; } @@ -423,7 +423,7 @@ export class AppConfigurationClient { const response = await this.client.getRevisions({ ...newOptions, ...formatAcceptDateTime(options), - ...formatFiltersAndSelect(newOptions), + ...formatFiltersAndSelect(newOptions) }); return response; @@ -432,7 +432,7 @@ export class AppConfigurationClient { yield { ...currentResponse, - items: currentResponse.items != null ? currentResponse.items.map(transformKeyValue) : [], + items: currentResponse.items != null ? currentResponse.items.map(transformKeyValue) : [] }; while (currentResponse.nextLink) { @@ -441,7 +441,7 @@ export class AppConfigurationClient { ...newOptions, ...formatAcceptDateTime(options), ...formatFiltersAndSelect(options), - after: extractAfterTokenFromNextLink(currentResponse.nextLink!), + after: extractAfterTokenFromNextLink(currentResponse.nextLink!) }); }); @@ -451,7 +451,7 @@ export class AppConfigurationClient { yield { ...currentResponse, - items: currentResponse.items != null ? currentResponse.items.map(transformKeyValue) : [], + items: currentResponse.items != null ? currentResponse.items.map(transformKeyValue) : [] }; } } @@ -481,7 +481,7 @@ export class AppConfigurationClient { ...newOptions, label: configurationSetting.label, entity: configurationSetting, - ...checkAndFormatIfAndIfNoneMatch(configurationSetting, options), + ...checkAndFormatIfAndIfNoneMatch(configurationSetting, options) }); return transformKeyValueResponse(response); @@ -505,7 +505,7 @@ export class AppConfigurationClient { const response = await this.client.putLock(id.key, { ...newOptions, label: id.label, - ...checkAndFormatIfAndIfNoneMatch(id, options), + ...checkAndFormatIfAndIfNoneMatch(id, options) }); return transformKeyValueResponse(response); @@ -513,7 +513,7 @@ export class AppConfigurationClient { const response = await this.client.deleteLock(id.key, { ...newOptions, label: id.label, - ...checkAndFormatIfAndIfNoneMatch(id, options), + ...checkAndFormatIfAndIfNoneMatch(id, options) }); return transformKeyValueResponse(response); @@ -538,12 +538,12 @@ export function getGeneratedClientOptions( const retryPolicies = [ exponentialRetryPolicy(), systemErrorRetryPolicy(), - throttlingRetryPolicy(), + throttlingRetryPolicy() ]; const userAgent = getUserAgentPrefix( internalAppConfigOptions.userAgentOptions && - internalAppConfigOptions.userAgentOptions.userAgentPrefix + internalAppConfigOptions.userAgentOptions.userAgentPrefix ); return { @@ -556,9 +556,9 @@ export function getGeneratedClientOptions( syncTokenPolicy(syncTokens), userAgentPolicy({ value: userAgent }), ...retryPolicies, - ...defaults, + ...defaults ], - generateClientRequestIdHeader: true, + generateClientRequestIdHeader: true }; } diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index 04a6d046a341..9f040e669ee8 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -1,7 +1,12 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { isJsonFeatureFlagPercentageClientFilter, isJsonFeatureFlagTargetingClientFilter, isJsonFeatureFlagTimeWindowClientFilter, JsonFeatureFlag } from "./internal/jsonModels"; +import { + isJsonFeatureFlagPercentageClientFilter, + isJsonFeatureFlagTargetingClientFilter, + isJsonFeatureFlagTimeWindowClientFilter, + JsonFeatureFlag +} from "./internal/jsonModels"; import { ConfigurationSetting, ConfigurationSettingParam } from "./models"; /** @@ -16,7 +21,7 @@ export const featureFlagContentType = "application/vnd.microsoft.appconfig.ff+js export interface FeatureFlagParam extends ConfigurationSettingParam { // TODO: can we hoist 'clientFilters' higher and avoid this sub-field? Need to talk to team. - conditions: { + conditions: { clientFilters: ( | FeatureFlagTargetingClientFilter | FeatureFlagTimeWindowClientFilter @@ -27,13 +32,14 @@ export interface FeatureFlagParam extends ConfigurationSettingParam { description?: string; displayName: string; enabled: boolean; -}; +} export interface FeatureFlag extends FeatureFlagParam, ConfigurationSetting {} export interface FeatureFlagTargetingClientFilter { name: "Microsoft.Targeting"; - parameters: { // TODO: can we hoist these values up directly into the filter, rather than having this 'parameters' intermediary. + parameters: { + // TODO: can we hoist these values up directly into the filter, rather than having this 'parameters' intermediary. audience: { users: string[]; groups: { @@ -63,31 +69,52 @@ export interface FeatureFlagPercentageClientFilter { }; } -export function isFeatureFlag( - setting: ConfigurationSetting | FeatureFlag -): setting is FeatureFlag { +export function isFeatureFlag(setting: ConfigurationSetting | FeatureFlag): setting is FeatureFlag { return setting.contentType === featureFlagContentType; } -/** - * @internal - */ - export function isFeatureFlagTargetingClientFilter(clientFilter: any): clientFilter is FeatureFlagTargetingClientFilter { +function isFeatureFlagTargetingClientFilter( + clientFilter: any +): clientFilter is FeatureFlagTargetingClientFilter { return clientFilter.name === "Microsoft.Targeting"; } -/** - * @internal - */ -export function isFeatureFlagTimeWindowClientFilter(clientFilter: any): clientFilter is FeatureFlagTimeWindowClientFilter { +function isFeatureFlagTimeWindowClientFilter( + clientFilter: any +): clientFilter is FeatureFlagTimeWindowClientFilter { return clientFilter.name === "Microsoft.TimeWindow"; } - /** - * @internal - */ -export function isFeatureFlagPercentageClientFilter(clientFilter: any): clientFilter is FeatureFlagPercentageClientFilter { - return clientFilter.name === "Microsoft.Percentage"; +function isFeatureFlagPercentageClientFilter( + clientFilter: any +): clientFilter is FeatureFlagPercentageClientFilter { + return clientFilter.name === "Microsoft.Percentage"; +} + +export type FeatureFlagType< + T extends "targeting" | "timeWindow" | "percentage" +> = T extends "targeting" + ? FeatureFlagTargetingClientFilter + : T extends "timeWindow" + ? FeatureFlagTimeWindowClientFilter + : T extends "percentage" + ? FeatureFlagPercentageClientFilter + : never; + +export function isFeatureFlagClientFilter( + type: T, + obj: any +): obj is FeatureFlagType { + switch (type) { + case "targeting": + return isFeatureFlagTargetingClientFilter(obj); + case "timeWindow": + return isFeatureFlagTimeWindowClientFilter(obj); + case "percentage": + return isFeatureFlagPercentageClientFilter(obj); + default: + throw new Error(`Invalid feature flag filter type ${type}`); + } } export function deserializeFeatureFlag(setting: ConfigurationSetting): FeatureFlag | undefined { @@ -95,7 +122,7 @@ export function deserializeFeatureFlag(setting: ConfigurationSetting): FeatureFl return undefined; } - let jsonFeatureFlag: JsonFeatureFlag + let jsonFeatureFlag: JsonFeatureFlag; try { jsonFeatureFlag = JSON.parse(setting.value) as JsonFeatureFlag; @@ -115,13 +142,15 @@ export function deserializeFeatureFlag(setting: ConfigurationSetting): FeatureFl } export function serializeFeatureFlag(_setting: FeatureFlag): ConfigurationSetting { - throw new Error("Not implemented") + throw new Error("Not implemented"); } /** * @internal */ -export function convertJsonConditions(conditions: JsonFeatureFlag['conditions']): FeatureFlag['conditions'] { +export function convertJsonConditions( + conditions: JsonFeatureFlag["conditions"] +): FeatureFlag["conditions"] { const clientFilters = conditions.client_filters.map((jsonFilter) => { if (isJsonFeatureFlagTargetingClientFilter(jsonFilter)) { // try not to slice any unknown attributes @@ -129,11 +158,12 @@ export function convertJsonConditions(conditions: JsonFeatureFlag['conditions']) name: jsonFilter.name, parameters: { audience: { - groups: jsonFilter.parameters.Audience?.Groups.map((grp) => ({ - name: grp.Name, - rolloutPercentage: grp.RolloutPercentage - })) || [], - users: jsonFilter.parameters.Audience?.Users || [], + groups: + jsonFilter.parameters.Audience?.Groups.map((grp) => ({ + name: grp.Name, + rolloutPercentage: grp.RolloutPercentage + })) || [], + users: jsonFilter.parameters.Audience?.Users || [] }, defaultRolloutPercentage: jsonFilter.parameters.DefaultRolloutPercentage } @@ -163,4 +193,4 @@ export function convertJsonConditions(conditions: JsonFeatureFlag['conditions']) return { clientFilters }; -} \ No newline at end of file +} diff --git a/sdk/appconfiguration/app-configuration/src/index.ts b/sdk/appconfiguration/app-configuration/src/index.ts index 6f4ef5d81084..83a87ec33809 100644 --- a/sdk/appconfiguration/app-configuration/src/index.ts +++ b/sdk/appconfiguration/app-configuration/src/index.ts @@ -5,8 +5,20 @@ export * from "./models"; export { AppConfigurationClientOptions, AppConfigurationClient } from "./appConfigurationClient"; export { - SecretReference, SecretReferenceParam, isSecretReference, - secretReferenceContentType } from "./keyvaultReference"; + SecretReference, + SecretReferenceParam, + isSecretReference, + secretReferenceContentType +} from "./keyvaultReference"; export { - FeatureFlag, FeatureFlagParam, FeatureFlagPercentageClientFilter, FeatureFlagTargetingClientFilter, FeatureFlagTimeWindowClientFilter, featureFlagContentType, featureFlagPrefix, isFeatureFlag, isFeatureFlagPercentageClientFilter, isFeatureFlagTargetingClientFilter, isFeatureFlagTimeWindowClientFilter} from "./featureFlag"; \ No newline at end of file + FeatureFlag, + FeatureFlagParam, + FeatureFlagPercentageClientFilter, + FeatureFlagTargetingClientFilter, + FeatureFlagTimeWindowClientFilter, + featureFlagContentType, + featureFlagPrefix, + isFeatureFlag, + isFeatureFlagClientFilter +} from "./featureFlag"; diff --git a/sdk/appconfiguration/app-configuration/src/internal/helpers.ts b/sdk/appconfiguration/app-configuration/src/internal/helpers.ts index 5b28dc03a364..c76f1fac992c 100644 --- a/sdk/appconfiguration/app-configuration/src/internal/helpers.ts +++ b/sdk/appconfiguration/app-configuration/src/internal/helpers.ts @@ -10,7 +10,7 @@ import { HttpResponseField, HttpResponseFields, HttpOnlyIfChangedField, - HttpOnlyIfUnchangedField, + HttpOnlyIfUnchangedField } from "../models"; import { AppConfigurationGetKeyValuesOptionalParams, KeyValue } from "../generated/src/models"; import { deserializeFeatureFlag, featureFlagContentType } from "../featureFlag"; diff --git a/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts index bbfdee2be125..e9f0cfc96c9c 100644 --- a/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts +++ b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts @@ -64,22 +64,28 @@ export interface JsonFeatureFlagPercentageClientFilter { /** * @internal */ -export function isJsonFeatureFlagTargetingClientFilter(clientFilter: any): clientFilter is JsonFeatureFlagTargetingClientFilter { +export function isJsonFeatureFlagTargetingClientFilter( + clientFilter: any +): clientFilter is JsonFeatureFlagTargetingClientFilter { return clientFilter.name === "Microsoft.Targeting"; } /** * @internal */ -export function isJsonFeatureFlagTimeWindowClientFilter(clientFilter: any): clientFilter is JsonFeatureFlagTimeWindowClientFilter { +export function isJsonFeatureFlagTimeWindowClientFilter( + clientFilter: any +): clientFilter is JsonFeatureFlagTimeWindowClientFilter { return clientFilter.name === "Microsoft.TimeWindow"; } - /** +/** * @internal */ -export function isJsonFeatureFlagPercentageClientFilter(clientFilter: any): clientFilter is JsonFeatureFlagPercentageClientFilter { - return clientFilter.name === "Microsoft.Percentage"; +export function isJsonFeatureFlagPercentageClientFilter( + clientFilter: any +): clientFilter is JsonFeatureFlagPercentageClientFilter { + return clientFilter.name === "Microsoft.Percentage"; } // keyvault reference diff --git a/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts index f46e3ca8ab61..caa7b494810c 100644 --- a/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts +++ b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts @@ -11,8 +11,7 @@ export interface SecretReferenceParam extends ConfigurationSettingParam { secretId: string; } -export interface SecretReference extends SecretReferenceParam, ConfigurationSetting { -} +export interface SecretReference extends SecretReferenceParam, ConfigurationSetting {} export function isSecretReference(setting: ConfigurationSetting): setting is SecretReference { return setting.contentType === secretReferenceContentType; @@ -21,7 +20,9 @@ export function isSecretReference(setting: ConfigurationSetting): setting is Sec /** * @internal */ -export function deserializeSecretReference(setting: ConfigurationSetting): SecretReference | undefined { +export function deserializeSecretReference( + setting: ConfigurationSetting +): SecretReference | undefined { if (!setting.value) { return undefined; } @@ -37,4 +38,4 @@ export function deserializeSecretReference(setting: ConfigurationSetting): Secre } catch (err) { return undefined; } -} \ No newline at end of file +} diff --git a/sdk/appconfiguration/app-configuration/src/models.ts b/sdk/appconfiguration/app-configuration/src/models.ts index 4c2f841bebdd..b016e92ac577 100644 --- a/sdk/appconfiguration/app-configuration/src/models.ts +++ b/sdk/appconfiguration/app-configuration/src/models.ts @@ -43,7 +43,7 @@ export interface ConfigurationSettingParam extends ConfigurationSettingId { * Tags for this key */ tags?: { [propertyName: string]: string }; -}; +} /** * Configuration setting with extra metadata from the server, indicating @@ -81,7 +81,7 @@ export interface AddConfigurationSettingParam extends ConfigurationSettingParam /** * Parameters for creating or updating a new configuration setting */ -export interface SetConfigurationSettingParam extends ConfigurationSettingParam { } +export interface SetConfigurationSettingParam extends ConfigurationSettingParam {} /** * Standard base response for getting, deleting or updating a configuration setting diff --git a/sdk/appconfiguration/app-configuration/test/internal/helpers.spec.ts b/sdk/appconfiguration/app-configuration/test/internal/helpers.spec.ts index c39a855e6199..3da8cdadcc73 100644 --- a/sdk/appconfiguration/app-configuration/test/internal/helpers.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/internal/helpers.spec.ts @@ -259,7 +259,7 @@ describe("helper methods", () => { lastModified: new Date(), isReadOnly: true, tags: {}, - value: "", + value: "" }; return Object.keys(configObjectWithAllFieldsRequired).sort() as (keyof ConfigurationSetting)[]; diff --git a/sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts b/sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts index 01b705879969..a850052666e6 100644 --- a/sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts +++ b/sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts @@ -1,4 +1,5 @@ -import { AppConfigurationClient, isFeatureFlag, isFeatureFlagPercentageClientFilter, isFeatureFlagTargetingClientFilter, isFeatureFlagTimeWindowClientFilter, isSecretReference } from "../src"; +import { AppConfigurationClient, isFeatureFlag, isSecretReference } from "../src"; +import { isFeatureFlagClientFilter } from "../src/featureFlag"; const appConfigClient = new AppConfigurationClient("connection-string"); @@ -8,7 +9,7 @@ export async function sampleSyncTokenExample() { key: "key for setting", label: "label for setting", syncToken: "opaque sync token" - } + }; // user treats token as an opaque value (note - this _does_ mutate the appconfigclient) // however, this mutations happens regardless since the system can and does return @@ -27,7 +28,7 @@ export async function sampleSyncTokenExample() { export async function sampleKeyVaultReference() { const secretClient: SecretClient = {} as any; // An actual KeyVault SecretClient gotten from "somewhere" else - + const setting = await appConfigClient.getConfigurationSetting({ key: "my keyvault reference" }); @@ -39,7 +40,7 @@ export async function sampleKeyVaultReference() { const parsedSecretId = parseKeyVaultSecretId(setting.secretId); const actualSecret = await secretClient.getSecret(parsedSecretId.name); - console.log(`Retrieved secret value: ${actualSecret.value}`) + console.log(`Retrieved secret value: ${actualSecret.value}`); } else { // otherwise it's potentially a feature flag or just a plain ConfigurationSetting } @@ -56,7 +57,7 @@ export async function sampleFeatureFlag() { const conditions = setting.conditions; // FeatureFlag specific properties: - // + // // setting.displayName // setting.enabled @@ -64,18 +65,18 @@ export async function sampleFeatureFlag() { // the client filters are the real meat of the FeatureFlag. // for (const clientFilter of conditions.clientFilters) { - if (isFeatureFlagTargetingClientFilter(clientFilter)) { - // some of the fields: + if (isFeatureFlagClientFilter("targeting", clientFilter)) { + // some of the fields: // clientFilter.parameters.audience // clientFilter.parameters.audience.groups[0].name // clientFilter.parameters.audience.groups[0].rolloutPercentage // clientFilter.parameters.audience.users[0] // string - // clientFilter.parameters.defaultRolloutPercentage - } else if (isFeatureFlagTimeWindowClientFilter(clientFilter)) { + // clientFilter.parameters.defaultRolloutPercentage + } else if (isFeatureFlagClientFilter("timeWindow", clientFilter)) { // clientFilter.parameters.end; // clientFilter.parameters.start; - } else if (isFeatureFlagPercentageClientFilter(clientFilter)) { - // TODO: didn't analyze this one. Just comes back as key/value pairs, perhaps with a + } else if (isFeatureFlagClientFilter("percentage", clientFilter)) { + // TODO: didn't analyze this one. Just comes back as key/value pairs, perhaps with a // numeric constraint? } else { // we return a generic object and they get whatever was deserialized. @@ -88,12 +89,12 @@ export async function sampleFeatureFlag() { /** BEGIN: Interfaces from KeyVault */ export interface SecretClient { - getSecret( - secretName: string, - ): Promise; + getSecret(secretName: string): Promise; } -export function parseKeyVaultSecretId(_id: string): KeyVaultSecretId { throw new Error("For sample only") } +export function parseKeyVaultSecretId(_id: string): KeyVaultSecretId { + throw new Error("For sample only"); +} export interface KeyVaultSecretId { /** other fields elided ... */ @@ -102,5 +103,5 @@ export interface KeyVaultSecretId { /** END: Interfaces from KeyVault */ export interface KeyVaultSecret { - value?: string; + value?: string; } From 9a1d05bd3cc1dbdce0c4e011d4b29f044d9c7165 Mon Sep 17 00:00:00 2001 From: richardpark-msft Date: Tue, 16 Mar 2021 13:51:13 -0700 Subject: [PATCH 06/50] Updated with the new isFeatureFlagClientFilter --- .../review/app-configuration.api.md | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md index 06fe98d9a843..effcecf4ab57 100644 --- a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md +++ b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md @@ -176,20 +176,10 @@ export interface HttpResponseFields { // @public (undocumented) export function isFeatureFlag(setting: ConfigurationSetting | FeatureFlag): setting is FeatureFlag; -// Warning: (ae-internal-missing-underscore) The name "isFeatureFlagPercentageClientFilter" should be prefixed with an underscore because the declaration is marked as @internal +// Warning: (ae-forgotten-export) The symbol "FeatureFlagType" needs to be exported by the entry point index.d.ts // -// @internal (undocumented) -export function isFeatureFlagPercentageClientFilter(clientFilter: any): clientFilter is FeatureFlagPercentageClientFilter; - -// Warning: (ae-internal-missing-underscore) The name "isFeatureFlagTargetingClientFilter" should be prefixed with an underscore because the declaration is marked as @internal -// -// @internal (undocumented) -export function isFeatureFlagTargetingClientFilter(clientFilter: any): clientFilter is FeatureFlagTargetingClientFilter; - -// Warning: (ae-internal-missing-underscore) The name "isFeatureFlagTimeWindowClientFilter" should be prefixed with an underscore because the declaration is marked as @internal -// -// @internal (undocumented) -export function isFeatureFlagTimeWindowClientFilter(clientFilter: any): clientFilter is FeatureFlagTimeWindowClientFilter; +// @public (undocumented) +export function isFeatureFlagClientFilter(type: T, obj: any): obj is FeatureFlagType; // @public (undocumented) export function isSecretReference(setting: ConfigurationSetting): setting is SecretReference; From b2a45b75c73afeb8525a71b708d1417f53da14b1 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Sat, 27 Mar 2021 01:54:18 -0700 Subject: [PATCH 07/50] changelog --- sdk/appconfiguration/app-configuration/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/appconfiguration/app-configuration/CHANGELOG.md b/sdk/appconfiguration/app-configuration/CHANGELOG.md index cceb1680ff88..6bceec6c6822 100644 --- a/sdk/appconfiguration/app-configuration/CHANGELOG.md +++ b/sdk/appconfiguration/app-configuration/CHANGELOG.md @@ -2,8 +2,10 @@ ## 1.2.0-beta.1 (Unreleased) +- New `SecretReferenceConfigurationSetting` and `FeatureFlagConfigurationSetting`types to represent configuration settings that references KeyVault Secret reference and feature flag respectively. + [#14342](https://github.com/Azure/azure-sdk-for-js/pull/14342) - Added `updateSyncToken` method to `AppConfigurationClient` to be able to provide external synchronization tokens. - [PR #14507](https://github.com/Azure/azure-sdk-for-js/pull/14507) + [#14507](https://github.com/Azure/azure-sdk-for-js/pull/14507) ## 1.1.1 (2021-03-25) From 168e8316d3d0b36d023ab0c38eafa75cbb553107 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Mon, 29 Mar 2021 13:07:24 -0700 Subject: [PATCH 08/50] feature flag tests --- .../test/public/index.spec.ts | 133 +++++++++++++++++- 1 file changed, 131 insertions(+), 2 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts index 39e1096d3e3d..3f5a9f392d6e 100644 --- a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts @@ -11,8 +11,19 @@ import { assertThrowsAbortError, startRecorder } from "./utils/testHelpers"; -import { AppConfigurationClient, ConfigurationSetting, ConfigurationSettingParam } from "../../src"; -import { delay } from "@azure/core-http"; +import { + AppConfigurationClient, + ConfigurationSetting, + ConfigurationSettingParam, + FeatureFlag, + featureFlagContentType, + FeatureFlagPercentageClientFilter, + featureFlagPrefix, + FeatureFlagTargetingClientFilter, + FeatureFlagTimeWindowClientFilter, + isFeatureFlag +} from "../../src"; +import { delay, generateUuid } from "@azure/core-http"; import { Recorder } from "@azure/test-utils-recorder"; import { Context } from "mocha"; @@ -1140,4 +1151,122 @@ describe("AppConfigurationClient", () => { }); }); }); + + describe("FeatureFlag configuration setting", () => { + const clientFilters: ( + | object + | FeatureFlagTargetingClientFilter + | FeatureFlagTimeWindowClientFilter + | FeatureFlagPercentageClientFilter + )[] = [ + { + name: "Microsoft.TimeWindow", + parameters: { + start: "Wed, 01 May 2019 13:59:59 GMT", + end: "Mon, 01 July 2019 00:00:00 GMT" + // TODO: dates should accept `string | Date` + // Returning + // - SDK would return Date for all the config-settings if parsable + // - SDK would return strings for non parsable strings + // Passing as an argument + // - If passed in a string, we'll pass it as is. + // - If passed Date, we'll make it a string. + } + }, + { name: "FilterX" } + // TODO: add the rest two filters too + ]; + const baseSetting: FeatureFlag = { + conditions: { + clientFilters + }, + displayName: "I'mFeatFlag", + enabled: false, + isReadOnly: false, + key: `${featureFlagPrefix + generateUuid()}`, + contentType: featureFlagContentType + }; + + beforeEach(async () => { + await client.addConfigurationSetting(baseSetting); + }); + + afterEach(async () => { + await client.deleteConfigurationSetting({ + key: baseSetting.key + }); + }); + + it("can add and get FeatureFlag", async () => { + const getResponse = await client.getConfigurationSetting({ + key: baseSetting.key + }); + assert.equal(isFeatureFlag(getResponse), true, "Expected to get the feature flag"); + if (isFeatureFlag(getResponse)) { + assert.equal( + getResponse.key, + baseSetting.key, + "Key from the response from get request is not as expected" + ); + assert.equal( + getResponse.value, + baseSetting.value, + "value from the response from get request is not as expected" + ); + } + }); + + it("can add and update FeatureFlag", async () => { + const getResponse = await client.getConfigurationSetting({ + key: baseSetting.key + }); + assert.equal(isFeatureFlag(getResponse), true, "Should have been FeatureFlag"); + if (isFeatureFlag(getResponse)) { + // TODO: enabled returned was undefined, that's a bug? + assert.equal(!!getResponse.enabled, baseSetting.enabled, "Unexpected value for enabled"); + getResponse.enabled = true; + } + + await client.setConfigurationSetting(getResponse); + + const getResponseAfterUpdate = await client.getConfigurationSetting({ + key: baseSetting.key + }); + assert.equal(isFeatureFlag(getResponseAfterUpdate), true, "Expected to get the feature flag"); + if (isFeatureFlag(getResponseAfterUpdate)) { + assert.equal( + getResponseAfterUpdate.key, + baseSetting.key, + "Key from the response from get request is not as expected" + ); + assert.equal( + getResponseAfterUpdate.value, + baseSetting.value, + "value from the response from get request is not as expected" + ); + // More assertions - filters-count, displayName, description, filter name, enabled, isReadOnly + // TODO: enabled returned was undefined, that's a bug I guess + // assert.equal(getResponseAfterUpdate.enabled, !baseSetting.enabled, "Unexpected value for enabled"); + } + }); + + it("can add and update multiple FeatureFlags", async () => { + const secondSetting = { + ...baseSetting, + key: `${baseSetting.key}-2` + }; + await client.addConfigurationSetting(secondSetting); + + for await (const setting of client.listConfigurationSettings({ + keyFilter: `${baseSetting.key}*` + })) { + // TODO: check count before and after + assert.equal(isFeatureFlag(setting), true, "Should have been FeatureFlag"); + if (isFeatureFlag(setting)) { + // TODO: assert feat. description + } + } + await client.deleteConfigurationSetting({ key: secondSetting.key }); + }); + }); }); From 45a6d1868582813118805ecfdf728891c1323aea Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Mon, 29 Mar 2021 13:17:34 -0700 Subject: [PATCH 09/50] tests for SecretReference - left TODOs and ideas for future --- .../test/public/index.spec.ts | 100 +++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts index 3f5a9f392d6e..dcb14608f51e 100644 --- a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts @@ -21,7 +21,10 @@ import { featureFlagPrefix, FeatureFlagTargetingClientFilter, FeatureFlagTimeWindowClientFilter, - isFeatureFlag + isFeatureFlag, + isSecretReference, + SecretReference, + secretReferenceContentType } from "../../src"; import { delay, generateUuid } from "@azure/core-http"; import { Recorder } from "@azure/test-utils-recorder"; @@ -1269,4 +1272,99 @@ describe("AppConfigurationClient", () => { await client.deleteConfigurationSetting({ key: secondSetting.key }); }); }); + + describe("SecretReference configuration setting", () => { + const baseSetting: SecretReference = { + secretId: `secret-id-${generateUuid()}`, // TODO: It's a URL in .NET, why the difference? + isReadOnly: false, + key: generateUuid(), + contentType: secretReferenceContentType + }; + + beforeEach(async () => { + await client.addConfigurationSetting(baseSetting); + }); + + afterEach(async () => { + await client.deleteConfigurationSetting({ + key: baseSetting.key + }); + }); + + it("can add and get SecretReference", async () => { + const getResponse = await client.getConfigurationSetting({ + key: baseSetting.key + }); + assert.equal(isSecretReference(getResponse), true, "Expected to get the SecretReference"); + if (isSecretReference(getResponse)) { + assert.equal( + getResponse.key, + baseSetting.key, + "Key from the response from get request is not as expected" + ); + assert.equal( + getResponse.value, + baseSetting.value, + "value from the response from get request is not as expected" + ); + // TODO: assert with readonly and secretId + } + }); + + it("can add and update SecretReference", async () => { + const getResponse = await client.getConfigurationSetting({ + key: baseSetting.key + }); + assert.equal(isSecretReference(getResponse), true, "Should have been SecretReference"); + if (isSecretReference(getResponse)) { + console.log(getResponse.secretId); + // TODO: secretId returned was undefined, that's a bug? + assert.equal(getResponse.secretId, baseSetting.secretId, "Unexpected value for secretId"); + getResponse.secretId = `secret-id-${generateUuid()}`; + } + + await client.setConfigurationSetting(getResponse); + + const getResponseAfterUpdate = await client.getConfigurationSetting({ + key: baseSetting.key + }); + assert.equal( + isSecretReference(getResponseAfterUpdate), + true, + "Expected to get the SecretReference" + ); + if (isSecretReference(getResponseAfterUpdate)) { + assert.equal( + getResponseAfterUpdate.key, + baseSetting.key, + "Key from the response from get request is not as expected" + ); + assert.equal( + getResponseAfterUpdate.value, + baseSetting.value, + "value from the response from get request is not as expected" + ); + // TODO: assert with readonly and secretId + } + }); + + it("can add and update multiple SecretReferences", async () => { + const secondSetting = { + ...baseSetting, + key: `${baseSetting.key}-2` + }; + await client.addConfigurationSetting(secondSetting); + + for await (const setting of client.listConfigurationSettings({ + keyFilter: `${baseSetting.key}*` + })) { + // TODO: check count before and after + assert.equal(isSecretReference(setting), true, "Should have been FeatureFlag"); + if (isSecretReference(setting)) { + // TODO: assert with key, value, readonly and secretId + } + } + await client.deleteConfigurationSetting({ key: secondSetting.key }); + }); + }); }); From 7bb09f3f210f190d3e35b7bb80e5850acd98e60f Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Mon, 29 Mar 2021 17:46:52 -0700 Subject: [PATCH 10/50] update version --- sdk/appconfiguration/app-configuration/package.json | 2 +- .../app-configuration/src/appConfigurationClient.ts | 2 +- .../src/generated/src/appConfigurationContext.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/package.json b/sdk/appconfiguration/app-configuration/package.json index a2a74bd6712f..40151c57c37b 100644 --- a/sdk/appconfiguration/app-configuration/package.json +++ b/sdk/appconfiguration/app-configuration/package.json @@ -2,7 +2,7 @@ "name": "@azure/app-configuration", "author": "Microsoft Corporation", "description": "An isomorphic client library for the Azure App Configuration service.", - "version": "1.1.1", + "version": "1.2.0-beta.1", "sdk-type": "client", "keywords": [ "node", diff --git a/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts b/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts index b66392178f95..e6ff3b83abdb 100644 --- a/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts +++ b/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts @@ -67,7 +67,7 @@ const packageName = "azsdk-js-app-configuration"; * User - Agent header. There's a unit test that makes sure it always stays in sync. * @internal */ -export const packageVersion = "1.1.1"; +export const packageVersion = "1.2.0-beta.1"; const apiVersion = "1.0"; const ConnectionStringRegex = /Endpoint=(.*);Id=(.*);Secret=(.*)/; const deserializationContentTypes = { diff --git a/sdk/appconfiguration/app-configuration/src/generated/src/appConfigurationContext.ts b/sdk/appconfiguration/app-configuration/src/generated/src/appConfigurationContext.ts index 2dcac2c5c22d..aa81e115b834 100644 --- a/sdk/appconfiguration/app-configuration/src/generated/src/appConfigurationContext.ts +++ b/sdk/appconfiguration/app-configuration/src/generated/src/appConfigurationContext.ts @@ -10,7 +10,7 @@ import * as coreHttp from "@azure/core-http"; import { ApiVersion10, AppConfigurationOptionalParams } from "./models"; const packageName = "app-configuration"; -const packageVersion = "1.1.1"; +const packageVersion = "1.2.0-beta.1"; /** @internal */ export class AppConfigurationContext extends coreHttp.ServiceClient { From 6baf1e35ca23fec474a82ee975ef6f187e48dc13 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Tue, 30 Mar 2021 14:21:47 -0700 Subject: [PATCH 11/50] add docs for all --- .../app-configuration/src/featureFlag.ts | 75 +++++++++++++++++++ .../app-configuration/src/index.ts | 23 +++--- .../src/keyvaultReference.ts | 17 +++++ 3 files changed, 103 insertions(+), 12 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index 9f040e669ee8..d461d2281de6 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -19,8 +19,18 @@ export const featureFlagPrefix = ".appconfig.featureflag/"; */ export const featureFlagContentType = "application/vnd.microsoft.appconfig.ff+json;charset=utf-8"; +/** + * Necessary fields for updating or creating a new feature flag. + */ export interface FeatureFlagParam extends ConfigurationSettingParam { // TODO: can we hoist 'clientFilters' higher and avoid this sub-field? Need to talk to team. + /** + * A Feature filter consistently evaluates the state of a feature flag. + * Our feature management library supports three types of built-in filters: Targeting, TimeWindow, and Percentage. + * Custom filters can also be created based on different factors, such as device used, browser types, geographic location, etc. + * + * [More Info](https://docs.microsoft.com/en-us/azure/azure-app-configuration/howto-feature-filters-aspnet-core) + */ conditions: { clientFilters: ( | FeatureFlagTargetingClientFilter @@ -29,15 +39,40 @@ export interface FeatureFlagParam extends ConfigurationSettingParam { | object )[]; }; + /** + * Description of the feature. + */ description?: string; + /** + * Display name for the feature to use for display rather than the ID. + */ displayName: string; + /** + * Boolean flag to say if the feature flag is enabled. + */ enabled: boolean; } +/** + * FeatureFlag represents a configuration setting that stores a feature flag value. + * + * [More Info](https://docs.microsoft.com/azure/azure-app-configuration/concept-feature-management) + */ export interface FeatureFlag extends FeatureFlagParam, ConfigurationSetting {} +/** + * Targeting Client filter for the feature flag configuration setting. + * + * [More Info](https://docs.microsoft.com/en-us/azure/azure-app-configuration/howto-feature-filters-aspnet-core) + */ export interface FeatureFlagTargetingClientFilter { + /** + * The name of the feature filter. + */ name: "Microsoft.Targeting"; + /** + * Parameters that can be passed in the filter. + */ parameters: { // TODO: can we hoist these values up directly into the filter, rather than having this 'parameters' intermediary. audience: { @@ -53,8 +88,19 @@ export interface FeatureFlagTargetingClientFilter { }; } +/** + * Time window Client filter for the feature flag configuration setting. + * + * [More Info](https://docs.microsoft.com/en-us/azure/azure-app-configuration/howto-feature-filters-aspnet-core) + */ export interface FeatureFlagTimeWindowClientFilter { + /** + * The name of the feature filter. + */ name: "Microsoft.TimeWindow"; + /** + * Parameters that can be passed in the filter. + */ parameters: { start: string; end: string; @@ -62,29 +108,55 @@ export interface FeatureFlagTimeWindowClientFilter { }; } +/** + * Percentage Client filter for the feature flag configuration setting. + * + * [More Info](https://docs.microsoft.com/en-us/azure/azure-app-configuration/howto-feature-filters-aspnet-core) + */ export interface FeatureFlagPercentageClientFilter { + /** + * The name of the feature filter. + */ name: "Microsoft.Percentage"; + /** + * Parameters that can be passed in the filter. + */ parameters: { [key: string]: any; }; } +/** + * FeatureFlag represents a configuration setting that stores a feature flag value. + * [More Info](https://docs.microsoft.com/azure/azure-app-configuration/concept-feature-management) + * + * This helper method tells you if the given setting is a feature flag. + */ export function isFeatureFlag(setting: ConfigurationSetting | FeatureFlag): setting is FeatureFlag { return setting.contentType === featureFlagContentType; } +/** + * This helper method tells you if the given client filter has the name "Microsoft.Targeting". + */ function isFeatureFlagTargetingClientFilter( clientFilter: any ): clientFilter is FeatureFlagTargetingClientFilter { return clientFilter.name === "Microsoft.Targeting"; } +/** + * This helper method tells you if the given client filter has the name "Microsoft.TimeWindow". + */ function isFeatureFlagTimeWindowClientFilter( clientFilter: any ): clientFilter is FeatureFlagTimeWindowClientFilter { return clientFilter.name === "Microsoft.TimeWindow"; } +/** + * This helper method tells you if the given client filter has the name "Microsoft.Percentage". + */ function isFeatureFlagPercentageClientFilter( clientFilter: any ): clientFilter is FeatureFlagPercentageClientFilter { @@ -101,6 +173,9 @@ export type FeatureFlagType< ? FeatureFlagPercentageClientFilter : never; +/** + * This helper method tells you if the given client filter is a FeatureFlagClientFilter. + */ export function isFeatureFlagClientFilter( type: T, obj: any diff --git a/sdk/appconfiguration/app-configuration/src/index.ts b/sdk/appconfiguration/app-configuration/src/index.ts index 83a87ec33809..86044a0a51a2 100644 --- a/sdk/appconfiguration/app-configuration/src/index.ts +++ b/sdk/appconfiguration/app-configuration/src/index.ts @@ -1,24 +1,23 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -export * from "./models"; -export { AppConfigurationClientOptions, AppConfigurationClient } from "./appConfigurationClient"; - -export { - SecretReference, - SecretReferenceParam, - isSecretReference, - secretReferenceContentType -} from "./keyvaultReference"; - +export { AppConfigurationClient, AppConfigurationClientOptions } from "./appConfigurationClient"; export { FeatureFlag, + featureFlagContentType, FeatureFlagParam, FeatureFlagPercentageClientFilter, + featureFlagPrefix, FeatureFlagTargetingClientFilter, FeatureFlagTimeWindowClientFilter, - featureFlagContentType, - featureFlagPrefix, + FeatureFlagType, isFeatureFlag, isFeatureFlagClientFilter } from "./featureFlag"; +export { + isSecretReference, + SecretReference, + secretReferenceContentType, + SecretReferenceParam +} from "./keyvaultReference"; +export * from "./models"; diff --git a/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts index caa7b494810c..4390147644ec 100644 --- a/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts +++ b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts @@ -4,15 +4,32 @@ import { JsonKeyVaultReference } from "./internal/jsonModels"; import { ConfigurationSetting, ConfigurationSettingParam } from "./models"; +/** + * content-type for the secret reference. + */ export const secretReferenceContentType = "application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8"; +/** + * Necessary fields for updating or creating a new secret reference. + */ export interface SecretReferenceParam extends ConfigurationSettingParam { + /** + * Id for the secret reference. + */ secretId: string; } +/** + * SecretReference represents a configuration setting that references as KeyVault secret. + * + * Secret references have "application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8" content-type. + */ export interface SecretReference extends SecretReferenceParam, ConfigurationSetting {} +/** + * This helper method tells you if the given setting is a SecretReference configuration setting. + */ export function isSecretReference(setting: ConfigurationSetting): setting is SecretReference { return setting.contentType === secretReferenceContentType; } From 4ef8f49cc91603e2d517307136062966775f1ab7 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Tue, 30 Mar 2021 15:19:07 -0700 Subject: [PATCH 12/50] FeatureFlagType docs --- sdk/appconfiguration/app-configuration/src/featureFlag.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index d461d2281de6..d4be36b50bdc 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -163,6 +163,9 @@ function isFeatureFlagPercentageClientFilter( return clientFilter.name === "Microsoft.Percentage"; } +/** + * Type of FeatureFlag based on the client filters. + */ export type FeatureFlagType< T extends "targeting" | "timeWindow" | "percentage" > = T extends "targeting" From 0d9a096ab8c90f398b45fcfc49eb6b05aef99e28 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Tue, 30 Mar 2021 15:20:13 -0700 Subject: [PATCH 13/50] api report --- .../review/app-configuration.api.md | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md index ff8b0ead168f..b989ddc32fb0 100644 --- a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md +++ b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md @@ -74,32 +74,26 @@ export interface DeleteConfigurationSettingOptions extends HttpOnlyIfUnchangedFi export interface DeleteConfigurationSettingResponse extends SyncTokenHeaderField, HttpResponseFields, HttpResponseField { } -// @public (undocumented) +// @public export interface FeatureFlag extends FeatureFlagParam, ConfigurationSetting { } // @public export const featureFlagContentType = "application/vnd.microsoft.appconfig.ff+json;charset=utf-8"; -// @public (undocumented) +// @public export interface FeatureFlagParam extends ConfigurationSettingParam { - // (undocumented) conditions: { clientFilters: (FeatureFlagTargetingClientFilter | FeatureFlagTimeWindowClientFilter | FeatureFlagPercentageClientFilter | object)[]; }; - // (undocumented) description?: string; - // (undocumented) displayName: string; - // (undocumented) enabled: boolean; } -// @public (undocumented) +// @public export interface FeatureFlagPercentageClientFilter { - // (undocumented) name: "Microsoft.Percentage"; - // (undocumented) parameters: { [key: string]: any; }; @@ -108,11 +102,9 @@ export interface FeatureFlagPercentageClientFilter { // @public export const featureFlagPrefix = ".appconfig.featureflag/"; -// @public (undocumented) +// @public export interface FeatureFlagTargetingClientFilter { - // (undocumented) name: "Microsoft.Targeting"; - // (undocumented) parameters: { audience: { users: string[]; @@ -125,17 +117,18 @@ export interface FeatureFlagTargetingClientFilter { }; } -// @public (undocumented) +// @public export interface FeatureFlagTimeWindowClientFilter { - // (undocumented) name: "Microsoft.TimeWindow"; - // (undocumented) parameters: { start: string; end: string; }; } +// @public +export type FeatureFlagType = T extends "targeting" ? FeatureFlagTargetingClientFilter : T extends "timeWindow" ? FeatureFlagTimeWindowClientFilter : T extends "percentage" ? FeatureFlagPercentageClientFilter : never; + // @public export interface GetConfigurationHeaders extends SyncTokenHeaderField { } @@ -172,15 +165,13 @@ export interface HttpResponseFields { statusCode: number; } -// @public (undocumented) +// @public export function isFeatureFlag(setting: ConfigurationSetting | FeatureFlag): setting is FeatureFlag; -// Warning: (ae-forgotten-export) The symbol "FeatureFlagType" needs to be exported by the entry point index.d.ts -// -// @public (undocumented) +// @public export function isFeatureFlagClientFilter(type: T, obj: any): obj is FeatureFlagType; -// @public (undocumented) +// @public export function isSecretReference(setting: ConfigurationSetting): setting is SecretReference; // @public @@ -213,16 +204,15 @@ export interface OptionalFields { fields?: (keyof ConfigurationSetting)[]; } -// @public (undocumented) +// @public export interface SecretReference extends SecretReferenceParam, ConfigurationSetting { } -// @public (undocumented) +// @public export const secretReferenceContentType = "application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8"; -// @public (undocumented) +// @public export interface SecretReferenceParam extends ConfigurationSettingParam { - // (undocumented) secretId: string; } From 9122ddeb32862aacebda9643093cc42b58855980 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Tue, 30 Mar 2021 15:21:14 -0700 Subject: [PATCH 14/50] changelog --- sdk/appconfiguration/app-configuration/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/appconfiguration/app-configuration/CHANGELOG.md b/sdk/appconfiguration/app-configuration/CHANGELOG.md index 6bceec6c6822..0caa15ef1657 100644 --- a/sdk/appconfiguration/app-configuration/CHANGELOG.md +++ b/sdk/appconfiguration/app-configuration/CHANGELOG.md @@ -2,6 +2,8 @@ ## 1.2.0-beta.1 (Unreleased) +### New Features + - New `SecretReferenceConfigurationSetting` and `FeatureFlagConfigurationSetting`types to represent configuration settings that references KeyVault Secret reference and feature flag respectively. [#14342](https://github.com/Azure/azure-sdk-for-js/pull/14342) - Added `updateSyncToken` method to `AppConfigurationClient` to be able to provide external synchronization tokens. From 943de3d11aeb58cbb2263d94fecfd302663ea621 Mon Sep 17 00:00:00 2001 From: Harsha Nalluru Date: Tue, 30 Mar 2021 17:02:17 -0700 Subject: [PATCH 15/50] keeping the same as the JSON in portal --- sdk/appconfiguration/app-configuration/src/featureFlag.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index d4be36b50bdc..3a15da9ef829 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -23,7 +23,6 @@ export const featureFlagContentType = "application/vnd.microsoft.appconfig.ff+js * Necessary fields for updating or creating a new feature flag. */ export interface FeatureFlagParam extends ConfigurationSettingParam { - // TODO: can we hoist 'clientFilters' higher and avoid this sub-field? Need to talk to team. /** * A Feature filter consistently evaluates the state of a feature flag. * Our feature management library supports three types of built-in filters: Targeting, TimeWindow, and Percentage. From 747b58a16ed8d5eb41c9750bef7f9ab3ce8dd8dc Mon Sep 17 00:00:00 2001 From: Harsha Nalluru Date: Tue, 30 Mar 2021 17:03:24 -0700 Subject: [PATCH 16/50] keeping the JSON similar to that of the portal --- sdk/appconfiguration/app-configuration/src/featureFlag.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index 3a15da9ef829..4766ab6d6728 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -73,7 +73,6 @@ export interface FeatureFlagTargetingClientFilter { * Parameters that can be passed in the filter. */ parameters: { - // TODO: can we hoist these values up directly into the filter, rather than having this 'parameters' intermediary. audience: { users: string[]; groups: { From cf3c6682a2ec4f459c627c01908126dc9b0701a8 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Tue, 30 Mar 2021 18:00:39 -0700 Subject: [PATCH 17/50] fix percentage filter --- sdk/appconfiguration/app-configuration/src/featureFlag.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index 4766ab6d6728..baa9de6985fa 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -118,9 +118,11 @@ export interface FeatureFlagPercentageClientFilter { name: "Microsoft.Percentage"; /** * Parameters that can be passed in the filter. + * Value is expected to be from 0 to 100. SDK doesn't validate the value. */ parameters: { - [key: string]: any; + value: number; + // [key: string]: any - portal shows more inputs, but throws an error if provided more. }; } From 9cb5523e6a861db67e4cfc0337aa7669c8a95667 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Tue, 30 Mar 2021 18:00:52 -0700 Subject: [PATCH 18/50] add other two filters in the test --- .../app-configuration/test/public/index.spec.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts index dcb14608f51e..9761e5813663 100644 --- a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts @@ -1176,8 +1176,20 @@ describe("AppConfigurationClient", () => { // - If passed Date, we'll make it a string. } }, - { name: "FilterX" } - // TODO: add the rest two filters too + { name: "FilterX" }, + { + name: "Microsoft.Targeting", + parameters: { + audience: { + groups: [ + { name: "group-1", rolloutPercentage: 25 }, + { name: "group-2", rolloutPercentage: 45 } + ], + users: ["userA", "userB"] + } + } + }, + { name: "Microsoft.Percentage", parameters: { value: 25 } } ]; const baseSetting: FeatureFlag = { conditions: { From a0fd429b7bec7896d3cfb84b07c48909f3e151c5 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Tue, 30 Mar 2021 18:57:57 -0700 Subject: [PATCH 19/50] convertJsonConditions - FeatureFlagPercentageClientFilter - value --- .../app-configuration/src/featureFlag.ts | 11 ++++++++--- .../app-configuration/src/internal/jsonModels.ts | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index baa9de6985fa..19aeffc46e0c 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -260,9 +260,14 @@ export function convertJsonConditions( return filter; } else if (isJsonFeatureFlagPercentageClientFilter(jsonFilter)) { - // TODO: this is only working because I left the filter so unspecified in the type. Need to do - // a little more research into what that filter's actual type is. - return jsonFilter; + // try not to slice any unknown attributes + const filter: FeatureFlagPercentageClientFilter = { + name: jsonFilter.name, + parameters: { + value: jsonFilter.parameters.Value + } + }; + return filter; } else { return jsonFilter as object; } diff --git a/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts index e9f0cfc96c9c..99f145418a6c 100644 --- a/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts +++ b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts @@ -57,6 +57,7 @@ export interface JsonFeatureFlagTimeWindowClientFilter { export interface JsonFeatureFlagPercentageClientFilter { name: "Microsoft.Percentage"; parameters: { + Value: number; [key: string]: any; }; } From d14525a6dd330464299286c497cf57117bf1e9b7 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Tue, 30 Mar 2021 19:30:32 -0700 Subject: [PATCH 20/50] starter code for serializer --- .../app-configuration/src/internal/helpers.ts | 32 +++++++++++++++++-- .../src/keyvaultReference.ts | 4 +++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/src/internal/helpers.ts b/sdk/appconfiguration/app-configuration/src/internal/helpers.ts index a0f115fb3f8e..fa8faf7612e4 100644 --- a/sdk/appconfiguration/app-configuration/src/internal/helpers.ts +++ b/sdk/appconfiguration/app-configuration/src/internal/helpers.ts @@ -13,8 +13,18 @@ import { HttpOnlyIfUnchangedField } from "../models"; import { AppConfigurationGetKeyValuesOptionalParams, KeyValue } from "../generated/src/models"; -import { deserializeFeatureFlag, featureFlagContentType } from "../featureFlag"; -import { deserializeSecretReference, secretReferenceContentType } from "../keyvaultReference"; +import { + deserializeFeatureFlag, + FeatureFlag, + featureFlagContentType, + serializeFeatureFlag +} from "../featureFlag"; +import { + deserializeSecretReference, + SecretReference, + secretReferenceContentType, + serializeSecretReference +} from "../keyvaultReference"; /** * Formats the etag so it can be used with a If-Match/If-None-Match header @@ -171,6 +181,24 @@ export function transformKeyValue(kvp: KeyValue): ConfigurationSetting { } } +/** + * @internal + */ +export function serializeAsConfigurationSetting( + setting: FeatureFlag | SecretReference +): ConfigurationSetting { + switch (setting.contentType) { + case featureFlagContentType: { + return serializeFeatureFlag(setting as FeatureFlag); + } + case secretReferenceContentType: { + return serializeSecretReference(setting as SecretReference); + } + default: + return setting; + } +} + /** * @internal */ diff --git a/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts index 4390147644ec..6bd5732a2d55 100644 --- a/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts +++ b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts @@ -56,3 +56,7 @@ export function deserializeSecretReference( return undefined; } } + +export function serializeSecretReference(_setting: SecretReference): ConfigurationSetting { + throw new Error("Not implemented"); +} From b7ddf205df8726b388374ee6309545294c9da077 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Wed, 31 Mar 2021 10:29:24 -0700 Subject: [PATCH 21/50] feature flag serialization src and test and fixing FeatureFlagPercentageClientFilter --- .../review/app-configuration.api.md | 2 +- .../src/appConfigurationClient.ts | 8 +- .../app-configuration/src/featureFlag.ts | 78 ++++++++++++++++++- .../app-configuration/src/internal/helpers.ts | 19 +++-- .../src/internal/jsonModels.ts | 2 +- .../src/keyvaultReference.ts | 2 +- .../app-configuration/src/models.ts | 2 +- .../test/public/index.spec.ts | 11 +-- 8 files changed, 102 insertions(+), 22 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md index b989ddc32fb0..f83f5ebbc2b5 100644 --- a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md +++ b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md @@ -95,7 +95,7 @@ export interface FeatureFlagParam extends ConfigurationSettingParam { export interface FeatureFlagPercentageClientFilter { name: "Microsoft.Percentage"; parameters: { - [key: string]: any; + value: number; }; } diff --git a/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts b/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts index e6ff3b83abdb..41c297625549 100644 --- a/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts +++ b/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts @@ -50,7 +50,8 @@ import { transformKeyValueResponseWithStatusCode, transformKeyValue, formatAcceptDateTime, - formatFieldsForSelect + formatFieldsForSelect, + serializeAsConfigurationSettingParam } from "./internal/helpers"; import { tracingPolicy } from "@azure/core-http"; import { trace as traceFromTracingHelpers } from "./internal/tracingHelpers"; @@ -191,10 +192,13 @@ export class AppConfigurationClient { options: AddConfigurationSettingOptions = {} ): Promise { return this._trace("addConfigurationSetting", options, async (newOptions) => { + const internalConfigurationSetting = serializeAsConfigurationSettingParam( + configurationSetting + ); const originalResponse = await this.client.putKeyValue(configurationSetting.key, { ifNoneMatch: "*", label: configurationSetting.label, - entity: configurationSetting, + entity: internalConfigurationSetting, ...newOptions }); diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index 19aeffc46e0c..1af1df9ed6f5 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -5,7 +5,10 @@ import { isJsonFeatureFlagPercentageClientFilter, isJsonFeatureFlagTargetingClientFilter, isJsonFeatureFlagTimeWindowClientFilter, - JsonFeatureFlag + JsonFeatureFlag, + JsonFeatureFlagPercentageClientFilter, + JsonFeatureFlagTargetingClientFilter, + JsonFeatureFlagTimeWindowClientFilter } from "./internal/jsonModels"; import { ConfigurationSetting, ConfigurationSettingParam } from "./models"; @@ -219,8 +222,22 @@ export function deserializeFeatureFlag(setting: ConfigurationSetting): FeatureFl return setting; } -export function serializeFeatureFlag(_setting: FeatureFlag): ConfigurationSetting { - throw new Error("Not implemented"); +export function serializeFeatureFlagParam(setting: FeatureFlagParam): ConfigurationSettingParam { + const value: JsonFeatureFlag & { id: string } = { + id: setting.key.replace(featureFlagPrefix, ""), + description: setting.description, + enabled: setting.enabled, + conditions: convertToJsonConditions(setting.conditions) + }; + const configurationSetting: ConfigurationSettingParam = { + key: setting.key, + label: setting.label, + contentType: setting.contentType, + etag: setting.contentType, + tags: setting.tags, + value: JSON.stringify(value) + }; + return configurationSetting; } /** @@ -277,3 +294,58 @@ export function convertJsonConditions( clientFilters }; } + +/** + * @internal + */ +export function convertToJsonConditions( + conditions: FeatureFlag["conditions"] +): JsonFeatureFlag["conditions"] { + const client_filters = conditions.clientFilters.map((filter) => { + if (isFeatureFlagTargetingClientFilter(filter)) { + // try not to slice any unknown attributes + const jsonFilter: JsonFeatureFlagTargetingClientFilter = { + name: filter.name, + parameters: { + Audience: { + Groups: + filter.parameters.audience?.groups.map((grp) => ({ + Name: grp.name, + RolloutPercentage: grp.rolloutPercentage + })) || [], + Users: filter.parameters.audience?.users || [] + }, + DefaultRolloutPercentage: filter.parameters.defaultRolloutPercentage + } + }; + + return jsonFilter; + } else if (isFeatureFlagTimeWindowClientFilter(filter)) { + // try not to slice any unknown attributes + const jsonFilter: JsonFeatureFlagTimeWindowClientFilter = { + name: filter.name, + parameters: { + Start: filter.parameters.start, + End: filter.parameters.end + } + }; + + return jsonFilter; + } else if (isJsonFeatureFlagPercentageClientFilter(filter)) { + // try not to slice any unknown attributes + const jsonFilter: JsonFeatureFlagPercentageClientFilter = { + name: filter.name, + parameters: { + Value: filter.parameters.value + } + }; + return jsonFilter; + } else { + return filter as object; + } + }); + + return { + client_filters + }; +} diff --git a/sdk/appconfiguration/app-configuration/src/internal/helpers.ts b/sdk/appconfiguration/app-configuration/src/internal/helpers.ts index fa8faf7612e4..7a79c18a461c 100644 --- a/sdk/appconfiguration/app-configuration/src/internal/helpers.ts +++ b/sdk/appconfiguration/app-configuration/src/internal/helpers.ts @@ -10,20 +10,23 @@ import { HttpResponseField, HttpResponseFields, HttpOnlyIfChangedField, - HttpOnlyIfUnchangedField + HttpOnlyIfUnchangedField, + ConfigurationSettingParam } from "../models"; import { AppConfigurationGetKeyValuesOptionalParams, KeyValue } from "../generated/src/models"; import { deserializeFeatureFlag, FeatureFlag, featureFlagContentType, - serializeFeatureFlag + FeatureFlagParam, + serializeFeatureFlagParam } from "../featureFlag"; import { deserializeSecretReference, SecretReference, secretReferenceContentType, - serializeSecretReference + SecretReferenceParam, + serializeSecretReferenceParam } from "../keyvaultReference"; /** @@ -184,15 +187,15 @@ export function transformKeyValue(kvp: KeyValue): ConfigurationSetting { /** * @internal */ -export function serializeAsConfigurationSetting( - setting: FeatureFlag | SecretReference -): ConfigurationSetting { +export function serializeAsConfigurationSettingParam( + setting: FeatureFlagParam | SecretReferenceParam | ConfigurationSettingParam +): ConfigurationSettingParam { switch (setting.contentType) { case featureFlagContentType: { - return serializeFeatureFlag(setting as FeatureFlag); + return serializeFeatureFlagParam(setting as FeatureFlag); } case secretReferenceContentType: { - return serializeSecretReference(setting as SecretReference); + return serializeSecretReferenceParam(setting as SecretReference); } default: return setting; diff --git a/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts index 99f145418a6c..10e976d6618a 100644 --- a/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts +++ b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts @@ -18,7 +18,7 @@ export type JsonFeatureFlag = { )[]; }; description?: string; - enabled: true; + enabled: boolean; }; /** diff --git a/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts index 6bd5732a2d55..2b8904d2c216 100644 --- a/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts +++ b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts @@ -57,6 +57,6 @@ export function deserializeSecretReference( } } -export function serializeSecretReference(_setting: SecretReference): ConfigurationSetting { +export function serializeSecretReferenceParam(_setting: SecretReference): ConfigurationSetting { throw new Error("Not implemented"); } diff --git a/sdk/appconfiguration/app-configuration/src/models.ts b/sdk/appconfiguration/app-configuration/src/models.ts index b016e92ac577..b374c40230e9 100644 --- a/sdk/appconfiguration/app-configuration/src/models.ts +++ b/sdk/appconfiguration/app-configuration/src/models.ts @@ -37,7 +37,7 @@ export interface ConfigurationSettingParam extends ConfigurationSettingId { /** * The setting's value */ - value?: string; + value?: string; // Value is not present for FeatureFlag /** * Tags for this key diff --git a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts index 9761e5813663..4436c0054349 100644 --- a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts @@ -1186,7 +1186,8 @@ describe("AppConfigurationClient", () => { { name: "group-2", rolloutPercentage: 45 } ], users: ["userA", "userB"] - } + }, + defaultRolloutPercentage: 40 } }, { name: "Microsoft.Percentage", parameters: { value: 25 } } @@ -1223,10 +1224,10 @@ describe("AppConfigurationClient", () => { baseSetting.key, "Key from the response from get request is not as expected" ); - assert.equal( - getResponse.value, - baseSetting.value, - "value from the response from get request is not as expected" + assert.deepEqual( + getResponse.conditions, + baseSetting.conditions, + "conditions from the response from get request is not as expected" ); } }); From e3b9083ba302c306190aca8c5a6c6ef822fa5a6a Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Wed, 31 Mar 2021 15:01:09 -0700 Subject: [PATCH 22/50] update changelog --- sdk/appconfiguration/app-configuration/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/appconfiguration/app-configuration/CHANGELOG.md b/sdk/appconfiguration/app-configuration/CHANGELOG.md index 0caa15ef1657..bbac8d015fbf 100644 --- a/sdk/appconfiguration/app-configuration/CHANGELOG.md +++ b/sdk/appconfiguration/app-configuration/CHANGELOG.md @@ -1,6 +1,6 @@ # Release History -## 1.2.0-beta.1 (Unreleased) +## 1.2.0-beta.1 (2021-04-06) ### New Features From 0f782ec7b40fad1714b9e737151637e958f311eb Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Wed, 31 Mar 2021 17:05:45 -0700 Subject: [PATCH 23/50] fix DefaultRolloutPercentage placement --- .../app-configuration/src/featureFlag.ts | 15 +++++++-------- .../app-configuration/src/internal/jsonModels.ts | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index 1af1df9ed6f5..d49ce60d41f9 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -82,9 +82,8 @@ export interface FeatureFlagTargetingClientFilter { name: string; rolloutPercentage: number; }[]; + defaultRolloutPercentage: number; }; - defaultRolloutPercentage: number; - // [key: string]: any; }; } @@ -258,9 +257,9 @@ export function convertJsonConditions( name: grp.Name, rolloutPercentage: grp.RolloutPercentage })) || [], - users: jsonFilter.parameters.Audience?.Users || [] - }, - defaultRolloutPercentage: jsonFilter.parameters.DefaultRolloutPercentage + users: jsonFilter.parameters.Audience?.Users || [], + defaultRolloutPercentage: jsonFilter.parameters.DefaultRolloutPercentage + } } }; @@ -313,9 +312,9 @@ export function convertToJsonConditions( Name: grp.name, RolloutPercentage: grp.rolloutPercentage })) || [], - Users: filter.parameters.audience?.users || [] - }, - DefaultRolloutPercentage: filter.parameters.defaultRolloutPercentage + Users: filter.parameters.audience?.users || [], + DefaultRolloutPercentage: filter.parameters.audience.defaultRolloutPercentage + } } }; diff --git a/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts index 10e976d6618a..944c8056d111 100644 --- a/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts +++ b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts @@ -33,8 +33,8 @@ export interface JsonFeatureFlagTargetingClientFilter { Name: string; RolloutPercentage: number; }[]; + DefaultRolloutPercentage: number; }; - DefaultRolloutPercentage: number; [key: string]: any; }; } From a95264ea6043195b4cb5a45f2bfe32f5305e5f61 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Wed, 31 Mar 2021 17:23:35 -0700 Subject: [PATCH 24/50] API report --- .../app-configuration/review/app-configuration.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md index f83f5ebbc2b5..d19fa632cc05 100644 --- a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md +++ b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md @@ -112,8 +112,8 @@ export interface FeatureFlagTargetingClientFilter { name: string; rolloutPercentage: number; }[]; + defaultRolloutPercentage: number; }; - defaultRolloutPercentage: number; }; } From 9d3b0d845439aa60eda5b2de01402b8a94482dc6 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Wed, 31 Mar 2021 17:25:36 -0700 Subject: [PATCH 25/50] remove displayName and fix models --- .../app-configuration/src/featureFlag.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index d49ce60d41f9..16b4ec1daa5b 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -45,10 +45,6 @@ export interface FeatureFlagParam extends ConfigurationSettingParam { * Description of the feature. */ description?: string; - /** - * Display name for the feature to use for display rather than the ID. - */ - displayName: string; /** * Boolean flag to say if the feature flag is enabled. */ @@ -253,12 +249,12 @@ export function convertJsonConditions( parameters: { audience: { groups: - jsonFilter.parameters.Audience?.Groups.map((grp) => ({ + jsonFilter.parameters.Audience.Groups.map((grp) => ({ name: grp.Name, rolloutPercentage: grp.RolloutPercentage })) || [], - users: jsonFilter.parameters.Audience?.Users || [], - defaultRolloutPercentage: jsonFilter.parameters.DefaultRolloutPercentage + users: jsonFilter.parameters.Audience.Users || [], + defaultRolloutPercentage: jsonFilter.parameters.Audience.DefaultRolloutPercentage } } }; From 211dd9481a56afb918c642b93cf5a4e423d1594d Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Wed, 31 Mar 2021 17:25:56 -0700 Subject: [PATCH 26/50] Audience - required --- .../app-configuration/src/internal/jsonModels.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts index 944c8056d111..caad8829bae1 100644 --- a/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts +++ b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts @@ -27,7 +27,7 @@ export type JsonFeatureFlag = { export interface JsonFeatureFlagTargetingClientFilter { name: "Microsoft.Targeting"; parameters: { - Audience?: { + Audience: { Users: string[]; Groups: { Name: string; From 8db283604b29fc5df3d485bf779f4ea34ea3774d Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Wed, 31 Mar 2021 17:39:25 -0700 Subject: [PATCH 27/50] Proper test for fetaure flag --- .../test/public/index.spec.ts | 56 +++++++++++++------ 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts index 4436c0054349..4a4d14395ea2 100644 --- a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts @@ -12,6 +12,7 @@ import { startRecorder } from "./utils/testHelpers"; import { + AddConfigurationSettingResponse, AppConfigurationClient, ConfigurationSetting, ConfigurationSettingParam, @@ -1185,50 +1186,69 @@ describe("AppConfigurationClient", () => { { name: "group-1", rolloutPercentage: 25 }, { name: "group-2", rolloutPercentage: 45 } ], - users: ["userA", "userB"] - }, - defaultRolloutPercentage: 40 + users: ["userA", "userB"], + defaultRolloutPercentage: 40 + } } }, { name: "Microsoft.Percentage", parameters: { value: 25 } } ]; + const baseSetting: FeatureFlag = { conditions: { clientFilters }, - displayName: "I'mFeatFlag", enabled: false, isReadOnly: false, key: `${featureFlagPrefix + generateUuid()}`, - contentType: featureFlagContentType + contentType: featureFlagContentType, + description: "I'm a description", + label: "label-1" }; + let addResponse: AddConfigurationSettingResponse; + beforeEach(async () => { - await client.addConfigurationSetting(baseSetting); + addResponse = await client.addConfigurationSetting(baseSetting); }); afterEach(async () => { await client.deleteConfigurationSetting({ - key: baseSetting.key + key: baseSetting.key, + label: baseSetting.label }); }); + function assertFeatureFlagProps(actual: FeatureFlag, expected: FeatureFlag) { + assert.equal( + actual.key, + expected.key, + "Key from the response from get request is not as expected" + ); + assert.deepEqual( + actual.conditions, + expected.conditions, + "conditions from the response from get request is not as expected" + ); + assert.equal(actual.description, expected.description); + assert.equal(actual.enabled, expected.enabled); + assert.equal(actual.isReadOnly, expected.isReadOnly); + assert.equal(actual.label, expected.label); + assert.equal(actual.contentType, expected.contentType); + } + it("can add and get FeatureFlag", async () => { + assert.equal(isFeatureFlag(addResponse), true, "Expected to get the feature flag"); + if (isFeatureFlag(addResponse)) { + assertFeatureFlagProps(addResponse, baseSetting); + } const getResponse = await client.getConfigurationSetting({ - key: baseSetting.key + key: baseSetting.key, + label: baseSetting.label }); assert.equal(isFeatureFlag(getResponse), true, "Expected to get the feature flag"); if (isFeatureFlag(getResponse)) { - assert.equal( - getResponse.key, - baseSetting.key, - "Key from the response from get request is not as expected" - ); - assert.deepEqual( - getResponse.conditions, - baseSetting.conditions, - "conditions from the response from get request is not as expected" - ); + assertFeatureFlagProps(getResponse, baseSetting); } }); From 570fe41058708bc34815dba4114bf0ae76184c53 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Wed, 31 Mar 2021 18:14:25 -0700 Subject: [PATCH 28/50] update test done along with bug fixes --- .../src/appConfigurationClient.ts | 5 +- .../app-configuration/src/featureFlag.ts | 2 +- .../test/public/index.spec.ts | 86 +++++++++---------- 3 files changed, 44 insertions(+), 49 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts b/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts index 41c297625549..57a238d64db7 100644 --- a/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts +++ b/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts @@ -461,10 +461,13 @@ export class AppConfigurationClient { options: SetConfigurationSettingOptions = {} ): Promise { return this._trace("setConfigurationSetting", options, async (newOptions) => { + const internalConfigurationSetting = serializeAsConfigurationSettingParam( + configurationSetting + ); const response = await this.client.putKeyValue(configurationSetting.key, { ...newOptions, label: configurationSetting.label, - entity: configurationSetting, + entity: internalConfigurationSetting, ...checkAndFormatIfAndIfNoneMatch(configurationSetting, options) }); diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index 16b4ec1daa5b..6e03cadcb9b5 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -228,7 +228,7 @@ export function serializeFeatureFlagParam(setting: FeatureFlagParam): Configurat key: setting.key, label: setting.label, contentType: setting.contentType, - etag: setting.contentType, + etag: setting.etag, tags: setting.tags, value: JSON.stringify(value) }; diff --git a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts index 4a4d14395ea2..d806be782009 100644 --- a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts @@ -1219,71 +1219,63 @@ describe("AppConfigurationClient", () => { }); }); - function assertFeatureFlagProps(actual: FeatureFlag, expected: FeatureFlag) { - assert.equal( - actual.key, - expected.key, - "Key from the response from get request is not as expected" - ); - assert.deepEqual( - actual.conditions, - expected.conditions, - "conditions from the response from get request is not as expected" - ); - assert.equal(actual.description, expected.description); - assert.equal(actual.enabled, expected.enabled); - assert.equal(actual.isReadOnly, expected.isReadOnly); - assert.equal(actual.label, expected.label); - assert.equal(actual.contentType, expected.contentType); + function assertFeatureFlagProps( + actual: AddConfigurationSettingResponse, + expected: FeatureFlag + ) { + assert.equal(isFeatureFlag(actual), true, "Expected to get the feature flag"); + if (isFeatureFlag(actual)) { + assert.equal( + actual.key, + expected.key, + "Key from the response from get request is not as expected" + ); + assert.deepEqual( + actual.conditions, + expected.conditions, + "conditions from the response from get request is not as expected" + ); + assert.equal(actual.description, expected.description); + assert.equal(actual.enabled, expected.enabled); + assert.equal(actual.isReadOnly, expected.isReadOnly); + assert.equal(actual.label, expected.label); + assert.equal(actual.contentType, expected.contentType); + } } it("can add and get FeatureFlag", async () => { - assert.equal(isFeatureFlag(addResponse), true, "Expected to get the feature flag"); - if (isFeatureFlag(addResponse)) { - assertFeatureFlagProps(addResponse, baseSetting); - } + assertFeatureFlagProps(addResponse, baseSetting); const getResponse = await client.getConfigurationSetting({ key: baseSetting.key, label: baseSetting.label }); - assert.equal(isFeatureFlag(getResponse), true, "Expected to get the feature flag"); - if (isFeatureFlag(getResponse)) { - assertFeatureFlagProps(getResponse, baseSetting); - } + assertFeatureFlagProps(getResponse, baseSetting); }); it("can add and update FeatureFlag", async () => { const getResponse = await client.getConfigurationSetting({ - key: baseSetting.key + key: baseSetting.key, + label: baseSetting.label }); - assert.equal(isFeatureFlag(getResponse), true, "Should have been FeatureFlag"); + assertFeatureFlagProps(getResponse, baseSetting); if (isFeatureFlag(getResponse)) { - // TODO: enabled returned was undefined, that's a bug? - assert.equal(!!getResponse.enabled, baseSetting.enabled, "Unexpected value for enabled"); - getResponse.enabled = true; + getResponse.enabled = !baseSetting.enabled; } - await client.setConfigurationSetting(getResponse); + const setResponse = await client.setConfigurationSetting(getResponse); + assertFeatureFlagProps(setResponse, { + ...baseSetting, + enabled: !baseSetting.enabled + }); const getResponseAfterUpdate = await client.getConfigurationSetting({ - key: baseSetting.key + key: baseSetting.key, + label: baseSetting.label + }); + assertFeatureFlagProps(getResponseAfterUpdate, { + ...baseSetting, + enabled: !baseSetting.enabled }); - assert.equal(isFeatureFlag(getResponseAfterUpdate), true, "Expected to get the feature flag"); - if (isFeatureFlag(getResponseAfterUpdate)) { - assert.equal( - getResponseAfterUpdate.key, - baseSetting.key, - "Key from the response from get request is not as expected" - ); - assert.equal( - getResponseAfterUpdate.value, - baseSetting.value, - "value from the response from get request is not as expected" - ); - // More assertions - filters-count, displayName, description, filter name, enabled, isReadOnly - // TODO: enabled returned was undefined, that's a bug I guess - // assert.equal(getResponseAfterUpdate.enabled, !baseSetting.enabled, "Unexpected value for enabled"); - } }); it("can add and update multiple FeatureFlags", async () => { From 99688d7801b21a0e5b4f92b8ce88ef236a074557 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Wed, 31 Mar 2021 18:28:49 -0700 Subject: [PATCH 29/50] final feature flag test --- .../test/public/index.spec.ts | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts index d806be782009..6af5fabe8a62 100644 --- a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts @@ -1220,7 +1220,7 @@ describe("AppConfigurationClient", () => { }); function assertFeatureFlagProps( - actual: AddConfigurationSettingResponse, + actual: Omit, expected: FeatureFlag ) { assert.equal(isFeatureFlag(actual), true, "Expected to get the feature flag"); @@ -1278,22 +1278,50 @@ describe("AppConfigurationClient", () => { }); }); - it("can add and update multiple FeatureFlags", async () => { + it("can add, list and update multiple FeatureFlags", async () => { const secondSetting = { ...baseSetting, key: `${baseSetting.key}-2` }; await client.addConfigurationSetting(secondSetting); + let numberOFFeatureFlagsReceived = 0; for await (const setting of client.listConfigurationSettings({ keyFilter: `${baseSetting.key}*` })) { - // TODO: check count before and after - assert.equal(isFeatureFlag(setting), true, "Should have been FeatureFlag"); - if (isFeatureFlag(setting)) { - // TODO: assert feat. description + numberOFFeatureFlagsReceived++; + if (setting.key === baseSetting.key) { + assertFeatureFlagProps(setting, baseSetting); + await client.setConfigurationSetting({ + ...baseSetting, + enabled: !baseSetting.enabled + } as FeatureFlag); + } else { + assertFeatureFlagProps(setting, secondSetting); + await client.setConfigurationSetting({ + ...setting, + description: "I'm new description" + } as FeatureFlag); + } + } + assert.equal(numberOFFeatureFlagsReceived, 2, "Unexpected number of FeatureFlags seen"); + + for await (const setting of client.listConfigurationSettings({ + keyFilter: `${baseSetting.key}*` + })) { + numberOFFeatureFlagsReceived--; + if (setting.key === baseSetting.key) { + assertFeatureFlagProps(setting, { ...baseSetting, enabled: !baseSetting.enabled }); + } else { + assertFeatureFlagProps(setting, { ...secondSetting, description: "I'm new description" }); } } + + assert.equal( + numberOFFeatureFlagsReceived, + 0, + "Unexpected number of FeatureFlags seen after updating" + ); await client.deleteConfigurationSetting({ key: secondSetting.key }); }); }); From ce61d455feb6754dd37f9851724634a0dda228d4 Mon Sep 17 00:00:00 2001 From: Harsha Nalluru Date: Wed, 31 Mar 2021 18:29:57 -0700 Subject: [PATCH 30/50] Update sdk/appconfiguration/app-configuration/src/models.ts --- sdk/appconfiguration/app-configuration/src/models.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/appconfiguration/app-configuration/src/models.ts b/sdk/appconfiguration/app-configuration/src/models.ts index b374c40230e9..b016e92ac577 100644 --- a/sdk/appconfiguration/app-configuration/src/models.ts +++ b/sdk/appconfiguration/app-configuration/src/models.ts @@ -37,7 +37,7 @@ export interface ConfigurationSettingParam extends ConfigurationSettingId { /** * The setting's value */ - value?: string; // Value is not present for FeatureFlag + value?: string; /** * Tags for this key From 0a72f509106260ddf5f56eed652a625ee48be246 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Thu, 1 Apr 2021 02:43:47 -0700 Subject: [PATCH 31/50] serializeSecretReferenceParam --- .../app-configuration/src/keyvaultReference.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts index 2b8904d2c216..cf7c7faabf37 100644 --- a/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts +++ b/sdk/appconfiguration/app-configuration/src/keyvaultReference.ts @@ -57,6 +57,16 @@ export function deserializeSecretReference( } } -export function serializeSecretReferenceParam(_setting: SecretReference): ConfigurationSetting { - throw new Error("Not implemented"); +export function serializeSecretReferenceParam( + setting: SecretReferenceParam +): ConfigurationSettingParam { + const configurationSetting: ConfigurationSettingParam = { + key: setting.key, + label: setting.label, + contentType: setting.contentType, + etag: setting.etag, + tags: setting.tags, + value: JSON.stringify({ uri: setting.secretId }) + }; + return configurationSetting; } From 2227c5a90d844c388250a813f464be6071dc8254 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Thu, 1 Apr 2021 02:44:14 -0700 Subject: [PATCH 32/50] SecretReference tests --- .../test/public/index.spec.ts | 139 +++++++++++------- 1 file changed, 87 insertions(+), 52 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts index 6af5fabe8a62..e882fb6748a2 100644 --- a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts @@ -1327,15 +1327,39 @@ describe("AppConfigurationClient", () => { }); describe("SecretReference configuration setting", () => { - const baseSetting: SecretReference = { - secretId: `secret-id-${generateUuid()}`, // TODO: It's a URL in .NET, why the difference? - isReadOnly: false, - key: generateUuid(), - contentType: secretReferenceContentType + const getBaseSetting = (): SecretReference => { + return { + secretId: `https://vault_name.vault.azure.net/secrets/${generateUuid()}`, // TODO: It's a URL in .NET, should we leave it as a string input? + isReadOnly: false, + key: generateUuid(), + label: "label-s", + contentType: secretReferenceContentType + }; }; + function assertSecretReferenceProps( + actual: Omit, + expected: SecretReference + ) { + assert.equal(isSecretReference(actual), true, "Expected to get the SecretReference"); + if (isSecretReference(actual)) { + assert.equal( + actual.key, + expected.key, + "Key from the response from get request is not as expected" + ); + assert.equal(actual.secretId, expected.secretId); + assert.equal(actual.isReadOnly, expected.isReadOnly); + assert.equal(actual.label, expected.label); + assert.equal(actual.contentType, expected.contentType); + } + } + + let addResponse: AddConfigurationSettingResponse; + let baseSetting: SecretReference; beforeEach(async () => { - await client.addConfigurationSetting(baseSetting); + baseSetting = getBaseSetting(); + addResponse = await client.addConfigurationSetting(baseSetting); }); afterEach(async () => { @@ -1345,78 +1369,89 @@ describe("AppConfigurationClient", () => { }); it("can add and get SecretReference", async () => { + assertSecretReferenceProps(addResponse, baseSetting); const getResponse = await client.getConfigurationSetting({ - key: baseSetting.key + key: baseSetting.key, + label: baseSetting.label }); - assert.equal(isSecretReference(getResponse), true, "Expected to get the SecretReference"); - if (isSecretReference(getResponse)) { - assert.equal( - getResponse.key, - baseSetting.key, - "Key from the response from get request is not as expected" - ); - assert.equal( - getResponse.value, - baseSetting.value, - "value from the response from get request is not as expected" - ); - // TODO: assert with readonly and secretId - } + assertSecretReferenceProps(getResponse, baseSetting); }); it("can add and update SecretReference", async () => { const getResponse = await client.getConfigurationSetting({ - key: baseSetting.key + key: baseSetting.key, + label: baseSetting.label }); - assert.equal(isSecretReference(getResponse), true, "Should have been SecretReference"); + const newSecretId = `https://vault_name.vault.azure.net/secrets/${generateUuid()}`; + + assertSecretReferenceProps(getResponse, baseSetting); if (isSecretReference(getResponse)) { - console.log(getResponse.secretId); - // TODO: secretId returned was undefined, that's a bug? - assert.equal(getResponse.secretId, baseSetting.secretId, "Unexpected value for secretId"); - getResponse.secretId = `secret-id-${generateUuid()}`; + getResponse.secretId = newSecretId; } - await client.setConfigurationSetting(getResponse); + const setResponse = await client.setConfigurationSetting(getResponse); + assertSecretReferenceProps(setResponse, { + ...baseSetting, + secretId: newSecretId + }); const getResponseAfterUpdate = await client.getConfigurationSetting({ - key: baseSetting.key + key: baseSetting.key, + label: baseSetting.label + }); + assertSecretReferenceProps(getResponseAfterUpdate, { + ...baseSetting, + secretId: newSecretId }); - assert.equal( - isSecretReference(getResponseAfterUpdate), - true, - "Expected to get the SecretReference" - ); - if (isSecretReference(getResponseAfterUpdate)) { - assert.equal( - getResponseAfterUpdate.key, - baseSetting.key, - "Key from the response from get request is not as expected" - ); - assert.equal( - getResponseAfterUpdate.value, - baseSetting.value, - "value from the response from get request is not as expected" - ); - // TODO: assert with readonly and secretId - } }); - it("can add and update multiple SecretReferences", async () => { + it("can add, list and update multiple SecretReferences", async () => { const secondSetting = { ...baseSetting, key: `${baseSetting.key}-2` }; + const newSecretId = `https://vault_name.vault.azure.net/secrets/${generateUuid()}`; await client.addConfigurationSetting(secondSetting); + let numberOFSecretReferencesReceived = 0; for await (const setting of client.listConfigurationSettings({ keyFilter: `${baseSetting.key}*` })) { - // TODO: check count before and after - assert.equal(isSecretReference(setting), true, "Should have been FeatureFlag"); - if (isSecretReference(setting)) { - // TODO: assert with key, value, readonly and secretId + numberOFSecretReferencesReceived++; + if (setting.key === baseSetting.key) { + assertSecretReferenceProps(setting, baseSetting); + await client.setConfigurationSetting({ + ...baseSetting, + secretId: newSecretId + } as SecretReference); + } else { + assertSecretReferenceProps(setting, secondSetting); + await client.setReadOnly( + { key: setting.key, label: setting.label }, + !secondSetting.isReadOnly + ); } } + assert.equal(numberOFSecretReferencesReceived, 2, "Unexpected number of FeatureFlags seen"); + for await (const setting of client.listConfigurationSettings({ + keyFilter: `${baseSetting.key}*` + })) { + numberOFSecretReferencesReceived--; + if (setting.key === baseSetting.key) { + assertSecretReferenceProps(setting, { ...baseSetting, secretId: newSecretId }); + } else { + assertSecretReferenceProps(setting, { + ...secondSetting, + isReadOnly: !secondSetting.isReadOnly + }); + } + } + + assert.equal( + numberOFSecretReferencesReceived, + 0, + "Unexpected number of SecretReferences seen after updating" + ); await client.deleteConfigurationSetting({ key: secondSetting.key }); }); }); From 2417507a518b5a77d34d62989fde18d7071f7884 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Thu, 1 Apr 2021 02:54:03 -0700 Subject: [PATCH 33/50] Add recordings --- .../recording_can_add_and_get_featureflag.js | 107 +++++++ ...ecording_can_add_and_update_featureflag.js | 175 ++++++++++++ ...d_list_and_update_multiple_featureflags.js | 261 ++++++++++++++++++ ...cording_can_add_and_get_secretreference.js | 99 +++++++ ...ding_can_add_and_update_secretreference.js | 167 +++++++++++ ...st_and_update_multiple_secretreferences.js | 253 +++++++++++++++++ .../test/public/index.spec.ts | 38 +-- 7 files changed, 1083 insertions(+), 17 deletions(-) create mode 100644 sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_and_get_featureflag.js create mode 100644 sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_and_update_featureflag.js create mode 100644 sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_list_and_update_multiple_featureflags.js create mode 100644 sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_and_get_secretreference.js create mode 100644 sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_and_update_secretreference.js create mode 100644 sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_list_and_update_multiple_secretreferences.js diff --git a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_and_get_featureflag.js b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_and_get_featureflag.js new file mode 100644 index 000000000000..87fbee7f2bc1 --- /dev/null +++ b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_and_get_featureflag.js @@ -0,0 +1,107 @@ +let nock = require('nock'); + +module.exports.hash = "0f3d1c1e4498c8bfdfe2a40257d26862"; + +module.exports.testInfo = {"uniqueName":{"name-1":"name-1161727072887004268"},"newDate":{}} + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .put('/kv/.appconfig.featureflag%2Fname-1161727072887004268', {"key":".appconfig.featureflag/name-1161727072887004268","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072887004268\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}"}) + .query(true) + .reply(200, {"etag":"S9DTi4yqIqKbprknPA6rQfErwVn","key":".appconfig.featureflag/name-1161727072887004268","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072887004268\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:09+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:09 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:09 GMT', + 'ETag', + '"S9DTi4yqIqKbprknPA6rQfErwVn"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2Njg=;sn=3030668', + 'x-ms-request-id', + 'f3f8fabb-f338-48e1-bb79-1f063151cb7c', + 'x-ms-correlation-request-id', + 'f3f8fabb-f338-48e1-bb79-1f063151cb7c', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .get('/kv/.appconfig.featureflag%2Fname-1161727072887004268') + .query(true) + .reply(200, {"etag":"S9DTi4yqIqKbprknPA6rQfErwVn","key":".appconfig.featureflag/name-1161727072887004268","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072887004268\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:09+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:09 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:09 GMT', + 'ETag', + '"S9DTi4yqIqKbprknPA6rQfErwVn"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2Njg=;sn=3030668', + 'x-ms-request-id', + '6063d6ea-9721-4d43-816a-75a013f02244', + 'x-ms-correlation-request-id', + '6063d6ea-9721-4d43-816a-75a013f02244', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .delete('/kv/.appconfig.featureflag%2Fname-1161727072887004268') + .query(true) + .reply(200, {"etag":"S9DTi4yqIqKbprknPA6rQfErwVn","key":".appconfig.featureflag/name-1161727072887004268","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072887004268\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:09+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:10 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:09 GMT', + 'ETag', + '"S9DTi4yqIqKbprknPA6rQfErwVn"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2Njk=;sn=3030669', + 'x-ms-request-id', + 'ce339958-523a-4bb9-ac99-6018d09f02f4', + 'x-ms-correlation-request-id', + 'ce339958-523a-4bb9-ac99-6018d09f02f4', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); diff --git a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_and_update_featureflag.js b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_and_update_featureflag.js new file mode 100644 index 000000000000..0150065c126d --- /dev/null +++ b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_and_update_featureflag.js @@ -0,0 +1,175 @@ +let nock = require('nock'); + +module.exports.hash = "3c8d196cecc4465637a5e1cf232b3075"; + +module.exports.testInfo = {"uniqueName":{"name-1":"name-1161727072987507081"},"newDate":{}} + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .put('/kv/.appconfig.featureflag%2Fname-1161727072987507081', {"key":".appconfig.featureflag/name-1161727072987507081","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072987507081\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}"}) + .query(true) + .reply(200, {"etag":"t3LlroJSiLKU6eJF0loGvYQM98U","key":".appconfig.featureflag/name-1161727072987507081","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072987507081\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:10+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:09 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:10 GMT', + 'ETag', + '"t3LlroJSiLKU6eJF0loGvYQM98U"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2NzA=;sn=3030670', + 'x-ms-request-id', + 'c56708a9-7755-4187-886b-67ebb6216be4', + 'x-ms-correlation-request-id', + 'c56708a9-7755-4187-886b-67ebb6216be4', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .get('/kv/.appconfig.featureflag%2Fname-1161727072987507081') + .query(true) + .reply(200, {"etag":"t3LlroJSiLKU6eJF0loGvYQM98U","key":".appconfig.featureflag/name-1161727072987507081","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072987507081\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:10+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:10 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:10 GMT', + 'ETag', + '"t3LlroJSiLKU6eJF0loGvYQM98U"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2NzA=;sn=3030670', + 'x-ms-request-id', + 'faacf86e-42c8-48a0-8b19-e8a65a3a9603', + 'x-ms-correlation-request-id', + 'faacf86e-42c8-48a0-8b19-e8a65a3a9603', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .put('/kv/.appconfig.featureflag%2Fname-1161727072987507081', {"key":".appconfig.featureflag/name-1161727072987507081","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072987507081\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"etag":"t3LlroJSiLKU6eJF0loGvYQM98U"}) + .query(true) + .reply(200, {"etag":"sAsbLlwHYvXpVKkdbOHp51DAyGn","key":".appconfig.featureflag/name-1161727072987507081","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072987507081\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:10+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:10 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:10 GMT', + 'ETag', + '"sAsbLlwHYvXpVKkdbOHp51DAyGn"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2NzE=;sn=3030671', + 'x-ms-request-id', + '80da7901-1e8a-4668-a3dd-1f3062517871', + 'x-ms-correlation-request-id', + '80da7901-1e8a-4668-a3dd-1f3062517871', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .get('/kv/.appconfig.featureflag%2Fname-1161727072987507081') + .query(true) + .reply(200, {"etag":"sAsbLlwHYvXpVKkdbOHp51DAyGn","key":".appconfig.featureflag/name-1161727072987507081","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072987507081\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:10+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:11 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:10 GMT', + 'ETag', + '"sAsbLlwHYvXpVKkdbOHp51DAyGn"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2NzE=;sn=3030671', + 'x-ms-request-id', + '1b619093-0e02-49fb-bc32-f18431ea8461', + 'x-ms-correlation-request-id', + '1b619093-0e02-49fb-bc32-f18431ea8461', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .delete('/kv/.appconfig.featureflag%2Fname-1161727072987507081') + .query(true) + .reply(200, {"etag":"sAsbLlwHYvXpVKkdbOHp51DAyGn","key":".appconfig.featureflag/name-1161727072987507081","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072987507081\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:10+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:10 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:10 GMT', + 'ETag', + '"sAsbLlwHYvXpVKkdbOHp51DAyGn"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2NzI=;sn=3030672', + 'x-ms-request-id', + 'a7882374-6acb-429b-b266-3b28be239d99', + 'x-ms-correlation-request-id', + 'a7882374-6acb-429b-b266-3b28be239d99', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); diff --git a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_list_and_update_multiple_featureflags.js b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_list_and_update_multiple_featureflags.js new file mode 100644 index 000000000000..0aaf9e8f3ff4 --- /dev/null +++ b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_list_and_update_multiple_featureflags.js @@ -0,0 +1,261 @@ +let nock = require('nock'); + +module.exports.hash = "0cc988c098bc8745cc267bc5c3792b4a"; + +module.exports.testInfo = {"uniqueName":{"name-1":"name-1161727073119304174"},"newDate":{}} + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .put('/kv/.appconfig.featureflag%2Fname-1161727073119304174', {"key":".appconfig.featureflag/name-1161727073119304174","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}"}) + .query(true) + .reply(200, {"etag":"UJxMvKnVRioPzKGTYjBkA5xW6QH","key":".appconfig.featureflag/name-1161727073119304174","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:11+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:11 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:11 GMT', + 'ETag', + '"UJxMvKnVRioPzKGTYjBkA5xW6QH"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2NzM=;sn=3030673', + 'x-ms-request-id', + 'abfd28e5-076f-4981-956f-8f3d41167263', + 'x-ms-correlation-request-id', + 'abfd28e5-076f-4981-956f-8f3d41167263', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .put('/kv/.appconfig.featureflag%2Fname-1161727073119304174-2', {"key":".appconfig.featureflag/name-1161727073119304174-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174-2\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}"}) + .query(true) + .reply(200, {"etag":"ad1cici6qLxChztqw22lEjHUZ1T","key":".appconfig.featureflag/name-1161727073119304174-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174-2\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:11+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:11 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:11 GMT', + 'ETag', + '"ad1cici6qLxChztqw22lEjHUZ1T"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2NzQ=;sn=3030674', + 'x-ms-request-id', + '6397af60-9432-4352-a22b-86d7b2f520a2', + 'x-ms-correlation-request-id', + '6397af60-9432-4352-a22b-86d7b2f520a2', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .get('/kv') + .query(true) + .reply(200, {"items":[{"etag":"UJxMvKnVRioPzKGTYjBkA5xW6QH","key":".appconfig.featureflag/name-1161727073119304174","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:11+00:00"},{"etag":"ad1cici6qLxChztqw22lEjHUZ1T","key":".appconfig.featureflag/name-1161727073119304174-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174-2\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:11+00:00"}]}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:12 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kvset+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2NzQ=;sn=3030674', + 'x-ms-request-id', + '3e52e20f-d451-40aa-b4ba-7bf99bab869a', + 'x-ms-correlation-request-id', + '3e52e20f-d451-40aa-b4ba-7bf99bab869a', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .put('/kv/.appconfig.featureflag%2Fname-1161727073119304174', {"key":".appconfig.featureflag/name-1161727073119304174","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}"}) + .query(true) + .reply(200, {"etag":"ZrLg0196ACVWsBbsRkvIMmlK6od","key":".appconfig.featureflag/name-1161727073119304174","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:12+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:12 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:12 GMT', + 'ETag', + '"ZrLg0196ACVWsBbsRkvIMmlK6od"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2NzU=;sn=3030675', + 'x-ms-request-id', + '49a795f4-0c13-4a3d-87c0-b18eabdb8ce0', + 'x-ms-correlation-request-id', + '49a795f4-0c13-4a3d-87c0-b18eabdb8ce0', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .put('/kv/.appconfig.featureflag%2Fname-1161727073119304174-2', {"key":".appconfig.featureflag/name-1161727073119304174-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174-2\",\"description\":\"I'm new description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"etag":"ad1cici6qLxChztqw22lEjHUZ1T"}) + .query(true) + .reply(200, {"etag":"fryVmYDYh85sVGDXYhwlV6K8YxY","key":".appconfig.featureflag/name-1161727073119304174-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174-2\",\"description\":\"I'm new description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:12+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:12 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:12 GMT', + 'ETag', + '"fryVmYDYh85sVGDXYhwlV6K8YxY"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2NzY=;sn=3030676', + 'x-ms-request-id', + 'ae095328-7f09-46c1-b9dc-c74283e140e1', + 'x-ms-correlation-request-id', + 'ae095328-7f09-46c1-b9dc-c74283e140e1', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .get('/kv') + .query(true) + .reply(200, {"items":[{"etag":"ZrLg0196ACVWsBbsRkvIMmlK6od","key":".appconfig.featureflag/name-1161727073119304174","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:12+00:00"},{"etag":"fryVmYDYh85sVGDXYhwlV6K8YxY","key":".appconfig.featureflag/name-1161727073119304174-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174-2\",\"description\":\"I'm new description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:12+00:00"}]}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:12 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kvset+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2NzY=;sn=3030676', + 'x-ms-request-id', + 'd433f831-bafd-4b5b-8d08-10ddc63e61db', + 'x-ms-correlation-request-id', + 'd433f831-bafd-4b5b-8d08-10ddc63e61db', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .delete('/kv/.appconfig.featureflag%2Fname-1161727073119304174-2') + .query(true) + .reply(204, "", [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:13 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Connection', + 'close', + 'x-ms-request-id', + 'f78a9af3-4f0e-41cb-839b-0628840e6c3d', + 'x-ms-correlation-request-id', + 'f78a9af3-4f0e-41cb-839b-0628840e6c3d', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .delete('/kv/.appconfig.featureflag%2Fname-1161727073119304174') + .query(true) + .reply(200, {"etag":"ZrLg0196ACVWsBbsRkvIMmlK6od","key":".appconfig.featureflag/name-1161727073119304174","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:12+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:13 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:12 GMT', + 'ETag', + '"ZrLg0196ACVWsBbsRkvIMmlK6od"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2Nzc=;sn=3030677', + 'x-ms-request-id', + '9dbe622b-92cc-4255-aabc-522a60c9b9fb', + 'x-ms-correlation-request-id', + '9dbe622b-92cc-4255-aabc-522a60c9b9fb', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); diff --git a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_and_get_secretreference.js b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_and_get_secretreference.js new file mode 100644 index 000000000000..20fcf80b58e9 --- /dev/null +++ b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_and_get_secretreference.js @@ -0,0 +1,99 @@ +let nock = require('nock'); + +module.exports.hash = "505c9a3a224a00ff0b9a9c06eefae9e4"; + +module.exports.testInfo = {"uniqueName":{"name-2":"name-2161727073344802614","name-3":"name-3161727073344802596"},"newDate":{}} + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .put('/kv/name-3161727073344802596', {"key":"name-3161727073344802596","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073344802614\"}"}) + .query(true) + .reply(200, {"etag":"2pCLRTH84Qyodv6bzHnpAyjHJls","key":"name-3161727073344802596","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073344802614\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:13+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:14 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:13 GMT', + 'ETag', + '"2pCLRTH84Qyodv6bzHnpAyjHJls"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2Nzg=;sn=3030678', + 'x-ms-request-id', + 'cc1396e6-f3eb-4f90-8ea8-5d66173e87cc', + 'x-ms-correlation-request-id', + 'cc1396e6-f3eb-4f90-8ea8-5d66173e87cc', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .get('/kv/name-3161727073344802596') + .query(true) + .reply(200, {"etag":"2pCLRTH84Qyodv6bzHnpAyjHJls","key":"name-3161727073344802596","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073344802614\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:13+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:13 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:13 GMT', + 'ETag', + '"2pCLRTH84Qyodv6bzHnpAyjHJls"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2Nzg=;sn=3030678', + 'x-ms-request-id', + '5effd0c1-3b28-4b65-b111-ad302151477f', + 'x-ms-correlation-request-id', + '5effd0c1-3b28-4b65-b111-ad302151477f', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .delete('/kv/name-3161727073344802596') + .query(true) + .reply(204, "", [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:14 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Connection', + 'close', + 'x-ms-request-id', + '95903e9d-ae5d-4c02-9711-86412696f7c7', + 'x-ms-correlation-request-id', + '95903e9d-ae5d-4c02-9711-86412696f7c7', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); diff --git a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_and_update_secretreference.js b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_and_update_secretreference.js new file mode 100644 index 000000000000..068f6c0cdf4a --- /dev/null +++ b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_and_update_secretreference.js @@ -0,0 +1,167 @@ +let nock = require('nock'); + +module.exports.hash = "a9957c7b9cfbfcbbecbe8e53ed0fb850"; + +module.exports.testInfo = {"uniqueName":{"name-2":"name-2161727073431701823","name-3":"name-3161727073431701524","name-4":"name-4161727073488106751"},"newDate":{}} + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .put('/kv/name-3161727073431701524', {"key":"name-3161727073431701524","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073431701823\"}"}) + .query(true) + .reply(200, {"etag":"9clx3p52mLOybg5yELR0OdyOkBG","key":"name-3161727073431701524","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073431701823\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:14+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:14 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:14 GMT', + 'ETag', + '"9clx3p52mLOybg5yELR0OdyOkBG"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2Nzk=;sn=3030679', + 'x-ms-request-id', + '37a33f1b-6668-42a6-a90d-bb9b9e9c40ad', + 'x-ms-correlation-request-id', + '37a33f1b-6668-42a6-a90d-bb9b9e9c40ad', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .get('/kv/name-3161727073431701524') + .query(true) + .reply(200, {"etag":"9clx3p52mLOybg5yELR0OdyOkBG","key":"name-3161727073431701524","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073431701823\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:14+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:15 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:14 GMT', + 'ETag', + '"9clx3p52mLOybg5yELR0OdyOkBG"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2Nzk=;sn=3030679', + 'x-ms-request-id', + '8dadfbb9-f162-4509-b55f-c11d1877d523', + 'x-ms-correlation-request-id', + '8dadfbb9-f162-4509-b55f-c11d1877d523', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .put('/kv/name-3161727073431701524', {"key":"name-3161727073431701524","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-4161727073488106751\"}","tags":{},"etag":"9clx3p52mLOybg5yELR0OdyOkBG"}) + .query(true) + .reply(200, {"etag":"r8HpOzCxIibrgWWdgvRqaHzmf2A","key":"name-3161727073431701524","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-4161727073488106751\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:15+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:14 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:15 GMT', + 'ETag', + '"r8HpOzCxIibrgWWdgvRqaHzmf2A"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2ODA=;sn=3030680', + 'x-ms-request-id', + '6d3bca69-b745-4c8d-97bd-b05869735683', + 'x-ms-correlation-request-id', + '6d3bca69-b745-4c8d-97bd-b05869735683', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .get('/kv/name-3161727073431701524') + .query(true) + .reply(200, {"etag":"r8HpOzCxIibrgWWdgvRqaHzmf2A","key":"name-3161727073431701524","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-4161727073488106751\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:15+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:15 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:15 GMT', + 'ETag', + '"r8HpOzCxIibrgWWdgvRqaHzmf2A"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2ODA=;sn=3030680', + 'x-ms-request-id', + '197d7ee7-2252-460f-ba6b-5632f4c26e8f', + 'x-ms-correlation-request-id', + '197d7ee7-2252-460f-ba6b-5632f4c26e8f', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .delete('/kv/name-3161727073431701524') + .query(true) + .reply(204, "", [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:15 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Connection', + 'close', + 'x-ms-request-id', + '2f97529e-7257-4499-9be3-3be9eea1b96c', + 'x-ms-correlation-request-id', + '2f97529e-7257-4499-9be3-3be9eea1b96c', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); diff --git a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_list_and_update_multiple_secretreferences.js b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_list_and_update_multiple_secretreferences.js new file mode 100644 index 000000000000..aa626cd43e28 --- /dev/null +++ b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_list_and_update_multiple_secretreferences.js @@ -0,0 +1,253 @@ +let nock = require('nock'); + +module.exports.hash = "b4a0c9bd7dbaf880005fc7e37af01169"; + +module.exports.testInfo = {"uniqueName":{"name-2":"name-2161727073573502374","name-3":"name-3161727073573502979","name-5":"name-5161727073599408784"},"newDate":{}} + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .put('/kv/name-3161727073573502979', {"key":"name-3161727073573502979","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}"}) + .query(true) + .reply(200, {"etag":"I9YupYSj1oJt9iMGQ9182qHQH9u","key":"name-3161727073573502979","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:16+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:16 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:16 GMT', + 'ETag', + '"I9YupYSj1oJt9iMGQ9182qHQH9u"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2ODE=;sn=3030681', + 'x-ms-request-id', + '75fa4b1c-e25d-4543-a3f9-f0c96303ff56', + 'x-ms-correlation-request-id', + '75fa4b1c-e25d-4543-a3f9-f0c96303ff56', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .put('/kv/name-3161727073573502979-2', {"key":"name-3161727073573502979-2","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}"}) + .query(true) + .reply(200, {"etag":"F6Xhn8XTexGUi19MlmuYgMqZFIY","key":"name-3161727073573502979-2","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:16+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:15 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:16 GMT', + 'ETag', + '"F6Xhn8XTexGUi19MlmuYgMqZFIY"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2ODI=;sn=3030682', + 'x-ms-request-id', + '4b3a346b-dc79-4b6d-9c0d-7583041ec800', + 'x-ms-correlation-request-id', + '4b3a346b-dc79-4b6d-9c0d-7583041ec800', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .get('/kv') + .query(true) + .reply(200, {"items":[{"etag":"I9YupYSj1oJt9iMGQ9182qHQH9u","key":"name-3161727073573502979","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:16+00:00"},{"etag":"F6Xhn8XTexGUi19MlmuYgMqZFIY","key":"name-3161727073573502979-2","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:16+00:00"}]}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:16 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kvset+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2ODI=;sn=3030682', + 'x-ms-request-id', + '4120b416-ba57-4beb-8ae2-8a930a732205', + 'x-ms-correlation-request-id', + '4120b416-ba57-4beb-8ae2-8a930a732205', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .put('/kv/name-3161727073573502979', {"key":"name-3161727073573502979","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-5161727073599408784\"}"}) + .query(true) + .reply(200, {"etag":"Qrc2lygn505tdOc9ZlUXv1G7x7h","key":"name-3161727073573502979","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-5161727073599408784\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:16+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:17 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:16 GMT', + 'ETag', + '"Qrc2lygn505tdOc9ZlUXv1G7x7h"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2ODM=;sn=3030683', + 'x-ms-request-id', + '234cc4db-4bd4-402c-82b3-5a8af51f3ff2', + 'x-ms-correlation-request-id', + '234cc4db-4bd4-402c-82b3-5a8af51f3ff2', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .put('/locks/name-3161727073573502979-2') + .query(true) + .reply(200, {"etag":"fcQO0dSZGtVEanPOL8sUScVF731","key":"name-3161727073573502979-2","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}","tags":{},"locked":true,"last_modified":"2021-04-01T09:52:17+00:00"}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:16 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Last-Modified', + 'Thu, 01 Apr 2021 09:52:17 GMT', + 'ETag', + '"fcQO0dSZGtVEanPOL8sUScVF731"', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2ODQ=;sn=3030684', + 'x-ms-request-id', + '45b0802a-9f31-43a0-8a35-abd8b2c9a28a', + 'x-ms-correlation-request-id', + '45b0802a-9f31-43a0-8a35-abd8b2c9a28a', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .get('/kv') + .query(true) + .reply(200, {"items":[{"etag":"Qrc2lygn505tdOc9ZlUXv1G7x7h","key":"name-3161727073573502979","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-5161727073599408784\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:16+00:00"},{"etag":"fcQO0dSZGtVEanPOL8sUScVF731","key":"name-3161727073573502979-2","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}","tags":{},"locked":true,"last_modified":"2021-04-01T09:52:17+00:00"}]}, [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:17 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kvset+json; charset=utf-8', + 'Transfer-Encoding', + 'chunked', + 'Connection', + 'close', + 'Sync-Token', + 'zAJw6V16=NTo1IzMwMzA2ODQ=;sn=3030684', + 'x-ms-request-id', + '321e5b1e-385c-4366-9052-ab2ea3b4c6ad', + 'x-ms-correlation-request-id', + '321e5b1e-385c-4366-9052-ab2ea3b4c6ad', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .delete('/kv/name-3161727073573502979-2') + .query(true) + .reply(204, "", [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:17 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Connection', + 'close', + 'x-ms-request-id', + 'c49af6ea-1247-4dd0-a4c5-7f14e9036ea1', + 'x-ms-correlation-request-id', + 'c49af6ea-1247-4dd0-a4c5-7f14e9036ea1', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); + +nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) + .delete('/kv/name-3161727073573502979') + .query(true) + .reply(204, "", [ + 'Server', + 'openresty/1.17.8.2', + 'Date', + 'Thu, 01 Apr 2021 09:52:18 GMT', + 'Content-Type', + 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', + 'Connection', + 'close', + 'x-ms-request-id', + 'affb9d48-91fa-4d8f-bfdc-edd7dbfd5319', + 'x-ms-correlation-request-id', + 'affb9d48-91fa-4d8f-bfdc-edd7dbfd5319', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Credentials', + 'true', + 'Access-Control-Expose-Headers', + 'DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Authorization, x-ms-client-request-id, x-ms-useragent, x-ms-content-sha256, x-ms-date, host, Accept, Accept-Datetime, Date, If-Match, If-None-Match, Sync-Token, x-ms-return-client-request-id, ETag, Last-Modified, Link, Memento-Datetime, retry-after-ms, x-ms-request-id, x-ms-client-session-id, x-ms-effective-locale, WWW-Authenticate', + 'Strict-Transport-Security', + 'max-age=15724800; includeSubDomains' +]); diff --git a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts index e882fb6748a2..7e13164bfbc5 100644 --- a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts @@ -27,7 +27,7 @@ import { SecretReference, secretReferenceContentType } from "../../src"; -import { delay, generateUuid } from "@azure/core-http"; +import { delay } from "@azure/core-http"; import { Recorder } from "@azure/test-utils-recorder"; import { Context } from "mocha"; @@ -1194,21 +1194,21 @@ describe("AppConfigurationClient", () => { { name: "Microsoft.Percentage", parameters: { value: 25 } } ]; - const baseSetting: FeatureFlag = { - conditions: { - clientFilters - }, - enabled: false, - isReadOnly: false, - key: `${featureFlagPrefix + generateUuid()}`, - contentType: featureFlagContentType, - description: "I'm a description", - label: "label-1" - }; - + let baseSetting: FeatureFlag; let addResponse: AddConfigurationSettingResponse; beforeEach(async () => { + baseSetting = { + conditions: { + clientFilters + }, + enabled: false, + isReadOnly: false, + key: `${featureFlagPrefix + recorder.getUniqueName("name-1")}`, + contentType: featureFlagContentType, + description: "I'm a description", + label: "label-1" + }; addResponse = await client.addConfigurationSetting(baseSetting); }); @@ -1329,9 +1329,9 @@ describe("AppConfigurationClient", () => { describe("SecretReference configuration setting", () => { const getBaseSetting = (): SecretReference => { return { - secretId: `https://vault_name.vault.azure.net/secrets/${generateUuid()}`, // TODO: It's a URL in .NET, should we leave it as a string input? + secretId: `https://vault_name.vault.azure.net/secrets/${recorder.getUniqueName("name-2")}`, // TODO: It's a URL in .NET, should we leave it as a string input? isReadOnly: false, - key: generateUuid(), + key: recorder.getUniqueName("name-3"), label: "label-s", contentType: secretReferenceContentType }; @@ -1382,7 +1382,9 @@ describe("AppConfigurationClient", () => { key: baseSetting.key, label: baseSetting.label }); - const newSecretId = `https://vault_name.vault.azure.net/secrets/${generateUuid()}`; + const newSecretId = `https://vault_name.vault.azure.net/secrets/${recorder.getUniqueName( + "name-4" + )}`; assertSecretReferenceProps(getResponse, baseSetting); if (isSecretReference(getResponse)) { @@ -1410,7 +1412,9 @@ describe("AppConfigurationClient", () => { ...baseSetting, key: `${baseSetting.key}-2` }; - const newSecretId = `https://vault_name.vault.azure.net/secrets/${generateUuid()}`; + const newSecretId = `https://vault_name.vault.azure.net/secrets/${recorder.getUniqueName( + "name-5" + )}`; await client.addConfigurationSetting(secondSetting); let numberOFSecretReferencesReceived = 0; From fa647f483a65e2942a3e6b29040b521ed47b105b Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Thu, 1 Apr 2021 11:39:10 -0700 Subject: [PATCH 34/50] API Report --- .../app-configuration/review/app-configuration.api.md | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md index d19fa632cc05..4e7073178ebd 100644 --- a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md +++ b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md @@ -87,7 +87,6 @@ export interface FeatureFlagParam extends ConfigurationSettingParam { clientFilters: (FeatureFlagTargetingClientFilter | FeatureFlagTimeWindowClientFilter | FeatureFlagPercentageClientFilter | object)[]; }; description?: string; - displayName: string; enabled: boolean; } From edc91f0e0234ae1bb098759ba824b195189cba7f Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Thu, 1 Apr 2021 13:53:06 -0700 Subject: [PATCH 35/50] minor bug fix and switch to Record --- sdk/appconfiguration/app-configuration/src/featureFlag.ts | 8 ++++---- .../app-configuration/src/internal/jsonModels.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index 6e03cadcb9b5..71e027ab0bae 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -38,7 +38,7 @@ export interface FeatureFlagParam extends ConfigurationSettingParam { | FeatureFlagTargetingClientFilter | FeatureFlagTimeWindowClientFilter | FeatureFlagPercentageClientFilter - | object + | Record )[]; }; /** @@ -281,7 +281,7 @@ export function convertJsonConditions( }; return filter; } else { - return jsonFilter as object; + return jsonFilter; } }); @@ -326,7 +326,7 @@ export function convertToJsonConditions( }; return jsonFilter; - } else if (isJsonFeatureFlagPercentageClientFilter(filter)) { + } else if (isFeatureFlagPercentageClientFilter(filter)) { // try not to slice any unknown attributes const jsonFilter: JsonFeatureFlagPercentageClientFilter = { name: filter.name, @@ -336,7 +336,7 @@ export function convertToJsonConditions( }; return jsonFilter; } else { - return filter as object; + return filter; } }); diff --git a/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts index caad8829bae1..4360c0833827 100644 --- a/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts +++ b/sdk/appconfiguration/app-configuration/src/internal/jsonModels.ts @@ -14,7 +14,7 @@ export type JsonFeatureFlag = { | JsonFeatureFlagTargetingClientFilter | JsonFeatureFlagTimeWindowClientFilter | JsonFeatureFlagPercentageClientFilter - | object + | Record )[]; }; description?: string; From 9e4d21f89ef345bdb8a90df1e984c91250cafbcd Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Thu, 1 Apr 2021 13:57:35 -0700 Subject: [PATCH 36/50] API Report --- .../app-configuration/review/app-configuration.api.md | 2 +- .../app-configuration/test/public/index.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md index 4e7073178ebd..8398fffc3057 100644 --- a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md +++ b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md @@ -84,7 +84,7 @@ export const featureFlagContentType = "application/vnd.microsoft.appconfig.ff+js // @public export interface FeatureFlagParam extends ConfigurationSettingParam { conditions: { - clientFilters: (FeatureFlagTargetingClientFilter | FeatureFlagTimeWindowClientFilter | FeatureFlagPercentageClientFilter | object)[]; + clientFilters: (FeatureFlagTargetingClientFilter | FeatureFlagTimeWindowClientFilter | FeatureFlagPercentageClientFilter | Record)[]; }; description?: string; enabled: boolean; diff --git a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts index 7e13164bfbc5..32fc935b6ff2 100644 --- a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts @@ -1158,7 +1158,7 @@ describe("AppConfigurationClient", () => { describe("FeatureFlag configuration setting", () => { const clientFilters: ( - | object + | Record | FeatureFlagTargetingClientFilter | FeatureFlagTimeWindowClientFilter | FeatureFlagPercentageClientFilter From d53899ca6e9d14f9495886c1079b081dcbd3b148 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Thu, 1 Apr 2021 13:57:47 -0700 Subject: [PATCH 37/50] remove commented code --- sdk/appconfiguration/app-configuration/src/featureFlag.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index 71e027ab0bae..2a7805452027 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -80,7 +80,6 @@ export interface FeatureFlagTargetingClientFilter { }[]; defaultRolloutPercentage: number; }; - // [key: string]: any; }; } @@ -100,7 +99,6 @@ export interface FeatureFlagTimeWindowClientFilter { parameters: { start: string; end: string; - // [key: string]: any; }; } @@ -120,7 +118,6 @@ export interface FeatureFlagPercentageClientFilter { */ parameters: { value: number; - // [key: string]: any - portal shows more inputs, but throws an error if provided more. }; } From 16831fd861093885bf0d52e58d4ebd7affb05b52 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Thu, 1 Apr 2021 14:01:21 -0700 Subject: [PATCH 38/50] what if client filters is undefined? answered! --- .../app-configuration/src/featureFlag.ts | 82 ++++++++++--------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index 2a7805452027..2e50f7b08475 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -238,49 +238,51 @@ export function serializeFeatureFlagParam(setting: FeatureFlagParam): Configurat export function convertJsonConditions( conditions: JsonFeatureFlag["conditions"] ): FeatureFlag["conditions"] { - const clientFilters = conditions.client_filters.map((jsonFilter) => { - if (isJsonFeatureFlagTargetingClientFilter(jsonFilter)) { - // try not to slice any unknown attributes - const filter: FeatureFlagTargetingClientFilter = { - name: jsonFilter.name, - parameters: { - audience: { - groups: - jsonFilter.parameters.Audience.Groups.map((grp) => ({ - name: grp.Name, - rolloutPercentage: grp.RolloutPercentage - })) || [], - users: jsonFilter.parameters.Audience.Users || [], - defaultRolloutPercentage: jsonFilter.parameters.Audience.DefaultRolloutPercentage - } - } - }; + const clientFilters = !conditions.client_filters + ? [] + : conditions.client_filters.map((jsonFilter) => { + if (isJsonFeatureFlagTargetingClientFilter(jsonFilter)) { + // try not to slice any unknown attributes + const filter: FeatureFlagTargetingClientFilter = { + name: jsonFilter.name, + parameters: { + audience: { + groups: + jsonFilter.parameters.Audience.Groups.map((grp) => ({ + name: grp.Name, + rolloutPercentage: grp.RolloutPercentage + })) || [], + users: jsonFilter.parameters.Audience.Users || [], + defaultRolloutPercentage: jsonFilter.parameters.Audience.DefaultRolloutPercentage + } + } + }; - return filter; - } else if (isJsonFeatureFlagTimeWindowClientFilter(jsonFilter)) { - // try not to slice any unknown attributes - const filter: FeatureFlagTimeWindowClientFilter = { - name: jsonFilter.name, - parameters: { - start: jsonFilter.parameters.Start, - end: jsonFilter.parameters.End - } - }; + return filter; + } else if (isJsonFeatureFlagTimeWindowClientFilter(jsonFilter)) { + // try not to slice any unknown attributes + const filter: FeatureFlagTimeWindowClientFilter = { + name: jsonFilter.name, + parameters: { + start: jsonFilter.parameters.Start, + end: jsonFilter.parameters.End + } + }; - return filter; - } else if (isJsonFeatureFlagPercentageClientFilter(jsonFilter)) { - // try not to slice any unknown attributes - const filter: FeatureFlagPercentageClientFilter = { - name: jsonFilter.name, - parameters: { - value: jsonFilter.parameters.Value + return filter; + } else if (isJsonFeatureFlagPercentageClientFilter(jsonFilter)) { + // try not to slice any unknown attributes + const filter: FeatureFlagPercentageClientFilter = { + name: jsonFilter.name, + parameters: { + value: jsonFilter.parameters.Value + } + }; + return filter; + } else { + return jsonFilter; } - }; - return filter; - } else { - return jsonFilter; - } - }); + }); return { clientFilters From 50365f0fcb2458fe8a9241d78aec4f19611f662d Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Thu, 1 Apr 2021 16:58:08 -0700 Subject: [PATCH 39/50] docs for start and end --- .../app-configuration/src/featureFlag.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index 2e50f7b08475..afe666dd22bc 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -97,7 +97,21 @@ export interface FeatureFlagTimeWindowClientFilter { * Parameters that can be passed in the filter. */ parameters: { + /** + * Start time of the time window. + * Expected UTCString - Example: "Wed, 01 May 2021 13:59:59 GMT" + * + * UTCString can be obtained from `new Date().toUTCString()`. + * Use `Date.parse()` to parse the UTCString as Date. + */ start: string; + /** + * End time of the time window. + * Expected UTCString - Example: "Wed, 05 May 2021 13:59:59 GMT" + * + * UTCString can be obtained from `new Date().toUTCString()`. + * Use `Date.parse()` to parse the UTCString as Date. + */ end: string; }; } From ffe3a3835a1688f1a51724f3d59f8e140a6d1351 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Thu, 1 Apr 2021 16:58:52 -0700 Subject: [PATCH 40/50] remove TODO for Dates --- .../app-configuration/test/public/index.spec.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts index 32fc935b6ff2..61cd1a833fe3 100644 --- a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts @@ -1168,13 +1168,6 @@ describe("AppConfigurationClient", () => { parameters: { start: "Wed, 01 May 2019 13:59:59 GMT", end: "Mon, 01 July 2019 00:00:00 GMT" - // TODO: dates should accept `string | Date` - // Returning - // - SDK would return Date for all the config-settings if parsable - // - SDK would return strings for non parsable strings - // Passing as an argument - // - If passed in a string, we'll pass it as is. - // - If passed Date, we'll make it a string. } }, { name: "FilterX" }, From 07bb8f267606d610e3a65bd9b1c1e4c27aa5f48e Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Thu, 1 Apr 2021 17:01:32 -0700 Subject: [PATCH 41/50] internalConfigurationSetting-> KeyValue --- .../app-configuration/src/appConfigurationClient.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts b/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts index 57a238d64db7..515daf84d8d7 100644 --- a/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts +++ b/sdk/appconfiguration/app-configuration/src/appConfigurationClient.ts @@ -192,13 +192,11 @@ export class AppConfigurationClient { options: AddConfigurationSettingOptions = {} ): Promise { return this._trace("addConfigurationSetting", options, async (newOptions) => { - const internalConfigurationSetting = serializeAsConfigurationSettingParam( - configurationSetting - ); + const keyValue = serializeAsConfigurationSettingParam(configurationSetting); const originalResponse = await this.client.putKeyValue(configurationSetting.key, { ifNoneMatch: "*", label: configurationSetting.label, - entity: internalConfigurationSetting, + entity: keyValue, ...newOptions }); @@ -461,13 +459,11 @@ export class AppConfigurationClient { options: SetConfigurationSettingOptions = {} ): Promise { return this._trace("setConfigurationSetting", options, async (newOptions) => { - const internalConfigurationSetting = serializeAsConfigurationSettingParam( - configurationSetting - ); + const keyValue = serializeAsConfigurationSettingParam(configurationSetting); const response = await this.client.putKeyValue(configurationSetting.key, { ...newOptions, label: configurationSetting.label, - entity: internalConfigurationSetting, + entity: keyValue, ...checkAndFormatIfAndIfNoneMatch(configurationSetting, options) }); From a7a46edff55f7418aa2171d158e3cc9918a6e559 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Fri, 2 Apr 2021 04:16:31 -0700 Subject: [PATCH 42/50] typguard, remove comments, etc - feedback --- .../app-configuration/src/featureFlag.ts | 31 ++++++------ .../src/internal/typeguards.ts | 47 +++++++++++++++++++ 2 files changed, 63 insertions(+), 15 deletions(-) create mode 100644 sdk/appconfiguration/app-configuration/src/internal/typeguards.ts diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index afe666dd22bc..714e4b1af044 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -10,6 +10,7 @@ import { JsonFeatureFlagTargetingClientFilter, JsonFeatureFlagTimeWindowClientFilter } from "./internal/jsonModels"; +import { isObjectWithProperties } from "./internal/typeguards"; import { ConfigurationSetting, ConfigurationSettingParam } from "./models"; /** @@ -149,27 +150,33 @@ export function isFeatureFlag(setting: ConfigurationSetting | FeatureFlag): sett * This helper method tells you if the given client filter has the name "Microsoft.Targeting". */ function isFeatureFlagTargetingClientFilter( - clientFilter: any + clientFilter: unknown ): clientFilter is FeatureFlagTargetingClientFilter { - return clientFilter.name === "Microsoft.Targeting"; + return ( + isObjectWithProperties(clientFilter, ["name"]) && clientFilter.name === "Microsoft.Targeting" + ); } /** * This helper method tells you if the given client filter has the name "Microsoft.TimeWindow". */ function isFeatureFlagTimeWindowClientFilter( - clientFilter: any + clientFilter: unknown ): clientFilter is FeatureFlagTimeWindowClientFilter { - return clientFilter.name === "Microsoft.TimeWindow"; + return ( + isObjectWithProperties(clientFilter, ["name"]) && clientFilter.name === "Microsoft.TimeWindow" + ); } /** * This helper method tells you if the given client filter has the name "Microsoft.Percentage". */ function isFeatureFlagPercentageClientFilter( - clientFilter: any + clientFilter: unknown ): clientFilter is FeatureFlagPercentageClientFilter { - return clientFilter.name === "Microsoft.Percentage"; + return ( + isObjectWithProperties(clientFilter, ["name"]) && clientFilter.name === "Microsoft.Percentage" + ); } /** @@ -190,7 +197,7 @@ export type FeatureFlagType< */ export function isFeatureFlagClientFilter( type: T, - obj: any + obj: unknown ): obj is FeatureFlagType { switch (type) { case "targeting": @@ -200,7 +207,7 @@ export function isFeatureFlagClientFilter; featureFlag.conditions = convertJsonConditions(jsonFeatureFlag.conditions); - featureFlag.enabled = jsonFeatureFlag.enabled; + featureFlag.enabled = Boolean(jsonFeatureFlag.enabled); featureFlag.description = jsonFeatureFlag.description; return setting; @@ -256,7 +263,6 @@ export function convertJsonConditions( ? [] : conditions.client_filters.map((jsonFilter) => { if (isJsonFeatureFlagTargetingClientFilter(jsonFilter)) { - // try not to slice any unknown attributes const filter: FeatureFlagTargetingClientFilter = { name: jsonFilter.name, parameters: { @@ -274,7 +280,6 @@ export function convertJsonConditions( return filter; } else if (isJsonFeatureFlagTimeWindowClientFilter(jsonFilter)) { - // try not to slice any unknown attributes const filter: FeatureFlagTimeWindowClientFilter = { name: jsonFilter.name, parameters: { @@ -285,7 +290,6 @@ export function convertJsonConditions( return filter; } else if (isJsonFeatureFlagPercentageClientFilter(jsonFilter)) { - // try not to slice any unknown attributes const filter: FeatureFlagPercentageClientFilter = { name: jsonFilter.name, parameters: { @@ -311,7 +315,6 @@ export function convertToJsonConditions( ): JsonFeatureFlag["conditions"] { const client_filters = conditions.clientFilters.map((filter) => { if (isFeatureFlagTargetingClientFilter(filter)) { - // try not to slice any unknown attributes const jsonFilter: JsonFeatureFlagTargetingClientFilter = { name: filter.name, parameters: { @@ -329,7 +332,6 @@ export function convertToJsonConditions( return jsonFilter; } else if (isFeatureFlagTimeWindowClientFilter(filter)) { - // try not to slice any unknown attributes const jsonFilter: JsonFeatureFlagTimeWindowClientFilter = { name: filter.name, parameters: { @@ -340,7 +342,6 @@ export function convertToJsonConditions( return jsonFilter; } else if (isFeatureFlagPercentageClientFilter(filter)) { - // try not to slice any unknown attributes const jsonFilter: JsonFeatureFlagPercentageClientFilter = { name: filter.name, parameters: { diff --git a/sdk/appconfiguration/app-configuration/src/internal/typeguards.ts b/sdk/appconfiguration/app-configuration/src/internal/typeguards.ts new file mode 100644 index 000000000000..f14b6c20d1f2 --- /dev/null +++ b/sdk/appconfiguration/app-configuration/src/internal/typeguards.ts @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * Helper TypeGuard that checks if something is defined or not. + * @param thing - Anything + * @internal + */ +export function isDefined(thing: T | undefined | null): thing is T { + return typeof thing !== "undefined" && thing !== null; +} + +/** + * Helper TypeGuard that checks if the input is an object with the specified properties. + * @param thing - Anything. + * @param properties - The name of the properties that should appear in the object. + * @internal + */ +export function isObjectWithProperties( + thing: Thing, + properties: PropertyName[] +): thing is Thing & Record { + if (!isDefined(thing) || typeof thing !== "object") { + return false; + } + + for (const property of properties) { + if (!objectHasProperty(thing, property)) { + return false; + } + } + + return true; +} + +/** + * Helper TypeGuard that checks if the input is an object with the specified property. + * @param thing - Any object. + * @param property - The name of the property that should appear in the object. + * @internal + */ +function objectHasProperty( + thing: Thing, + property: PropertyName +): thing is Thing & Record { + return typeof thing === "object" && property in (thing as Record); +} From 30b53fc51b9ef8e741e111a917069ed17758dc07 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Fri, 2 Apr 2021 04:22:43 -0700 Subject: [PATCH 43/50] tests split --- .../recording_can_add_and_get_featureflag.js | 50 +-- ...ecording_can_add_and_update_featureflag.js | 82 ++--- ...d_list_and_update_multiple_featureflags.js | 110 +++--- ...cording_can_add_and_get_secretreference.js | 42 +-- ...ding_can_add_and_update_secretreference.js | 74 ++--- ...st_and_update_multiple_secretreferences.js | 104 +++--- .../test/public/featureFlag.spec.ts | 195 +++++++++++ .../test/public/index.spec.ts | 314 +----------------- .../test/public/secretReference.spec.ts | 162 +++++++++ 9 files changed, 589 insertions(+), 544 deletions(-) rename sdk/appconfiguration/app-configuration/recordings/node/{appconfigurationclient_featureflag_configuration_setting => appconfigurationclient__featureflag_featureflag_configuration_setting}/recording_can_add_and_get_featureflag.js (74%) rename sdk/appconfiguration/app-configuration/recordings/node/{appconfigurationclient_featureflag_configuration_setting => appconfigurationclient__featureflag_featureflag_configuration_setting}/recording_can_add_and_update_featureflag.js (74%) rename sdk/appconfiguration/app-configuration/recordings/node/{appconfigurationclient_featureflag_configuration_setting => appconfigurationclient__featureflag_featureflag_configuration_setting}/recording_can_add_list_and_update_multiple_featureflags.js (76%) rename sdk/appconfiguration/app-configuration/recordings/node/{appconfigurationclient_secretreference_configuration_setting => appconfigurationclient__secretreference_secretreference_configuration_setting}/recording_can_add_and_get_secretreference.js (70%) rename sdk/appconfiguration/app-configuration/recordings/node/{appconfigurationclient_secretreference_configuration_setting => appconfigurationclient__secretreference_secretreference_configuration_setting}/recording_can_add_and_update_secretreference.js (68%) rename sdk/appconfiguration/app-configuration/recordings/node/{appconfigurationclient_secretreference_configuration_setting => appconfigurationclient__secretreference_secretreference_configuration_setting}/recording_can_add_list_and_update_multiple_secretreferences.js (68%) create mode 100644 sdk/appconfiguration/app-configuration/test/public/featureFlag.spec.ts create mode 100644 sdk/appconfiguration/app-configuration/test/public/secretReference.spec.ts diff --git a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_and_get_featureflag.js b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__featureflag_featureflag_configuration_setting/recording_can_add_and_get_featureflag.js similarity index 74% rename from sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_and_get_featureflag.js rename to sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__featureflag_featureflag_configuration_setting/recording_can_add_and_get_featureflag.js index 87fbee7f2bc1..ce51de218009 100644 --- a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_and_get_featureflag.js +++ b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__featureflag_featureflag_configuration_setting/recording_can_add_and_get_featureflag.js @@ -2,16 +2,16 @@ let nock = require('nock'); module.exports.hash = "0f3d1c1e4498c8bfdfe2a40257d26862"; -module.exports.testInfo = {"uniqueName":{"name-1":"name-1161727072887004268"},"newDate":{}} +module.exports.testInfo = {"uniqueName":{"name-1":"name-1161736249716600236"},"newDate":{}} nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .put('/kv/.appconfig.featureflag%2Fname-1161727072887004268', {"key":".appconfig.featureflag/name-1161727072887004268","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072887004268\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}"}) + .put('/kv/.appconfig.featureflag%2Fname-1161736249716600236', {"key":".appconfig.featureflag/name-1161736249716600236","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249716600236\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}"}) .query(true) - .reply(200, {"etag":"S9DTi4yqIqKbprknPA6rQfErwVn","key":".appconfig.featureflag/name-1161727072887004268","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072887004268\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:09+00:00"}, [ + .reply(200, {"etag":"L715LplZhnVkTcair5pIUmLYbrb","key":".appconfig.featureflag/name-1161736249716600236","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249716600236\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:37+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:09 GMT', + 'Fri, 02 Apr 2021 11:21:37 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -19,15 +19,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:09 GMT', + 'Fri, 02 Apr 2021 11:21:37 GMT', 'ETag', - '"S9DTi4yqIqKbprknPA6rQfErwVn"', + '"L715LplZhnVkTcair5pIUmLYbrb"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2Njg=;sn=3030668', + 'zAJw6V16=NTo1IzMwMzYwNTQ=;sn=3036054', 'x-ms-request-id', - 'f3f8fabb-f338-48e1-bb79-1f063151cb7c', + '532e0b89-15a8-401d-8cb3-15b407a102f7', 'x-ms-correlation-request-id', - 'f3f8fabb-f338-48e1-bb79-1f063151cb7c', + '532e0b89-15a8-401d-8cb3-15b407a102f7', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -39,13 +39,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .get('/kv/.appconfig.featureflag%2Fname-1161727072887004268') + .get('/kv/.appconfig.featureflag%2Fname-1161736249716600236') .query(true) - .reply(200, {"etag":"S9DTi4yqIqKbprknPA6rQfErwVn","key":".appconfig.featureflag/name-1161727072887004268","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072887004268\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:09+00:00"}, [ + .reply(200, {"etag":"L715LplZhnVkTcair5pIUmLYbrb","key":".appconfig.featureflag/name-1161736249716600236","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249716600236\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:37+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:09 GMT', + 'Fri, 02 Apr 2021 11:21:37 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -53,15 +53,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:09 GMT', + 'Fri, 02 Apr 2021 11:21:37 GMT', 'ETag', - '"S9DTi4yqIqKbprknPA6rQfErwVn"', + '"L715LplZhnVkTcair5pIUmLYbrb"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2Njg=;sn=3030668', + 'zAJw6V16=NTo1IzMwMzYwNTQ=;sn=3036054', 'x-ms-request-id', - '6063d6ea-9721-4d43-816a-75a013f02244', + 'cb0ff52f-e454-49b4-9cb5-48bd8d0336c0', 'x-ms-correlation-request-id', - '6063d6ea-9721-4d43-816a-75a013f02244', + 'cb0ff52f-e454-49b4-9cb5-48bd8d0336c0', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -73,13 +73,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .delete('/kv/.appconfig.featureflag%2Fname-1161727072887004268') + .delete('/kv/.appconfig.featureflag%2Fname-1161736249716600236') .query(true) - .reply(200, {"etag":"S9DTi4yqIqKbprknPA6rQfErwVn","key":".appconfig.featureflag/name-1161727072887004268","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072887004268\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:09+00:00"}, [ + .reply(200, {"etag":"L715LplZhnVkTcair5pIUmLYbrb","key":".appconfig.featureflag/name-1161736249716600236","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249716600236\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:37+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:10 GMT', + 'Fri, 02 Apr 2021 11:21:38 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -87,15 +87,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:09 GMT', + 'Fri, 02 Apr 2021 11:21:37 GMT', 'ETag', - '"S9DTi4yqIqKbprknPA6rQfErwVn"', + '"L715LplZhnVkTcair5pIUmLYbrb"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2Njk=;sn=3030669', + 'zAJw6V16=NTo1IzMwMzYwNTU=;sn=3036055', 'x-ms-request-id', - 'ce339958-523a-4bb9-ac99-6018d09f02f4', + '202dd25d-3be6-4b00-acd4-68bd4483d710', 'x-ms-correlation-request-id', - 'ce339958-523a-4bb9-ac99-6018d09f02f4', + '202dd25d-3be6-4b00-acd4-68bd4483d710', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', diff --git a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_and_update_featureflag.js b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__featureflag_featureflag_configuration_setting/recording_can_add_and_update_featureflag.js similarity index 74% rename from sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_and_update_featureflag.js rename to sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__featureflag_featureflag_configuration_setting/recording_can_add_and_update_featureflag.js index 0150065c126d..0590d90b408d 100644 --- a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_and_update_featureflag.js +++ b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__featureflag_featureflag_configuration_setting/recording_can_add_and_update_featureflag.js @@ -2,16 +2,16 @@ let nock = require('nock'); module.exports.hash = "3c8d196cecc4465637a5e1cf232b3075"; -module.exports.testInfo = {"uniqueName":{"name-1":"name-1161727072987507081"},"newDate":{}} +module.exports.testInfo = {"uniqueName":{"name-1":"name-1161736249818502403"},"newDate":{}} nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .put('/kv/.appconfig.featureflag%2Fname-1161727072987507081', {"key":".appconfig.featureflag/name-1161727072987507081","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072987507081\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}"}) + .put('/kv/.appconfig.featureflag%2Fname-1161736249818502403', {"key":".appconfig.featureflag/name-1161736249818502403","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249818502403\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}"}) .query(true) - .reply(200, {"etag":"t3LlroJSiLKU6eJF0loGvYQM98U","key":".appconfig.featureflag/name-1161727072987507081","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072987507081\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:10+00:00"}, [ + .reply(200, {"etag":"8we5v58XC63IpZHp58O2eL3jOju","key":".appconfig.featureflag/name-1161736249818502403","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249818502403\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:38+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:09 GMT', + 'Fri, 02 Apr 2021 11:21:37 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -19,15 +19,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:10 GMT', + 'Fri, 02 Apr 2021 11:21:38 GMT', 'ETag', - '"t3LlroJSiLKU6eJF0loGvYQM98U"', + '"8we5v58XC63IpZHp58O2eL3jOju"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2NzA=;sn=3030670', + 'zAJw6V16=NTo1IzMwMzYwNTY=;sn=3036056', 'x-ms-request-id', - 'c56708a9-7755-4187-886b-67ebb6216be4', + 'd53850c6-cf46-4324-8646-82f218463eae', 'x-ms-correlation-request-id', - 'c56708a9-7755-4187-886b-67ebb6216be4', + 'd53850c6-cf46-4324-8646-82f218463eae', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -39,13 +39,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .get('/kv/.appconfig.featureflag%2Fname-1161727072987507081') + .get('/kv/.appconfig.featureflag%2Fname-1161736249818502403') .query(true) - .reply(200, {"etag":"t3LlroJSiLKU6eJF0loGvYQM98U","key":".appconfig.featureflag/name-1161727072987507081","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072987507081\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:10+00:00"}, [ + .reply(200, {"etag":"8we5v58XC63IpZHp58O2eL3jOju","key":".appconfig.featureflag/name-1161736249818502403","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249818502403\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:38+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:10 GMT', + 'Fri, 02 Apr 2021 11:21:38 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -53,15 +53,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:10 GMT', + 'Fri, 02 Apr 2021 11:21:38 GMT', 'ETag', - '"t3LlroJSiLKU6eJF0loGvYQM98U"', + '"8we5v58XC63IpZHp58O2eL3jOju"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2NzA=;sn=3030670', + 'zAJw6V16=NTo1IzMwMzYwNTY=;sn=3036056', 'x-ms-request-id', - 'faacf86e-42c8-48a0-8b19-e8a65a3a9603', + '98015151-7f1e-478f-bc74-52f8405b2d31', 'x-ms-correlation-request-id', - 'faacf86e-42c8-48a0-8b19-e8a65a3a9603', + '98015151-7f1e-478f-bc74-52f8405b2d31', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -73,13 +73,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .put('/kv/.appconfig.featureflag%2Fname-1161727072987507081', {"key":".appconfig.featureflag/name-1161727072987507081","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072987507081\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"etag":"t3LlroJSiLKU6eJF0loGvYQM98U"}) + .put('/kv/.appconfig.featureflag%2Fname-1161736249818502403', {"key":".appconfig.featureflag/name-1161736249818502403","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249818502403\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"etag":"8we5v58XC63IpZHp58O2eL3jOju"}) .query(true) - .reply(200, {"etag":"sAsbLlwHYvXpVKkdbOHp51DAyGn","key":".appconfig.featureflag/name-1161727072987507081","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072987507081\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:10+00:00"}, [ + .reply(200, {"etag":"Ku6xNQEd3zTg0wSuhj3gmz1e72i","key":".appconfig.featureflag/name-1161736249818502403","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249818502403\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:38+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:10 GMT', + 'Fri, 02 Apr 2021 11:21:38 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -87,15 +87,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:10 GMT', + 'Fri, 02 Apr 2021 11:21:38 GMT', 'ETag', - '"sAsbLlwHYvXpVKkdbOHp51DAyGn"', + '"Ku6xNQEd3zTg0wSuhj3gmz1e72i"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2NzE=;sn=3030671', + 'zAJw6V16=NTo1IzMwMzYwNTc=;sn=3036057', 'x-ms-request-id', - '80da7901-1e8a-4668-a3dd-1f3062517871', + 'a455a51b-61a3-4a26-a651-a9228e299c30', 'x-ms-correlation-request-id', - '80da7901-1e8a-4668-a3dd-1f3062517871', + 'a455a51b-61a3-4a26-a651-a9228e299c30', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -107,13 +107,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .get('/kv/.appconfig.featureflag%2Fname-1161727072987507081') + .get('/kv/.appconfig.featureflag%2Fname-1161736249818502403') .query(true) - .reply(200, {"etag":"sAsbLlwHYvXpVKkdbOHp51DAyGn","key":".appconfig.featureflag/name-1161727072987507081","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072987507081\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:10+00:00"}, [ + .reply(200, {"etag":"Ku6xNQEd3zTg0wSuhj3gmz1e72i","key":".appconfig.featureflag/name-1161736249818502403","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249818502403\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:38+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:11 GMT', + 'Fri, 02 Apr 2021 11:21:39 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -121,15 +121,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:10 GMT', + 'Fri, 02 Apr 2021 11:21:38 GMT', 'ETag', - '"sAsbLlwHYvXpVKkdbOHp51DAyGn"', + '"Ku6xNQEd3zTg0wSuhj3gmz1e72i"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2NzE=;sn=3030671', + 'zAJw6V16=NTo1IzMwMzYwNTc=;sn=3036057', 'x-ms-request-id', - '1b619093-0e02-49fb-bc32-f18431ea8461', + '191f5a74-27a8-4bdf-828a-494cdcaf7513', 'x-ms-correlation-request-id', - '1b619093-0e02-49fb-bc32-f18431ea8461', + '191f5a74-27a8-4bdf-828a-494cdcaf7513', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -141,13 +141,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .delete('/kv/.appconfig.featureflag%2Fname-1161727072987507081') + .delete('/kv/.appconfig.featureflag%2Fname-1161736249818502403') .query(true) - .reply(200, {"etag":"sAsbLlwHYvXpVKkdbOHp51DAyGn","key":".appconfig.featureflag/name-1161727072987507081","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727072987507081\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:10+00:00"}, [ + .reply(200, {"etag":"Ku6xNQEd3zTg0wSuhj3gmz1e72i","key":".appconfig.featureflag/name-1161736249818502403","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249818502403\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:38+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:10 GMT', + 'Fri, 02 Apr 2021 11:21:38 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -155,15 +155,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:10 GMT', + 'Fri, 02 Apr 2021 11:21:38 GMT', 'ETag', - '"sAsbLlwHYvXpVKkdbOHp51DAyGn"', + '"Ku6xNQEd3zTg0wSuhj3gmz1e72i"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2NzI=;sn=3030672', + 'zAJw6V16=NTo1IzMwMzYwNTg=;sn=3036058', 'x-ms-request-id', - 'a7882374-6acb-429b-b266-3b28be239d99', + '631dd3e8-1d08-45fc-9ede-660491627951', 'x-ms-correlation-request-id', - 'a7882374-6acb-429b-b266-3b28be239d99', + '631dd3e8-1d08-45fc-9ede-660491627951', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', diff --git a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_list_and_update_multiple_featureflags.js b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__featureflag_featureflag_configuration_setting/recording_can_add_list_and_update_multiple_featureflags.js similarity index 76% rename from sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_list_and_update_multiple_featureflags.js rename to sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__featureflag_featureflag_configuration_setting/recording_can_add_list_and_update_multiple_featureflags.js index 0aaf9e8f3ff4..38772819ca63 100644 --- a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_featureflag_configuration_setting/recording_can_add_list_and_update_multiple_featureflags.js +++ b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__featureflag_featureflag_configuration_setting/recording_can_add_list_and_update_multiple_featureflags.js @@ -2,16 +2,16 @@ let nock = require('nock'); module.exports.hash = "0cc988c098bc8745cc267bc5c3792b4a"; -module.exports.testInfo = {"uniqueName":{"name-1":"name-1161727073119304174"},"newDate":{}} +module.exports.testInfo = {"uniqueName":{"name-1":"name-1161736249956207002"},"newDate":{}} nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .put('/kv/.appconfig.featureflag%2Fname-1161727073119304174', {"key":".appconfig.featureflag/name-1161727073119304174","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}"}) + .put('/kv/.appconfig.featureflag%2Fname-1161736249956207002', {"key":".appconfig.featureflag/name-1161736249956207002","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249956207002\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}"}) .query(true) - .reply(200, {"etag":"UJxMvKnVRioPzKGTYjBkA5xW6QH","key":".appconfig.featureflag/name-1161727073119304174","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:11+00:00"}, [ + .reply(200, {"etag":"YzAo7BoElnaZyBpV7qQTmHF6yAL","key":".appconfig.featureflag/name-1161736249956207002","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249956207002\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:39+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:11 GMT', + 'Fri, 02 Apr 2021 11:21:39 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -19,15 +19,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:11 GMT', + 'Fri, 02 Apr 2021 11:21:39 GMT', 'ETag', - '"UJxMvKnVRioPzKGTYjBkA5xW6QH"', + '"YzAo7BoElnaZyBpV7qQTmHF6yAL"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2NzM=;sn=3030673', + 'zAJw6V16=NTo1IzMwMzYwNTk=;sn=3036059', 'x-ms-request-id', - 'abfd28e5-076f-4981-956f-8f3d41167263', + '78a8d14e-50d8-4236-8bf2-6e84e1309fe6', 'x-ms-correlation-request-id', - 'abfd28e5-076f-4981-956f-8f3d41167263', + '78a8d14e-50d8-4236-8bf2-6e84e1309fe6', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -39,13 +39,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .put('/kv/.appconfig.featureflag%2Fname-1161727073119304174-2', {"key":".appconfig.featureflag/name-1161727073119304174-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174-2\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}"}) + .put('/kv/.appconfig.featureflag%2Fname-1161736249956207002-2', {"key":".appconfig.featureflag/name-1161736249956207002-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249956207002-2\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}"}) .query(true) - .reply(200, {"etag":"ad1cici6qLxChztqw22lEjHUZ1T","key":".appconfig.featureflag/name-1161727073119304174-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174-2\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:11+00:00"}, [ + .reply(200, {"etag":"h7SXoBgaTaRLWZ7bvuIbwcl5OcZ","key":".appconfig.featureflag/name-1161736249956207002-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249956207002-2\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:39+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:11 GMT', + 'Fri, 02 Apr 2021 11:21:40 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -53,15 +53,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:11 GMT', + 'Fri, 02 Apr 2021 11:21:39 GMT', 'ETag', - '"ad1cici6qLxChztqw22lEjHUZ1T"', + '"h7SXoBgaTaRLWZ7bvuIbwcl5OcZ"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2NzQ=;sn=3030674', + 'zAJw6V16=NTo1IzMwMzYwNjA=;sn=3036060', 'x-ms-request-id', - '6397af60-9432-4352-a22b-86d7b2f520a2', + '2cbf20e5-12ee-4b11-b50b-07ef6b0b1f79', 'x-ms-correlation-request-id', - '6397af60-9432-4352-a22b-86d7b2f520a2', + '2cbf20e5-12ee-4b11-b50b-07ef6b0b1f79', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -75,11 +75,11 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) .get('/kv') .query(true) - .reply(200, {"items":[{"etag":"UJxMvKnVRioPzKGTYjBkA5xW6QH","key":".appconfig.featureflag/name-1161727073119304174","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:11+00:00"},{"etag":"ad1cici6qLxChztqw22lEjHUZ1T","key":".appconfig.featureflag/name-1161727073119304174-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174-2\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:11+00:00"}]}, [ + .reply(200, {"items":[{"etag":"YzAo7BoElnaZyBpV7qQTmHF6yAL","key":".appconfig.featureflag/name-1161736249956207002","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249956207002\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:39+00:00"},{"etag":"h7SXoBgaTaRLWZ7bvuIbwcl5OcZ","key":".appconfig.featureflag/name-1161736249956207002-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249956207002-2\",\"description\":\"I'm a description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:39+00:00"}]}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:12 GMT', + 'Fri, 02 Apr 2021 11:21:39 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kvset+json; charset=utf-8', 'Transfer-Encoding', @@ -87,11 +87,11 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2NzQ=;sn=3030674', + 'zAJw6V16=NTo1IzMwMzYwNjA=;sn=3036060', 'x-ms-request-id', - '3e52e20f-d451-40aa-b4ba-7bf99bab869a', + 'dfc4296a-5baa-4850-9ddb-6989f1df1f15', 'x-ms-correlation-request-id', - '3e52e20f-d451-40aa-b4ba-7bf99bab869a', + 'dfc4296a-5baa-4850-9ddb-6989f1df1f15', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -103,13 +103,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .put('/kv/.appconfig.featureflag%2Fname-1161727073119304174', {"key":".appconfig.featureflag/name-1161727073119304174","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}"}) + .put('/kv/.appconfig.featureflag%2Fname-1161736249956207002', {"key":".appconfig.featureflag/name-1161736249956207002","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249956207002\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}"}) .query(true) - .reply(200, {"etag":"ZrLg0196ACVWsBbsRkvIMmlK6od","key":".appconfig.featureflag/name-1161727073119304174","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:12+00:00"}, [ + .reply(200, {"etag":"YOzTU1fuA9vA5EBTNPsM1sKe0DA","key":".appconfig.featureflag/name-1161736249956207002","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249956207002\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:40+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:12 GMT', + 'Fri, 02 Apr 2021 11:21:40 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -117,15 +117,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:12 GMT', + 'Fri, 02 Apr 2021 11:21:40 GMT', 'ETag', - '"ZrLg0196ACVWsBbsRkvIMmlK6od"', + '"YOzTU1fuA9vA5EBTNPsM1sKe0DA"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2NzU=;sn=3030675', + 'zAJw6V16=NTo1IzMwMzYwNjE=;sn=3036061', 'x-ms-request-id', - '49a795f4-0c13-4a3d-87c0-b18eabdb8ce0', + '0cf64fa0-67a2-4300-bfc3-321fd139b693', 'x-ms-correlation-request-id', - '49a795f4-0c13-4a3d-87c0-b18eabdb8ce0', + '0cf64fa0-67a2-4300-bfc3-321fd139b693', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -137,13 +137,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .put('/kv/.appconfig.featureflag%2Fname-1161727073119304174-2', {"key":".appconfig.featureflag/name-1161727073119304174-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174-2\",\"description\":\"I'm new description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"etag":"ad1cici6qLxChztqw22lEjHUZ1T"}) + .put('/kv/.appconfig.featureflag%2Fname-1161736249956207002-2', {"key":".appconfig.featureflag/name-1161736249956207002-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249956207002-2\",\"description\":\"I'm new description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"etag":"h7SXoBgaTaRLWZ7bvuIbwcl5OcZ"}) .query(true) - .reply(200, {"etag":"fryVmYDYh85sVGDXYhwlV6K8YxY","key":".appconfig.featureflag/name-1161727073119304174-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174-2\",\"description\":\"I'm new description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:12+00:00"}, [ + .reply(200, {"etag":"no786ahiSoamdZXNcws7U5ttPOc","key":".appconfig.featureflag/name-1161736249956207002-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249956207002-2\",\"description\":\"I'm new description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:40+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:12 GMT', + 'Fri, 02 Apr 2021 11:21:40 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -151,15 +151,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:12 GMT', + 'Fri, 02 Apr 2021 11:21:40 GMT', 'ETag', - '"fryVmYDYh85sVGDXYhwlV6K8YxY"', + '"no786ahiSoamdZXNcws7U5ttPOc"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2NzY=;sn=3030676', + 'zAJw6V16=NTo1IzMwMzYwNjI=;sn=3036062', 'x-ms-request-id', - 'ae095328-7f09-46c1-b9dc-c74283e140e1', + 'e695682e-dd93-49cf-812d-ab5cdef12c54', 'x-ms-correlation-request-id', - 'ae095328-7f09-46c1-b9dc-c74283e140e1', + 'e695682e-dd93-49cf-812d-ab5cdef12c54', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -173,11 +173,11 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) .get('/kv') .query(true) - .reply(200, {"items":[{"etag":"ZrLg0196ACVWsBbsRkvIMmlK6od","key":".appconfig.featureflag/name-1161727073119304174","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:12+00:00"},{"etag":"fryVmYDYh85sVGDXYhwlV6K8YxY","key":".appconfig.featureflag/name-1161727073119304174-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174-2\",\"description\":\"I'm new description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:12+00:00"}]}, [ + .reply(200, {"items":[{"etag":"YOzTU1fuA9vA5EBTNPsM1sKe0DA","key":".appconfig.featureflag/name-1161736249956207002","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249956207002\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:40+00:00"},{"etag":"no786ahiSoamdZXNcws7U5ttPOc","key":".appconfig.featureflag/name-1161736249956207002-2","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249956207002-2\",\"description\":\"I'm new description\",\"enabled\":false,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:40+00:00"}]}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:12 GMT', + 'Fri, 02 Apr 2021 11:21:40 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kvset+json; charset=utf-8', 'Transfer-Encoding', @@ -185,11 +185,11 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2NzY=;sn=3030676', + 'zAJw6V16=NTo1IzMwMzYwNjI=;sn=3036062', 'x-ms-request-id', - 'd433f831-bafd-4b5b-8d08-10ddc63e61db', + '8ca384a5-c490-4e69-a145-712d63f403e1', 'x-ms-correlation-request-id', - 'd433f831-bafd-4b5b-8d08-10ddc63e61db', + '8ca384a5-c490-4e69-a145-712d63f403e1', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -201,21 +201,21 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .delete('/kv/.appconfig.featureflag%2Fname-1161727073119304174-2') + .delete('/kv/.appconfig.featureflag%2Fname-1161736249956207002-2') .query(true) .reply(204, "", [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:13 GMT', + 'Fri, 02 Apr 2021 11:21:41 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Connection', 'close', 'x-ms-request-id', - 'f78a9af3-4f0e-41cb-839b-0628840e6c3d', + '54c5dc4c-4e3e-45e8-9db0-4f4113906bad', 'x-ms-correlation-request-id', - 'f78a9af3-4f0e-41cb-839b-0628840e6c3d', + '54c5dc4c-4e3e-45e8-9db0-4f4113906bad', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -227,13 +227,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .delete('/kv/.appconfig.featureflag%2Fname-1161727073119304174') + .delete('/kv/.appconfig.featureflag%2Fname-1161736249956207002') .query(true) - .reply(200, {"etag":"ZrLg0196ACVWsBbsRkvIMmlK6od","key":".appconfig.featureflag/name-1161727073119304174","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161727073119304174\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:12+00:00"}, [ + .reply(200, {"etag":"YOzTU1fuA9vA5EBTNPsM1sKe0DA","key":".appconfig.featureflag/name-1161736249956207002","label":"label-1","content_type":"application/vnd.microsoft.appconfig.ff+json;charset=utf-8","value":"{\"id\":\"name-1161736249956207002\",\"description\":\"I'm a description\",\"enabled\":true,\"conditions\":{\"client_filters\":[{\"name\":\"Microsoft.TimeWindow\",\"parameters\":{\"Start\":\"Wed, 01 May 2019 13:59:59 GMT\",\"End\":\"Mon, 01 July 2019 00:00:00 GMT\"}},{\"name\":\"FilterX\"},{\"name\":\"Microsoft.Targeting\",\"parameters\":{\"Audience\":{\"Groups\":[{\"Name\":\"group-1\",\"RolloutPercentage\":25},{\"Name\":\"group-2\",\"RolloutPercentage\":45}],\"Users\":[\"userA\",\"userB\"],\"DefaultRolloutPercentage\":40}}},{\"name\":\"Microsoft.Percentage\",\"parameters\":{\"Value\":25}}]}}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:40+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:13 GMT', + 'Fri, 02 Apr 2021 11:21:41 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -241,15 +241,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:12 GMT', + 'Fri, 02 Apr 2021 11:21:40 GMT', 'ETag', - '"ZrLg0196ACVWsBbsRkvIMmlK6od"', + '"YOzTU1fuA9vA5EBTNPsM1sKe0DA"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2Nzc=;sn=3030677', + 'zAJw6V16=NTo1IzMwMzYwNjM=;sn=3036063', 'x-ms-request-id', - '9dbe622b-92cc-4255-aabc-522a60c9b9fb', + '77b1a36f-1ad3-4ad7-b18d-f613b2fa6df4', 'x-ms-correlation-request-id', - '9dbe622b-92cc-4255-aabc-522a60c9b9fb', + '77b1a36f-1ad3-4ad7-b18d-f613b2fa6df4', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', diff --git a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_and_get_secretreference.js b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__secretreference_secretreference_configuration_setting/recording_can_add_and_get_secretreference.js similarity index 70% rename from sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_and_get_secretreference.js rename to sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__secretreference_secretreference_configuration_setting/recording_can_add_and_get_secretreference.js index 20fcf80b58e9..539648991e20 100644 --- a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_and_get_secretreference.js +++ b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__secretreference_secretreference_configuration_setting/recording_can_add_and_get_secretreference.js @@ -2,16 +2,16 @@ let nock = require('nock'); module.exports.hash = "505c9a3a224a00ff0b9a9c06eefae9e4"; -module.exports.testInfo = {"uniqueName":{"name-2":"name-2161727073344802614","name-3":"name-3161727073344802596"},"newDate":{}} +module.exports.testInfo = {"uniqueName":{"name-2":"name-2161736250170700590","name-3":"name-3161736250170707837"},"newDate":{}} nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .put('/kv/name-3161727073344802596', {"key":"name-3161727073344802596","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073344802614\"}"}) + .put('/kv/name-3161736250170707837', {"key":"name-3161736250170707837","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161736250170700590\"}"}) .query(true) - .reply(200, {"etag":"2pCLRTH84Qyodv6bzHnpAyjHJls","key":"name-3161727073344802596","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073344802614\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:13+00:00"}, [ + .reply(200, {"etag":"wpeiKUlqzHn9DLVqMgvjq33XULc","key":"name-3161736250170707837","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161736250170700590\"}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:41+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:14 GMT', + 'Fri, 02 Apr 2021 11:21:41 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -19,15 +19,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:13 GMT', + 'Fri, 02 Apr 2021 11:21:41 GMT', 'ETag', - '"2pCLRTH84Qyodv6bzHnpAyjHJls"', + '"wpeiKUlqzHn9DLVqMgvjq33XULc"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2Nzg=;sn=3030678', + 'zAJw6V16=NTo1IzMwMzYwNjQ=;sn=3036064', 'x-ms-request-id', - 'cc1396e6-f3eb-4f90-8ea8-5d66173e87cc', + 'fe55b3c8-d6b5-438a-8d68-8fbc30f41358', 'x-ms-correlation-request-id', - 'cc1396e6-f3eb-4f90-8ea8-5d66173e87cc', + 'fe55b3c8-d6b5-438a-8d68-8fbc30f41358', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -39,13 +39,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .get('/kv/name-3161727073344802596') + .get('/kv/name-3161736250170707837') .query(true) - .reply(200, {"etag":"2pCLRTH84Qyodv6bzHnpAyjHJls","key":"name-3161727073344802596","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073344802614\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:13+00:00"}, [ + .reply(200, {"etag":"wpeiKUlqzHn9DLVqMgvjq33XULc","key":"name-3161736250170707837","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161736250170700590\"}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:41+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:13 GMT', + 'Fri, 02 Apr 2021 11:21:41 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -53,15 +53,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:13 GMT', + 'Fri, 02 Apr 2021 11:21:41 GMT', 'ETag', - '"2pCLRTH84Qyodv6bzHnpAyjHJls"', + '"wpeiKUlqzHn9DLVqMgvjq33XULc"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2Nzg=;sn=3030678', + 'zAJw6V16=NTo1IzMwMzYwNjQ=;sn=3036064', 'x-ms-request-id', - '5effd0c1-3b28-4b65-b111-ad302151477f', + '86de76bd-ce5c-46ea-bd40-1041c468815f', 'x-ms-correlation-request-id', - '5effd0c1-3b28-4b65-b111-ad302151477f', + '86de76bd-ce5c-46ea-bd40-1041c468815f', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -73,21 +73,21 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .delete('/kv/name-3161727073344802596') + .delete('/kv/name-3161736250170707837') .query(true) .reply(204, "", [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:14 GMT', + 'Fri, 02 Apr 2021 11:21:42 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Connection', 'close', 'x-ms-request-id', - '95903e9d-ae5d-4c02-9711-86412696f7c7', + 'ed499149-036a-4f00-9b3d-03fefae15bcb', 'x-ms-correlation-request-id', - '95903e9d-ae5d-4c02-9711-86412696f7c7', + 'ed499149-036a-4f00-9b3d-03fefae15bcb', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', diff --git a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_and_update_secretreference.js b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__secretreference_secretreference_configuration_setting/recording_can_add_and_update_secretreference.js similarity index 68% rename from sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_and_update_secretreference.js rename to sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__secretreference_secretreference_configuration_setting/recording_can_add_and_update_secretreference.js index 068f6c0cdf4a..80cad4d1bf62 100644 --- a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_and_update_secretreference.js +++ b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__secretreference_secretreference_configuration_setting/recording_can_add_and_update_secretreference.js @@ -2,16 +2,16 @@ let nock = require('nock'); module.exports.hash = "a9957c7b9cfbfcbbecbe8e53ed0fb850"; -module.exports.testInfo = {"uniqueName":{"name-2":"name-2161727073431701823","name-3":"name-3161727073431701524","name-4":"name-4161727073488106751"},"newDate":{}} +module.exports.testInfo = {"uniqueName":{"name-2":"name-2161736250258906310","name-3":"name-3161736250258904150","name-4":"name-4161736250310202956"},"newDate":{}} nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .put('/kv/name-3161727073431701524', {"key":"name-3161727073431701524","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073431701823\"}"}) + .put('/kv/name-3161736250258904150', {"key":"name-3161736250258904150","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161736250258906310\"}"}) .query(true) - .reply(200, {"etag":"9clx3p52mLOybg5yELR0OdyOkBG","key":"name-3161727073431701524","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073431701823\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:14+00:00"}, [ + .reply(200, {"etag":"SoYpTkSGhqAsCyxYoISyPDqIU6z","key":"name-3161736250258904150","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161736250258906310\"}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:42+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:14 GMT', + 'Fri, 02 Apr 2021 11:21:42 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -19,15 +19,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:14 GMT', + 'Fri, 02 Apr 2021 11:21:42 GMT', 'ETag', - '"9clx3p52mLOybg5yELR0OdyOkBG"', + '"SoYpTkSGhqAsCyxYoISyPDqIU6z"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2Nzk=;sn=3030679', + 'zAJw6V16=NTo1IzMwMzYwNjU=;sn=3036065', 'x-ms-request-id', - '37a33f1b-6668-42a6-a90d-bb9b9e9c40ad', + '9c406cb9-2a8f-4d92-a3c1-0849ec71adb1', 'x-ms-correlation-request-id', - '37a33f1b-6668-42a6-a90d-bb9b9e9c40ad', + '9c406cb9-2a8f-4d92-a3c1-0849ec71adb1', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -39,13 +39,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .get('/kv/name-3161727073431701524') + .get('/kv/name-3161736250258904150') .query(true) - .reply(200, {"etag":"9clx3p52mLOybg5yELR0OdyOkBG","key":"name-3161727073431701524","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073431701823\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:14+00:00"}, [ + .reply(200, {"etag":"SoYpTkSGhqAsCyxYoISyPDqIU6z","key":"name-3161736250258904150","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161736250258906310\"}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:42+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:15 GMT', + 'Fri, 02 Apr 2021 11:21:43 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -53,15 +53,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:14 GMT', + 'Fri, 02 Apr 2021 11:21:42 GMT', 'ETag', - '"9clx3p52mLOybg5yELR0OdyOkBG"', + '"SoYpTkSGhqAsCyxYoISyPDqIU6z"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2Nzk=;sn=3030679', + 'zAJw6V16=NTo1IzMwMzYwNjU=;sn=3036065', 'x-ms-request-id', - '8dadfbb9-f162-4509-b55f-c11d1877d523', + '722436bc-c235-4319-b382-16f10958f49d', 'x-ms-correlation-request-id', - '8dadfbb9-f162-4509-b55f-c11d1877d523', + '722436bc-c235-4319-b382-16f10958f49d', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -73,13 +73,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .put('/kv/name-3161727073431701524', {"key":"name-3161727073431701524","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-4161727073488106751\"}","tags":{},"etag":"9clx3p52mLOybg5yELR0OdyOkBG"}) + .put('/kv/name-3161736250258904150', {"key":"name-3161736250258904150","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-4161736250310202956\"}","tags":{},"etag":"SoYpTkSGhqAsCyxYoISyPDqIU6z"}) .query(true) - .reply(200, {"etag":"r8HpOzCxIibrgWWdgvRqaHzmf2A","key":"name-3161727073431701524","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-4161727073488106751\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:15+00:00"}, [ + .reply(200, {"etag":"o9IWxpqj3axCY3cbOS5NUOODX9g","key":"name-3161736250258904150","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-4161736250310202956\"}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:42+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:14 GMT', + 'Fri, 02 Apr 2021 11:21:42 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -87,15 +87,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:15 GMT', + 'Fri, 02 Apr 2021 11:21:42 GMT', 'ETag', - '"r8HpOzCxIibrgWWdgvRqaHzmf2A"', + '"o9IWxpqj3axCY3cbOS5NUOODX9g"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2ODA=;sn=3030680', + 'zAJw6V16=NTo1IzMwMzYwNjY=;sn=3036066', 'x-ms-request-id', - '6d3bca69-b745-4c8d-97bd-b05869735683', + 'b5ca320b-fab4-44d4-81bd-418f88e4b2fd', 'x-ms-correlation-request-id', - '6d3bca69-b745-4c8d-97bd-b05869735683', + 'b5ca320b-fab4-44d4-81bd-418f88e4b2fd', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -107,13 +107,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .get('/kv/name-3161727073431701524') + .get('/kv/name-3161736250258904150') .query(true) - .reply(200, {"etag":"r8HpOzCxIibrgWWdgvRqaHzmf2A","key":"name-3161727073431701524","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-4161727073488106751\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:15+00:00"}, [ + .reply(200, {"etag":"o9IWxpqj3axCY3cbOS5NUOODX9g","key":"name-3161736250258904150","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-4161736250310202956\"}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:42+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:15 GMT', + 'Fri, 02 Apr 2021 11:21:43 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -121,15 +121,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:15 GMT', + 'Fri, 02 Apr 2021 11:21:42 GMT', 'ETag', - '"r8HpOzCxIibrgWWdgvRqaHzmf2A"', + '"o9IWxpqj3axCY3cbOS5NUOODX9g"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2ODA=;sn=3030680', + 'zAJw6V16=NTo1IzMwMzYwNjY=;sn=3036066', 'x-ms-request-id', - '197d7ee7-2252-460f-ba6b-5632f4c26e8f', + '79664871-8f36-4967-b984-9040b1e9dfe5', 'x-ms-correlation-request-id', - '197d7ee7-2252-460f-ba6b-5632f4c26e8f', + '79664871-8f36-4967-b984-9040b1e9dfe5', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -141,21 +141,21 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .delete('/kv/name-3161727073431701524') + .delete('/kv/name-3161736250258904150') .query(true) .reply(204, "", [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:15 GMT', + 'Fri, 02 Apr 2021 11:21:43 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Connection', 'close', 'x-ms-request-id', - '2f97529e-7257-4499-9be3-3be9eea1b96c', + '8ca78901-1834-4236-8d1d-a8aaf32f1282', 'x-ms-correlation-request-id', - '2f97529e-7257-4499-9be3-3be9eea1b96c', + '8ca78901-1834-4236-8d1d-a8aaf32f1282', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', diff --git a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_list_and_update_multiple_secretreferences.js b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__secretreference_secretreference_configuration_setting/recording_can_add_list_and_update_multiple_secretreferences.js similarity index 68% rename from sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_list_and_update_multiple_secretreferences.js rename to sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__secretreference_secretreference_configuration_setting/recording_can_add_list_and_update_multiple_secretreferences.js index aa626cd43e28..eaccd61788e4 100644 --- a/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient_secretreference_configuration_setting/recording_can_add_list_and_update_multiple_secretreferences.js +++ b/sdk/appconfiguration/app-configuration/recordings/node/appconfigurationclient__secretreference_secretreference_configuration_setting/recording_can_add_list_and_update_multiple_secretreferences.js @@ -1,17 +1,17 @@ let nock = require('nock'); -module.exports.hash = "b4a0c9bd7dbaf880005fc7e37af01169"; +module.exports.hash = "6d49fc86f35574cb53bce8021e5755f4"; -module.exports.testInfo = {"uniqueName":{"name-2":"name-2161727073573502374","name-3":"name-3161727073573502979","name-5":"name-5161727073599408784"},"newDate":{}} +module.exports.testInfo = {"uniqueName":{"name-2":"name-2161736250391906013","name-3":"name-3161736250391900706","name-5":"name-5161736250422907435"},"newDate":{}} nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .put('/kv/name-3161727073573502979', {"key":"name-3161727073573502979","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}"}) + .put('/kv/name-3161736250391900706', {"key":"name-3161736250391900706","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161736250391906013\"}"}) .query(true) - .reply(200, {"etag":"I9YupYSj1oJt9iMGQ9182qHQH9u","key":"name-3161727073573502979","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:16+00:00"}, [ + .reply(200, {"etag":"oHhTHIwOX8S2SqoiGdkvyl8mpAt","key":"name-3161736250391900706","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161736250391906013\"}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:43+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:16 GMT', + 'Fri, 02 Apr 2021 11:21:44 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -19,15 +19,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:16 GMT', + 'Fri, 02 Apr 2021 11:21:43 GMT', 'ETag', - '"I9YupYSj1oJt9iMGQ9182qHQH9u"', + '"oHhTHIwOX8S2SqoiGdkvyl8mpAt"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2ODE=;sn=3030681', + 'zAJw6V16=NTo1IzMwMzYwNjc=;sn=3036067', 'x-ms-request-id', - '75fa4b1c-e25d-4543-a3f9-f0c96303ff56', + '9335a747-8814-479e-aa92-92d2d08b0ece', 'x-ms-correlation-request-id', - '75fa4b1c-e25d-4543-a3f9-f0c96303ff56', + '9335a747-8814-479e-aa92-92d2d08b0ece', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -39,13 +39,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .put('/kv/name-3161727073573502979-2', {"key":"name-3161727073573502979-2","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}"}) + .put('/kv/name-3161736250391900706-2', {"key":"name-3161736250391900706-2","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161736250391906013\"}"}) .query(true) - .reply(200, {"etag":"F6Xhn8XTexGUi19MlmuYgMqZFIY","key":"name-3161727073573502979-2","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:16+00:00"}, [ + .reply(200, {"etag":"PR20Km4VgoXYQ6tap64aZ9ZvgvV","key":"name-3161736250391900706-2","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161736250391906013\"}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:44+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:15 GMT', + 'Fri, 02 Apr 2021 11:21:43 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -53,15 +53,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:16 GMT', + 'Fri, 02 Apr 2021 11:21:44 GMT', 'ETag', - '"F6Xhn8XTexGUi19MlmuYgMqZFIY"', + '"PR20Km4VgoXYQ6tap64aZ9ZvgvV"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2ODI=;sn=3030682', + 'zAJw6V16=NTo1IzMwMzYwNjg=;sn=3036068', 'x-ms-request-id', - '4b3a346b-dc79-4b6d-9c0d-7583041ec800', + '2d5c435d-4fc5-4171-a4ed-a61f5b28ab0f', 'x-ms-correlation-request-id', - '4b3a346b-dc79-4b6d-9c0d-7583041ec800', + '2d5c435d-4fc5-4171-a4ed-a61f5b28ab0f', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -75,11 +75,11 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) .get('/kv') .query(true) - .reply(200, {"items":[{"etag":"I9YupYSj1oJt9iMGQ9182qHQH9u","key":"name-3161727073573502979","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:16+00:00"},{"etag":"F6Xhn8XTexGUi19MlmuYgMqZFIY","key":"name-3161727073573502979-2","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:16+00:00"}]}, [ + .reply(200, {"items":[{"etag":"oHhTHIwOX8S2SqoiGdkvyl8mpAt","key":"name-3161736250391900706","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161736250391906013\"}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:43+00:00"},{"etag":"PR20Km4VgoXYQ6tap64aZ9ZvgvV","key":"name-3161736250391900706-2","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161736250391906013\"}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:44+00:00"}]}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:16 GMT', + 'Fri, 02 Apr 2021 11:21:44 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kvset+json; charset=utf-8', 'Transfer-Encoding', @@ -87,11 +87,11 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2ODI=;sn=3030682', + 'zAJw6V16=NTo1IzMwMzYwNjg=;sn=3036068', 'x-ms-request-id', - '4120b416-ba57-4beb-8ae2-8a930a732205', + '65b35dc2-f741-402a-8492-5312ceb4209d', 'x-ms-correlation-request-id', - '4120b416-ba57-4beb-8ae2-8a930a732205', + '65b35dc2-f741-402a-8492-5312ceb4209d', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -103,13 +103,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .put('/kv/name-3161727073573502979', {"key":"name-3161727073573502979","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-5161727073599408784\"}"}) + .put('/kv/name-3161736250391900706', {"key":"name-3161736250391900706","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-5161736250422907435\"}"}) .query(true) - .reply(200, {"etag":"Qrc2lygn505tdOc9ZlUXv1G7x7h","key":"name-3161727073573502979","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-5161727073599408784\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:16+00:00"}, [ + .reply(200, {"etag":"aU5a3tU2BQ6FOX2F4XMsjfjUEWT","key":"name-3161736250391900706","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-5161736250422907435\"}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:44+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:17 GMT', + 'Fri, 02 Apr 2021 11:21:44 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -117,15 +117,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:16 GMT', + 'Fri, 02 Apr 2021 11:21:44 GMT', 'ETag', - '"Qrc2lygn505tdOc9ZlUXv1G7x7h"', + '"aU5a3tU2BQ6FOX2F4XMsjfjUEWT"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2ODM=;sn=3030683', + 'zAJw6V16=NTo1IzMwMzYwNjk=;sn=3036069', 'x-ms-request-id', - '234cc4db-4bd4-402c-82b3-5a8af51f3ff2', + '74f3434b-25a8-4b0f-b77a-ea0fd06da33f', 'x-ms-correlation-request-id', - '234cc4db-4bd4-402c-82b3-5a8af51f3ff2', + '74f3434b-25a8-4b0f-b77a-ea0fd06da33f', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -137,13 +137,13 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .put('/locks/name-3161727073573502979-2') + .put('/locks/name-3161736250391900706-2') .query(true) - .reply(200, {"etag":"fcQO0dSZGtVEanPOL8sUScVF731","key":"name-3161727073573502979-2","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}","tags":{},"locked":true,"last_modified":"2021-04-01T09:52:17+00:00"}, [ + .reply(200, {"etag":"utHqq8e86dQhoef4XjvhgRiz24P","key":"name-3161736250391900706-2","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161736250391906013\"}","tags":{},"locked":true,"last_modified":"2021-04-02T11:21:46+00:00"}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:16 GMT', + 'Fri, 02 Apr 2021 11:21:46 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Transfer-Encoding', @@ -151,15 +151,15 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Last-Modified', - 'Thu, 01 Apr 2021 09:52:17 GMT', + 'Fri, 02 Apr 2021 11:21:46 GMT', 'ETag', - '"fcQO0dSZGtVEanPOL8sUScVF731"', + '"utHqq8e86dQhoef4XjvhgRiz24P"', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2ODQ=;sn=3030684', + 'zAJw6V16=NTo1IzMwMzYwNzA=;sn=3036070', 'x-ms-request-id', - '45b0802a-9f31-43a0-8a35-abd8b2c9a28a', + 'f9b99f38-1c28-4e00-b16b-6fd4022dedae', 'x-ms-correlation-request-id', - '45b0802a-9f31-43a0-8a35-abd8b2c9a28a', + 'f9b99f38-1c28-4e00-b16b-6fd4022dedae', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -173,11 +173,11 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) .get('/kv') .query(true) - .reply(200, {"items":[{"etag":"Qrc2lygn505tdOc9ZlUXv1G7x7h","key":"name-3161727073573502979","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-5161727073599408784\"}","tags":{},"locked":false,"last_modified":"2021-04-01T09:52:16+00:00"},{"etag":"fcQO0dSZGtVEanPOL8sUScVF731","key":"name-3161727073573502979-2","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161727073573502374\"}","tags":{},"locked":true,"last_modified":"2021-04-01T09:52:17+00:00"}]}, [ + .reply(200, {"items":[{"etag":"aU5a3tU2BQ6FOX2F4XMsjfjUEWT","key":"name-3161736250391900706","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-5161736250422907435\"}","tags":{},"locked":false,"last_modified":"2021-04-02T11:21:44+00:00"},{"etag":"utHqq8e86dQhoef4XjvhgRiz24P","key":"name-3161736250391900706-2","label":"label-s","content_type":"application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8","value":"{\"uri\":\"https://vault_name.vault.azure.net/secrets/name-2161736250391906013\"}","tags":{},"locked":true,"last_modified":"2021-04-02T11:21:46+00:00"}]}, [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:17 GMT', + 'Fri, 02 Apr 2021 11:21:46 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kvset+json; charset=utf-8', 'Transfer-Encoding', @@ -185,11 +185,11 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) 'Connection', 'close', 'Sync-Token', - 'zAJw6V16=NTo1IzMwMzA2ODQ=;sn=3030684', + 'zAJw6V16=NTo1IzMwMzYwNzA=;sn=3036070', 'x-ms-request-id', - '321e5b1e-385c-4366-9052-ab2ea3b4c6ad', + 'cdbb73d9-4529-4775-b6ac-e7cc149bded8', 'x-ms-correlation-request-id', - '321e5b1e-385c-4366-9052-ab2ea3b4c6ad', + 'cdbb73d9-4529-4775-b6ac-e7cc149bded8', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -201,21 +201,21 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .delete('/kv/name-3161727073573502979-2') + .delete('/kv/name-3161736250391900706-2') .query(true) .reply(204, "", [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:17 GMT', + 'Fri, 02 Apr 2021 11:21:47 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Connection', 'close', 'x-ms-request-id', - 'c49af6ea-1247-4dd0-a4c5-7f14e9036ea1', + 'caf4721a-da0c-4df6-964a-2bd024935eed', 'x-ms-correlation-request-id', - 'c49af6ea-1247-4dd0-a4c5-7f14e9036ea1', + 'caf4721a-da0c-4df6-964a-2bd024935eed', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', @@ -227,21 +227,21 @@ nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) ]); nock('https://myappconfig.azconfig.io:443', {"encodedQueryParams":true}) - .delete('/kv/name-3161727073573502979') + .delete('/kv/name-3161736250391900706') .query(true) .reply(204, "", [ 'Server', 'openresty/1.17.8.2', 'Date', - 'Thu, 01 Apr 2021 09:52:18 GMT', + 'Fri, 02 Apr 2021 11:21:46 GMT', 'Content-Type', 'application/vnd.microsoft.appconfig.kv+json; charset=utf-8', 'Connection', 'close', 'x-ms-request-id', - 'affb9d48-91fa-4d8f-bfdc-edd7dbfd5319', + 'd13d6103-9496-439f-ad4c-f252717874bf', 'x-ms-correlation-request-id', - 'affb9d48-91fa-4d8f-bfdc-edd7dbfd5319', + 'd13d6103-9496-439f-ad4c-f252717874bf', 'Access-Control-Allow-Origin', '*', 'Access-Control-Allow-Credentials', diff --git a/sdk/appconfiguration/app-configuration/test/public/featureFlag.spec.ts b/sdk/appconfiguration/app-configuration/test/public/featureFlag.spec.ts new file mode 100644 index 000000000000..437695270533 --- /dev/null +++ b/sdk/appconfiguration/app-configuration/test/public/featureFlag.spec.ts @@ -0,0 +1,195 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { assert } from "chai"; +import { createAppConfigurationClientForTests, startRecorder } from "./utils/testHelpers"; +import { + AddConfigurationSettingResponse, + AppConfigurationClient, + FeatureFlag, + featureFlagContentType, + FeatureFlagPercentageClientFilter, + featureFlagPrefix, + FeatureFlagTargetingClientFilter, + FeatureFlagTimeWindowClientFilter, + isFeatureFlag +} from "../../src"; +import { Recorder } from "@azure/test-utils-recorder"; +import { Context } from "mocha"; + +describe("AppConfigurationClient - FeatureFlag", () => { + let client: AppConfigurationClient; + let recorder: Recorder; + + beforeEach(function(this: Context) { + recorder = startRecorder(this); + client = createAppConfigurationClientForTests() || this.skip(); + }); + + afterEach(async function(this: Context) { + await recorder.stop(); + }); + + describe("FeatureFlag configuration setting", () => { + const clientFilters: ( + | Record + | FeatureFlagTargetingClientFilter + | FeatureFlagTimeWindowClientFilter + | FeatureFlagPercentageClientFilter + )[] = [ + { + name: "Microsoft.TimeWindow", + parameters: { + start: "Wed, 01 May 2019 13:59:59 GMT", + end: "Mon, 01 July 2019 00:00:00 GMT" + } + }, + { name: "FilterX" }, + { + name: "Microsoft.Targeting", + parameters: { + audience: { + groups: [ + { name: "group-1", rolloutPercentage: 25 }, + { name: "group-2", rolloutPercentage: 45 } + ], + users: ["userA", "userB"], + defaultRolloutPercentage: 40 + } + } + }, + { name: "Microsoft.Percentage", parameters: { value: 25 } } + ]; + + let baseSetting: FeatureFlag; + let addResponse: AddConfigurationSettingResponse; + + beforeEach(async () => { + baseSetting = { + conditions: { + clientFilters + }, + enabled: false, + isReadOnly: false, + key: `${featureFlagPrefix + recorder.getUniqueName("name-1")}`, + contentType: featureFlagContentType, + description: "I'm a description", + label: "label-1" + }; + addResponse = await client.addConfigurationSetting(baseSetting); + }); + + afterEach(async () => { + await client.deleteConfigurationSetting({ + key: baseSetting.key, + label: baseSetting.label + }); + }); + + function assertFeatureFlagProps( + actual: Omit, + expected: FeatureFlag + ) { + assert.equal(isFeatureFlag(actual), true, "Expected to get the feature flag"); + if (isFeatureFlag(actual)) { + assert.equal( + actual.key, + expected.key, + "Key from the response from get request is not as expected" + ); + assert.deepEqual( + actual.conditions, + expected.conditions, + "conditions from the response from get request is not as expected" + ); + assert.equal(actual.description, expected.description); + assert.equal(actual.enabled, expected.enabled); + assert.equal(actual.isReadOnly, expected.isReadOnly); + assert.equal(actual.label, expected.label); + assert.equal(actual.contentType, expected.contentType); + } + } + + it("can add and get FeatureFlag", async () => { + assertFeatureFlagProps(addResponse, baseSetting); + const getResponse = await client.getConfigurationSetting({ + key: baseSetting.key, + label: baseSetting.label + }); + assertFeatureFlagProps(getResponse, baseSetting); + }); + + it("can add and update FeatureFlag", async () => { + const getResponse = await client.getConfigurationSetting({ + key: baseSetting.key, + label: baseSetting.label + }); + assertFeatureFlagProps(getResponse, baseSetting); + if (isFeatureFlag(getResponse)) { + getResponse.enabled = !baseSetting.enabled; + } + + const setResponse = await client.setConfigurationSetting(getResponse); + assertFeatureFlagProps(setResponse, { + ...baseSetting, + enabled: !baseSetting.enabled + }); + + const getResponseAfterUpdate = await client.getConfigurationSetting({ + key: baseSetting.key, + label: baseSetting.label + }); + assertFeatureFlagProps(getResponseAfterUpdate, { + ...baseSetting, + enabled: !baseSetting.enabled + }); + }); + + it("can add, list and update multiple FeatureFlags", async () => { + const secondSetting = { + ...baseSetting, + key: `${baseSetting.key}-2` + }; + await client.addConfigurationSetting(secondSetting); + + let numberOFFeatureFlagsReceived = 0; + for await (const setting of client.listConfigurationSettings({ + keyFilter: `${baseSetting.key}*` + })) { + numberOFFeatureFlagsReceived++; + if (setting.key === baseSetting.key) { + assertFeatureFlagProps(setting, baseSetting); + await client.setConfigurationSetting({ + ...baseSetting, + enabled: !baseSetting.enabled + } as FeatureFlag); + } else { + assertFeatureFlagProps(setting, secondSetting); + await client.setConfigurationSetting({ + ...setting, + description: "I'm new description" + } as FeatureFlag); + } + } + assert.equal(numberOFFeatureFlagsReceived, 2, "Unexpected number of FeatureFlags seen"); + + for await (const setting of client.listConfigurationSettings({ + keyFilter: `${baseSetting.key}*` + })) { + numberOFFeatureFlagsReceived--; + if (setting.key === baseSetting.key) { + assertFeatureFlagProps(setting, { ...baseSetting, enabled: !baseSetting.enabled }); + } else { + assertFeatureFlagProps(setting, { ...secondSetting, description: "I'm new description" }); + } + } + + assert.equal( + numberOFFeatureFlagsReceived, + 0, + "Unexpected number of FeatureFlags seen after updating" + ); + await client.deleteConfigurationSetting({ key: secondSetting.key }); + }); + }); +}); diff --git a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts index 61cd1a833fe3..39e1096d3e3d 100644 --- a/sdk/appconfiguration/app-configuration/test/public/index.spec.ts +++ b/sdk/appconfiguration/app-configuration/test/public/index.spec.ts @@ -11,22 +11,7 @@ import { assertThrowsAbortError, startRecorder } from "./utils/testHelpers"; -import { - AddConfigurationSettingResponse, - AppConfigurationClient, - ConfigurationSetting, - ConfigurationSettingParam, - FeatureFlag, - featureFlagContentType, - FeatureFlagPercentageClientFilter, - featureFlagPrefix, - FeatureFlagTargetingClientFilter, - FeatureFlagTimeWindowClientFilter, - isFeatureFlag, - isSecretReference, - SecretReference, - secretReferenceContentType -} from "../../src"; +import { AppConfigurationClient, ConfigurationSetting, ConfigurationSettingParam } from "../../src"; import { delay } from "@azure/core-http"; import { Recorder } from "@azure/test-utils-recorder"; import { Context } from "mocha"; @@ -1155,301 +1140,4 @@ describe("AppConfigurationClient", () => { }); }); }); - - describe("FeatureFlag configuration setting", () => { - const clientFilters: ( - | Record - | FeatureFlagTargetingClientFilter - | FeatureFlagTimeWindowClientFilter - | FeatureFlagPercentageClientFilter - )[] = [ - { - name: "Microsoft.TimeWindow", - parameters: { - start: "Wed, 01 May 2019 13:59:59 GMT", - end: "Mon, 01 July 2019 00:00:00 GMT" - } - }, - { name: "FilterX" }, - { - name: "Microsoft.Targeting", - parameters: { - audience: { - groups: [ - { name: "group-1", rolloutPercentage: 25 }, - { name: "group-2", rolloutPercentage: 45 } - ], - users: ["userA", "userB"], - defaultRolloutPercentage: 40 - } - } - }, - { name: "Microsoft.Percentage", parameters: { value: 25 } } - ]; - - let baseSetting: FeatureFlag; - let addResponse: AddConfigurationSettingResponse; - - beforeEach(async () => { - baseSetting = { - conditions: { - clientFilters - }, - enabled: false, - isReadOnly: false, - key: `${featureFlagPrefix + recorder.getUniqueName("name-1")}`, - contentType: featureFlagContentType, - description: "I'm a description", - label: "label-1" - }; - addResponse = await client.addConfigurationSetting(baseSetting); - }); - - afterEach(async () => { - await client.deleteConfigurationSetting({ - key: baseSetting.key, - label: baseSetting.label - }); - }); - - function assertFeatureFlagProps( - actual: Omit, - expected: FeatureFlag - ) { - assert.equal(isFeatureFlag(actual), true, "Expected to get the feature flag"); - if (isFeatureFlag(actual)) { - assert.equal( - actual.key, - expected.key, - "Key from the response from get request is not as expected" - ); - assert.deepEqual( - actual.conditions, - expected.conditions, - "conditions from the response from get request is not as expected" - ); - assert.equal(actual.description, expected.description); - assert.equal(actual.enabled, expected.enabled); - assert.equal(actual.isReadOnly, expected.isReadOnly); - assert.equal(actual.label, expected.label); - assert.equal(actual.contentType, expected.contentType); - } - } - - it("can add and get FeatureFlag", async () => { - assertFeatureFlagProps(addResponse, baseSetting); - const getResponse = await client.getConfigurationSetting({ - key: baseSetting.key, - label: baseSetting.label - }); - assertFeatureFlagProps(getResponse, baseSetting); - }); - - it("can add and update FeatureFlag", async () => { - const getResponse = await client.getConfigurationSetting({ - key: baseSetting.key, - label: baseSetting.label - }); - assertFeatureFlagProps(getResponse, baseSetting); - if (isFeatureFlag(getResponse)) { - getResponse.enabled = !baseSetting.enabled; - } - - const setResponse = await client.setConfigurationSetting(getResponse); - assertFeatureFlagProps(setResponse, { - ...baseSetting, - enabled: !baseSetting.enabled - }); - - const getResponseAfterUpdate = await client.getConfigurationSetting({ - key: baseSetting.key, - label: baseSetting.label - }); - assertFeatureFlagProps(getResponseAfterUpdate, { - ...baseSetting, - enabled: !baseSetting.enabled - }); - }); - - it("can add, list and update multiple FeatureFlags", async () => { - const secondSetting = { - ...baseSetting, - key: `${baseSetting.key}-2` - }; - await client.addConfigurationSetting(secondSetting); - - let numberOFFeatureFlagsReceived = 0; - for await (const setting of client.listConfigurationSettings({ - keyFilter: `${baseSetting.key}*` - })) { - numberOFFeatureFlagsReceived++; - if (setting.key === baseSetting.key) { - assertFeatureFlagProps(setting, baseSetting); - await client.setConfigurationSetting({ - ...baseSetting, - enabled: !baseSetting.enabled - } as FeatureFlag); - } else { - assertFeatureFlagProps(setting, secondSetting); - await client.setConfigurationSetting({ - ...setting, - description: "I'm new description" - } as FeatureFlag); - } - } - assert.equal(numberOFFeatureFlagsReceived, 2, "Unexpected number of FeatureFlags seen"); - - for await (const setting of client.listConfigurationSettings({ - keyFilter: `${baseSetting.key}*` - })) { - numberOFFeatureFlagsReceived--; - if (setting.key === baseSetting.key) { - assertFeatureFlagProps(setting, { ...baseSetting, enabled: !baseSetting.enabled }); - } else { - assertFeatureFlagProps(setting, { ...secondSetting, description: "I'm new description" }); - } - } - - assert.equal( - numberOFFeatureFlagsReceived, - 0, - "Unexpected number of FeatureFlags seen after updating" - ); - await client.deleteConfigurationSetting({ key: secondSetting.key }); - }); - }); - - describe("SecretReference configuration setting", () => { - const getBaseSetting = (): SecretReference => { - return { - secretId: `https://vault_name.vault.azure.net/secrets/${recorder.getUniqueName("name-2")}`, // TODO: It's a URL in .NET, should we leave it as a string input? - isReadOnly: false, - key: recorder.getUniqueName("name-3"), - label: "label-s", - contentType: secretReferenceContentType - }; - }; - - function assertSecretReferenceProps( - actual: Omit, - expected: SecretReference - ) { - assert.equal(isSecretReference(actual), true, "Expected to get the SecretReference"); - if (isSecretReference(actual)) { - assert.equal( - actual.key, - expected.key, - "Key from the response from get request is not as expected" - ); - assert.equal(actual.secretId, expected.secretId); - assert.equal(actual.isReadOnly, expected.isReadOnly); - assert.equal(actual.label, expected.label); - assert.equal(actual.contentType, expected.contentType); - } - } - - let addResponse: AddConfigurationSettingResponse; - let baseSetting: SecretReference; - beforeEach(async () => { - baseSetting = getBaseSetting(); - addResponse = await client.addConfigurationSetting(baseSetting); - }); - - afterEach(async () => { - await client.deleteConfigurationSetting({ - key: baseSetting.key - }); - }); - - it("can add and get SecretReference", async () => { - assertSecretReferenceProps(addResponse, baseSetting); - const getResponse = await client.getConfigurationSetting({ - key: baseSetting.key, - label: baseSetting.label - }); - assertSecretReferenceProps(getResponse, baseSetting); - }); - - it("can add and update SecretReference", async () => { - const getResponse = await client.getConfigurationSetting({ - key: baseSetting.key, - label: baseSetting.label - }); - const newSecretId = `https://vault_name.vault.azure.net/secrets/${recorder.getUniqueName( - "name-4" - )}`; - - assertSecretReferenceProps(getResponse, baseSetting); - if (isSecretReference(getResponse)) { - getResponse.secretId = newSecretId; - } - - const setResponse = await client.setConfigurationSetting(getResponse); - assertSecretReferenceProps(setResponse, { - ...baseSetting, - secretId: newSecretId - }); - - const getResponseAfterUpdate = await client.getConfigurationSetting({ - key: baseSetting.key, - label: baseSetting.label - }); - assertSecretReferenceProps(getResponseAfterUpdate, { - ...baseSetting, - secretId: newSecretId - }); - }); - - it("can add, list and update multiple SecretReferences", async () => { - const secondSetting = { - ...baseSetting, - key: `${baseSetting.key}-2` - }; - const newSecretId = `https://vault_name.vault.azure.net/secrets/${recorder.getUniqueName( - "name-5" - )}`; - await client.addConfigurationSetting(secondSetting); - - let numberOFSecretReferencesReceived = 0; - for await (const setting of client.listConfigurationSettings({ - keyFilter: `${baseSetting.key}*` - })) { - numberOFSecretReferencesReceived++; - if (setting.key === baseSetting.key) { - assertSecretReferenceProps(setting, baseSetting); - await client.setConfigurationSetting({ - ...baseSetting, - secretId: newSecretId - } as SecretReference); - } else { - assertSecretReferenceProps(setting, secondSetting); - await client.setReadOnly( - { key: setting.key, label: setting.label }, - !secondSetting.isReadOnly - ); - } - } - assert.equal(numberOFSecretReferencesReceived, 2, "Unexpected number of FeatureFlags seen"); - for await (const setting of client.listConfigurationSettings({ - keyFilter: `${baseSetting.key}*` - })) { - numberOFSecretReferencesReceived--; - if (setting.key === baseSetting.key) { - assertSecretReferenceProps(setting, { ...baseSetting, secretId: newSecretId }); - } else { - assertSecretReferenceProps(setting, { - ...secondSetting, - isReadOnly: !secondSetting.isReadOnly - }); - } - } - - assert.equal( - numberOFSecretReferencesReceived, - 0, - "Unexpected number of SecretReferences seen after updating" - ); - await client.deleteConfigurationSetting({ key: secondSetting.key }); - }); - }); }); diff --git a/sdk/appconfiguration/app-configuration/test/public/secretReference.spec.ts b/sdk/appconfiguration/app-configuration/test/public/secretReference.spec.ts new file mode 100644 index 000000000000..c5e3e12b5223 --- /dev/null +++ b/sdk/appconfiguration/app-configuration/test/public/secretReference.spec.ts @@ -0,0 +1,162 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { assert } from "chai"; +import { createAppConfigurationClientForTests, startRecorder } from "./utils/testHelpers"; +import { + AddConfigurationSettingResponse, + AppConfigurationClient, + isSecretReference, + SecretReference, + secretReferenceContentType +} from "../../src"; +import { Recorder } from "@azure/test-utils-recorder"; +import { Context } from "mocha"; + +describe("AppConfigurationClient - SecretReference", () => { + let client: AppConfigurationClient; + let recorder: Recorder; + + beforeEach(function(this: Context) { + recorder = startRecorder(this); + client = createAppConfigurationClientForTests() || this.skip(); + }); + + afterEach(async function(this: Context) { + await recorder.stop(); + }); + + describe("SecretReference configuration setting", () => { + const getBaseSetting = (): SecretReference => { + return { + secretId: `https://vault_name.vault.azure.net/secrets/${recorder.getUniqueName("name-2")}`, // TODO: It's a URL in .NET, should we leave it as a string input? + isReadOnly: false, + key: recorder.getUniqueName("name-3"), + label: "label-s", + contentType: secretReferenceContentType + }; + }; + + function assertSecretReferenceProps( + actual: Omit, + expected: SecretReference + ) { + assert.equal(isSecretReference(actual), true, "Expected to get the SecretReference"); + if (isSecretReference(actual)) { + assert.equal( + actual.key, + expected.key, + "Key from the response from get request is not as expected" + ); + assert.equal(actual.secretId, expected.secretId); + assert.equal(actual.isReadOnly, expected.isReadOnly); + assert.equal(actual.label, expected.label); + assert.equal(actual.contentType, expected.contentType); + } + } + + let addResponse: AddConfigurationSettingResponse; + let baseSetting: SecretReference; + beforeEach(async () => { + baseSetting = getBaseSetting(); + addResponse = await client.addConfigurationSetting(baseSetting); + }); + + afterEach(async () => { + await client.deleteConfigurationSetting({ + key: baseSetting.key + }); + }); + + it("can add and get SecretReference", async () => { + assertSecretReferenceProps(addResponse, baseSetting); + const getResponse = await client.getConfigurationSetting({ + key: baseSetting.key, + label: baseSetting.label + }); + assertSecretReferenceProps(getResponse, baseSetting); + }); + + it("can add and update SecretReference", async () => { + const getResponse = await client.getConfigurationSetting({ + key: baseSetting.key, + label: baseSetting.label + }); + const newSecretId = `https://vault_name.vault.azure.net/secrets/${recorder.getUniqueName( + "name-4" + )}`; + + assertSecretReferenceProps(getResponse, baseSetting); + if (isSecretReference(getResponse)) { + getResponse.secretId = newSecretId; + } + + const setResponse = await client.setConfigurationSetting(getResponse); + assertSecretReferenceProps(setResponse, { + ...baseSetting, + secretId: newSecretId + }); + + const getResponseAfterUpdate = await client.getConfigurationSetting({ + key: baseSetting.key, + label: baseSetting.label + }); + assertSecretReferenceProps(getResponseAfterUpdate, { + ...baseSetting, + secretId: newSecretId + }); + }); + + it("can add, list and update multiple SecretReferences", async () => { + const secondSetting = { + ...baseSetting, + key: `${baseSetting.key}-2` + }; + const newSecretId = `https://vault_name.vault.azure.net/secrets/${recorder.getUniqueName( + "name-5" + )}`; + await client.addConfigurationSetting(secondSetting); + + let numberOFSecretReferencesReceived = 0; + for await (const setting of client.listConfigurationSettings({ + keyFilter: `${baseSetting.key}*` + })) { + numberOFSecretReferencesReceived++; + if (setting.key === baseSetting.key) { + assertSecretReferenceProps(setting, baseSetting); + await client.setConfigurationSetting({ + ...baseSetting, + secretId: newSecretId + } as SecretReference); + } else { + assertSecretReferenceProps(setting, secondSetting); + await client.setReadOnly( + { key: setting.key, label: setting.label }, + !secondSetting.isReadOnly + ); + } + } + assert.equal(numberOFSecretReferencesReceived, 2, "Unexpected number of FeatureFlags seen"); + for await (const setting of client.listConfigurationSettings({ + keyFilter: `${baseSetting.key}*` + })) { + numberOFSecretReferencesReceived--; + if (setting.key === baseSetting.key) { + assertSecretReferenceProps(setting, { ...baseSetting, secretId: newSecretId }); + } else { + assertSecretReferenceProps(setting, { + ...secondSetting, + isReadOnly: !secondSetting.isReadOnly + }); + } + } + + assert.equal( + numberOFSecretReferencesReceived, + 0, + "Unexpected number of SecretReferences seen after updating" + ); + await client.deleteConfigurationSetting({ key: secondSetting.key }); + }); + }); +}); From 633924a1fe1622a4ff1890a6c96a447ddf495c68 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Fri, 2 Apr 2021 13:36:58 -0700 Subject: [PATCH 44/50] SecretReference sample --- .../app-configuration/package.json | 1 + .../app-configuration/sample.env | 13 ++ .../samples-dev/secretReference.ts | 114 ++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 sdk/appconfiguration/app-configuration/samples-dev/secretReference.ts diff --git a/sdk/appconfiguration/app-configuration/package.json b/sdk/appconfiguration/app-configuration/package.json index 618874cea6fd..371f755f9a5b 100644 --- a/sdk/appconfiguration/app-configuration/package.json +++ b/sdk/appconfiguration/app-configuration/package.json @@ -96,6 +96,7 @@ "@azure/dev-tool": "^1.0.0", "@azure/eslint-plugin-azure-sdk": "^3.0.0", "@azure/identity": "^1.1.0", + "@azure/keyvault-secrets": "^4.2.0-beta.4", "@azure/test-utils-recorder": "^1.0.0", "@microsoft/api-extractor": "7.7.11", "@rollup/plugin-commonjs": "11.0.2", diff --git a/sdk/appconfiguration/app-configuration/sample.env b/sdk/appconfiguration/app-configuration/sample.env index db027248c47f..b6206adee5a4 100644 --- a/sdk/appconfiguration/app-configuration/sample.env +++ b/sdk/appconfiguration/app-configuration/sample.env @@ -1,3 +1,16 @@ # This is the connection string for your app-configuration resource # You can get this from the Azure portal. APPCONFIG_CONNECTION_STRING= + +# The following is only required for the secretReference sample +# The name of the key vault to use in the samples. +# Create a Key Vault in the Azure Portal and enter its URI (e.g. https://mytest.vault.azure.net/) here. +KEYVAULT_URI= + +# Used to authenticate using Azure AD as a service principal for role-based authentication. +# +# See the documentation for `EnvironmentCredential` at the following link: +# https://docs.microsoft.com/javascript/api/@azure/identity/environmentcredential +AZURE_TENANT_ID= +AZURE_CLIENT_ID= +AZURE_CLIENT_SECRET= diff --git a/sdk/appconfiguration/app-configuration/samples-dev/secretReference.ts b/sdk/appconfiguration/app-configuration/samples-dev/secretReference.ts new file mode 100644 index 000000000000..481a87fd3371 --- /dev/null +++ b/sdk/appconfiguration/app-configuration/samples-dev/secretReference.ts @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** + * @summary SecretReference represents a configuration setting that references as KeyVault secret. + * + * @azsdk-weight 30 + */ +import { + AppConfigurationClient, + isSecretReference, + SecretReference, + secretReferenceContentType +} from "@azure/app-configuration"; +import { SecretClient } from "@azure/keyvault-secrets"; +import { DefaultAzureCredential } from "@azure/identity"; + +// Load the .env file if it exists +import * as dotenv from "dotenv"; +dotenv.config(); + +export async function main() { + console.log(`Running secretReference sample`); + const secretReference: SecretReference = { + key: `secret${new Date().getTime()}`, + secretId: `secret-key${Math.ceil(100 + Math.random() * 900)}`, + isReadOnly: false, + contentType: secretReferenceContentType + }; + + if ( + !process.env["AZURE_TENANT_ID"] || + !process.env["AZURE_CLIENT_ID"] || + !process.env["AZURE_CLIENT_SECRET"] || + !process.env["KEYVAULT_URI"] + ) { + console.log( + `At least one of the AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, and KEYVAULT_URI variables is not present, + please add the missing ones in your environment and rerun the sample.` + ); + return; + } + // DefaultAzureCredential expects the following three environment variables: + // - AZURE_TENANT_ID: The tenant ID in Azure Active Directory + // - AZURE_CLIENT_ID: The application (client) ID registered in the AAD tenant + // - AZURE_CLIENT_SECRET: The client secret for the registered application + const credential = new DefaultAzureCredential(); + const url = process.env["KEYVAULT_URI"] || ""; + + const secretClient = new SecretClient(url, credential); + // Create a secret + console.log( + `Create a keyvault secret with key: ${secretReference.secretId} and value: "MySecretValue"` + ); + await secretClient.setSecret(secretReference.secretId, "MySecretValue"); + + // Set the following environment variable or edit the value on the following line. + const connectionString = process.env["APPCONFIG_CONNECTION_STRING"] || ""; + const appConfigClient = new AppConfigurationClient(connectionString); + + await cleanupSampleValues([secretReference.key], appConfigClient); + + console.log( + `Add a new secretReference with key: ${secretReference.key} and secretId: ${secretReference.secretId}` + ); + await appConfigClient.addConfigurationSetting(secretReference); + + console.log(`Get the added secretReference from App Config with key: ${secretReference.key}`); + const getResponse = await appConfigClient.getConfigurationSetting({ + key: secretReference.key + }); + + if (isSecretReference(getResponse)) { + // isSecretReference() check for type inference to narrow down the configuration setting as a SecretReference + // setting is a `SecretReference` + // Read the secret we created + const secret = await secretClient.getSecret(getResponse.secretId); + console.log(`Get the secret from keyvault key: ${secret.name}, value: ${secret.value}`); + + console.log(`Deleting the secret from keyvault`); + await secretClient.beginDeleteSecret(getResponse.secretId); + } + + await cleanupSampleValues([secretReference.key], appConfigClient); +} + +async function cleanupSampleValues(keys: string[], client: AppConfigurationClient) { + const settingsIterator = client.listConfigurationSettings({ + keyFilter: keys.join(",") + }); + + for await (const setting of settingsIterator) { + await client.deleteConfigurationSetting({ key: setting.key, label: setting.label }); + } +} + +/** + * Returns the environment variable, throws an error if not defined. + * + * @export + * @param {string} name + */ +export function getEnvVar(name: string) { + const val = process.env[name]; + if (!val) { + throw `Environment variable ${name} is not defined.`; + } + return val; +} + +main().catch((err) => { + console.error("Failed to run sample:", err); + process.exit(1); +}); From a84091173593d1c8d73fc33ccf26a616afdf2abf Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Fri, 2 Apr 2021 13:48:09 -0700 Subject: [PATCH 45/50] feature flag sample --- .../samples-dev/featureFlag.ts | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 sdk/appconfiguration/app-configuration/samples-dev/featureFlag.ts diff --git a/sdk/appconfiguration/app-configuration/samples-dev/featureFlag.ts b/sdk/appconfiguration/app-configuration/samples-dev/featureFlag.ts new file mode 100644 index 000000000000..2d94b071859e --- /dev/null +++ b/sdk/appconfiguration/app-configuration/samples-dev/featureFlag.ts @@ -0,0 +1,172 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** + * @summary Feature flags are settings that follow specific JSON schema for the value. + * + * @azsdk-weight 20 + */ +import { + AppConfigurationClient, + FeatureFlag, + featureFlagContentType, + featureFlagPrefix, + isFeatureFlag, + isFeatureFlagClientFilter +} from "@azure/app-configuration"; + +// Load the .env file if it exists +import * as dotenv from "dotenv"; +dotenv.config(); + +export async function main() { + console.log(`Running featureFlag sample`); + + const featureFlag: FeatureFlag = { + conditions: { + clientFilters: [ + { + name: "Microsoft.TimeWindow", + parameters: { + start: "Wed, 01 May 2021 13:59:59 GMT", + end: "Mon, 01 July 2022 00:00:00 GMT" + } + }, + { name: "FilterX" }, + { + name: "Microsoft.Targeting", + parameters: { + audience: { + groups: [ + { name: "group-1", rolloutPercentage: 25 }, + { name: "group-2", rolloutPercentage: 45 } + ], + users: ["userA", "userB"], + defaultRolloutPercentage: 40 + } + } + }, + { name: "Microsoft.Percentage", parameters: { value: 25 } } + ] + }, + enabled: false, + isReadOnly: false, + key: `${featureFlagPrefix}new-feature-flag-${Math.ceil(100 + Math.random() * 900)}`, + contentType: featureFlagContentType, + description: "I'm a description" + }; + + // Set the following environment variable or edit the value on the following line. + const connectionString = process.env["APPCONFIG_CONNECTION_STRING"] || ""; + const appConfigClient = new AppConfigurationClient(connectionString); + + await cleanupSampleValues([featureFlag.key], appConfigClient); + + console.log(`Add a new featureFlag with key: ${featureFlag.key}`); + await appConfigClient.addConfigurationSetting(featureFlag); + + console.log(`Get the added featureFlag with key: ${featureFlag.key}`); + const getResponse = await appConfigClient.getConfigurationSetting({ + key: featureFlag.key + }); + + if (isFeatureFlag(getResponse)) { + // isFeatureFlag() check for type inference to narrow down the configuration setting as a FeatureFlag + // setting is a `FeatureFlag` + const conditions = getResponse.conditions; + + // the client filters are the real meat of the FeatureFlag. + // + for (const clientFilter of conditions.clientFilters) { + if (isFeatureFlagClientFilter("targeting", clientFilter)) { + // some of the fields: + // clientFilter.parameters.audience + // clientFilter.parameters.audience.groups[0].name + // clientFilter.parameters.audience.groups[0].rolloutPercentage + // clientFilter.parameters.audience.users[0] // string + // clientFilter.parameters.audience.defaultRolloutPercentage + console.log( + ` targeting feature flag client filter => name: ${clientFilter.name}, defaultRolloutPercentage: ${clientFilter.parameters.audience.defaultRolloutPercentage}` + ); + clientFilter.parameters.audience.defaultRolloutPercentage = 85; + } else if (isFeatureFlagClientFilter("timeWindow", clientFilter)) { + // clientFilter.parameters.end; + // clientFilter.parameters.start; + console.log( + ` timeWindow feature flag client filter => name: ${clientFilter.name}, start time: ${clientFilter.parameters.start}` + ); + clientFilter.parameters.start = "Wed, 01 June 2021 13:59:59 GMT"; + } else if (isFeatureFlagClientFilter("percentage", clientFilter)) { + console.log( + ` percentage feature flag client filter => name: ${clientFilter.name}, value: ${clientFilter.parameters.value}` + ); + clientFilter.parameters.value = 56; + } else { + console.log( + ` name of the custom feature flag client filter => name : ${clientFilter.name}` + ); + clientFilter.name = "FilterY"; + } + } + } + + console.log(`===> Update the featureFlag`); + await appConfigClient.setConfigurationSetting(getResponse); + const getResponseAfterUpdate = await appConfigClient.getConfigurationSetting({ + key: featureFlag.key + }); + console.log(`Get the updated featureFlag with key: ${featureFlag.key}`); + + if (isFeatureFlag(getResponseAfterUpdate)) { + const conditions = getResponseAfterUpdate.conditions; + for (const clientFilter of conditions.clientFilters) { + if (isFeatureFlagClientFilter("targeting", clientFilter)) { + console.log( + ` targeting feature flag client filter => name: ${clientFilter.name}, defaultRolloutPercentage: ${clientFilter.parameters.audience.defaultRolloutPercentage}` + ); + } else if (isFeatureFlagClientFilter("timeWindow", clientFilter)) { + console.log( + ` timeWindow feature flag client filter => name: ${clientFilter.name}, start time: ${clientFilter.parameters.start}` + ); + } else if (isFeatureFlagClientFilter("percentage", clientFilter)) { + console.log( + ` percentage feature flag client filter => name: ${clientFilter.name}, value: ${clientFilter.parameters.value}` + ); + } else { + console.log( + ` name of the custom feature flag client filter => name : ${clientFilter.name}` + ); + } + } + } + await cleanupSampleValues([featureFlag.key], appConfigClient); +} + +async function cleanupSampleValues(keys: string[], client: AppConfigurationClient) { + const settingsIterator = client.listConfigurationSettings({ + keyFilter: keys.join(",") + }); + + for await (const setting of settingsIterator) { + await client.deleteConfigurationSetting({ key: setting.key, label: setting.label }); + } +} + +/** + * Returns the environment variable, throws an error if not defined. + * + * @export + * @param {string} name + */ +export function getEnvVar(name: string) { + const val = process.env[name]; + if (!val) { + throw `Environment variable ${name} is not defined.`; + } + return val; +} + +main().catch((err) => { + console.error("Failed to run sample:", err); + process.exit(1); +}); From e7344387549a68d7aabfc3261f3029d70e0a09e0 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Fri, 2 Apr 2021 13:49:30 -0700 Subject: [PATCH 46/50] run dev-tool samples publish --force command --- .../samples/v1/javascript/README.md | 4 + .../samples/v1/javascript/featureFlag.js | 170 +++++++++++++++++ .../samples/v1/javascript/package.json | 6 +- .../samples/v1/javascript/sample.env | 13 ++ .../samples/v1/javascript/secretReference.js | 110 +++++++++++ .../samples/v1/typescript/README.md | 4 + .../samples/v1/typescript/package.json | 6 +- .../samples/v1/typescript/sample.env | 13 ++ .../samples/v1/typescript/src/featureFlag.ts | 172 ++++++++++++++++++ .../v1/typescript/src/secretReference.ts | 114 ++++++++++++ .../samples/v1/typescript/tsconfig.json | 2 +- 11 files changed, 609 insertions(+), 5 deletions(-) create mode 100644 sdk/appconfiguration/app-configuration/samples/v1/javascript/featureFlag.js create mode 100644 sdk/appconfiguration/app-configuration/samples/v1/javascript/secretReference.js create mode 100644 sdk/appconfiguration/app-configuration/samples/v1/typescript/src/featureFlag.ts create mode 100644 sdk/appconfiguration/app-configuration/samples/v1/typescript/src/secretReference.ts diff --git a/sdk/appconfiguration/app-configuration/samples/v1/javascript/README.md b/sdk/appconfiguration/app-configuration/samples/v1/javascript/README.md index 92e166b27ee7..634431a5ef08 100644 --- a/sdk/appconfiguration/app-configuration/samples/v1/javascript/README.md +++ b/sdk/appconfiguration/app-configuration/samples/v1/javascript/README.md @@ -21,6 +21,8 @@ These sample programs show how to use the JavaScript client libraries for Azure | [getSettingOnlyIfChanged.js][getsettingonlyifchanged] | Demonstrates getting a setting only if it has changed from what you already have. (This allows your app to avoid downloading the contents of a setting if the value is unchanged.) | | [listConfigurationSettings.js][listconfigurationsettings] | Demonstrates listing multiple configuration settings using a filter for a key or label. | | [listRevisions.js][listrevisions] | Demonstrates listing revisions for a configuration setting. | +| [secretReference.js][secretreference] | SecretReference represents a configuration setting that references as KeyVault secret. | +| [featureFlag.js][featureflag] | Feature flags are settings that follow specific JSON schema for the value. | ## Prerequisites @@ -69,6 +71,8 @@ Take a look at our [API Documentation][apiref] for more information about the AP [getsettingonlyifchanged]: https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/appconfiguration/app-configuration/samples/v1/javascript/getSettingOnlyIfChanged.js [listconfigurationsettings]: https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/appconfiguration/app-configuration/samples/v1/javascript/listConfigurationSettings.js [listrevisions]: https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/appconfiguration/app-configuration/samples/v1/javascript/listRevisions.js +[secretreference]: https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/appconfiguration/app-configuration/samples/v1/javascript/secretReference.js +[featureflag]: https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/appconfiguration/app-configuration/samples/v1/javascript/featureFlag.js [apiref]: https://docs.microsoft.com/javascript/api/@azure/app-configuration [freesub]: https://azure.microsoft.com/free/ [createinstance_azureappconfigurationaccount]: https://docs.microsoft.com/azure/azure-app-configuration/quickstart-aspnet-core-app?tabs=core5x#create-an-app-configuration-store diff --git a/sdk/appconfiguration/app-configuration/samples/v1/javascript/featureFlag.js b/sdk/appconfiguration/app-configuration/samples/v1/javascript/featureFlag.js new file mode 100644 index 000000000000..16babd7b7b75 --- /dev/null +++ b/sdk/appconfiguration/app-configuration/samples/v1/javascript/featureFlag.js @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** + * @summary Feature flags are settings that follow specific JSON schema for the value. + * + */ +const { + AppConfigurationClient, + featureFlagContentType, + featureFlagPrefix, + isFeatureFlag, + isFeatureFlagClientFilter +} = require("@azure/app-configuration"); + +// Load the .env file if it exists +const dotenv = require("dotenv"); +dotenv.config(); + +async function main() { + console.log(`Running featureFlag sample`); + + const featureFlag = { + conditions: { + clientFilters: [ + { + name: "Microsoft.TimeWindow", + parameters: { + start: "Wed, 01 May 2021 13:59:59 GMT", + end: "Mon, 01 July 2022 00:00:00 GMT" + } + }, + { name: "FilterX" }, + { + name: "Microsoft.Targeting", + parameters: { + audience: { + groups: [ + { name: "group-1", rolloutPercentage: 25 }, + { name: "group-2", rolloutPercentage: 45 } + ], + users: ["userA", "userB"], + defaultRolloutPercentage: 40 + } + } + }, + { name: "Microsoft.Percentage", parameters: { value: 25 } } + ] + }, + enabled: false, + isReadOnly: false, + key: `${featureFlagPrefix}new-feature-flag-${Math.ceil(100 + Math.random() * 900)}`, + contentType: featureFlagContentType, + description: "I'm a description" + }; + + // Set the following environment variable or edit the value on the following line. + const connectionString = process.env["APPCONFIG_CONNECTION_STRING"] || ""; + const appConfigClient = new AppConfigurationClient(connectionString); + + await cleanupSampleValues([featureFlag.key], appConfigClient); + + console.log(`Add a new featureFlag with key: ${featureFlag.key}`); + await appConfigClient.addConfigurationSetting(featureFlag); + + console.log(`Get the added featureFlag with key: ${featureFlag.key}`); + const getResponse = await appConfigClient.getConfigurationSetting({ + key: featureFlag.key + }); + + if (isFeatureFlag(getResponse)) { + // isFeatureFlag() check for type inference to narrow down the configuration setting as a FeatureFlag + // setting is a `FeatureFlag` + const conditions = getResponse.conditions; + + // the client filters are the real meat of the FeatureFlag. + // + for (const clientFilter of conditions.clientFilters) { + if (isFeatureFlagClientFilter("targeting", clientFilter)) { + // some of the fields: + // clientFilter.parameters.audience + // clientFilter.parameters.audience.groups[0].name + // clientFilter.parameters.audience.groups[0].rolloutPercentage + // clientFilter.parameters.audience.users[0] // string + // clientFilter.parameters.audience.defaultRolloutPercentage + console.log( + ` targeting feature flag client filter => name: ${clientFilter.name}, defaultRolloutPercentage: ${clientFilter.parameters.audience.defaultRolloutPercentage}` + ); + clientFilter.parameters.audience.defaultRolloutPercentage = 85; + } else if (isFeatureFlagClientFilter("timeWindow", clientFilter)) { + // clientFilter.parameters.end; + // clientFilter.parameters.start; + console.log( + ` timeWindow feature flag client filter => name: ${clientFilter.name}, start time: ${clientFilter.parameters.start}` + ); + clientFilter.parameters.start = "Wed, 01 June 2021 13:59:59 GMT"; + } else if (isFeatureFlagClientFilter("percentage", clientFilter)) { + console.log( + ` percentage feature flag client filter => name: ${clientFilter.name}, value: ${clientFilter.parameters.value}` + ); + clientFilter.parameters.value = 56; + } else { + console.log( + ` name of the custom feature flag client filter => name : ${clientFilter.name}` + ); + clientFilter.name = "FilterY"; + } + } + } + + console.log(`===> Update the featureFlag`); + await appConfigClient.setConfigurationSetting(getResponse); + const getResponseAfterUpdate = await appConfigClient.getConfigurationSetting({ + key: featureFlag.key + }); + console.log(`Get the updated featureFlag with key: ${featureFlag.key}`); + + if (isFeatureFlag(getResponseAfterUpdate)) { + const conditions = getResponseAfterUpdate.conditions; + for (const clientFilter of conditions.clientFilters) { + if (isFeatureFlagClientFilter("targeting", clientFilter)) { + console.log( + ` targeting feature flag client filter => name: ${clientFilter.name}, defaultRolloutPercentage: ${clientFilter.parameters.audience.defaultRolloutPercentage}` + ); + } else if (isFeatureFlagClientFilter("timeWindow", clientFilter)) { + console.log( + ` timeWindow feature flag client filter => name: ${clientFilter.name}, start time: ${clientFilter.parameters.start}` + ); + } else if (isFeatureFlagClientFilter("percentage", clientFilter)) { + console.log( + ` percentage feature flag client filter => name: ${clientFilter.name}, value: ${clientFilter.parameters.value}` + ); + } else { + console.log( + ` name of the custom feature flag client filter => name : ${clientFilter.name}` + ); + } + } + } + await cleanupSampleValues([featureFlag.key], appConfigClient); +} + +async function cleanupSampleValues(keys, client) { + const settingsIterator = client.listConfigurationSettings({ + keyFilter: keys.join(",") + }); + + for await (const setting of settingsIterator) { + await client.deleteConfigurationSetting({ key: setting.key, label: setting.label }); + } +} + +/** + * Returns the environment variable, throws an error if not defined. + * + * @export + * @param {string} name + */ +export function getEnvVar(name) { + const val = process.env[name]; + if (!val) { + throw `Environment variable ${name} is not defined.`; + } + return val; +} + +main().catch((err) => { + console.error("Failed to run sample:", err); + process.exit(1); +}); diff --git a/sdk/appconfiguration/app-configuration/samples/v1/javascript/package.json b/sdk/appconfiguration/app-configuration/samples/v1/javascript/package.json index e39a6bb13b34..b962c6f89b07 100644 --- a/sdk/appconfiguration/app-configuration/samples/v1/javascript/package.json +++ b/sdk/appconfiguration/app-configuration/samples/v1/javascript/package.json @@ -26,7 +26,9 @@ }, "homepage": "https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/appconfiguration/app-configuration", "dependencies": { - "@azure/app-configuration": "latest", - "dotenv": "latest" + "@azure/app-configuration": "next", + "dotenv": "latest", + "@azure/keyvault-secrets": "^4.2.0-beta.4", + "@azure/identity": "^1.1.0" } } diff --git a/sdk/appconfiguration/app-configuration/samples/v1/javascript/sample.env b/sdk/appconfiguration/app-configuration/samples/v1/javascript/sample.env index db027248c47f..b6206adee5a4 100644 --- a/sdk/appconfiguration/app-configuration/samples/v1/javascript/sample.env +++ b/sdk/appconfiguration/app-configuration/samples/v1/javascript/sample.env @@ -1,3 +1,16 @@ # This is the connection string for your app-configuration resource # You can get this from the Azure portal. APPCONFIG_CONNECTION_STRING= + +# The following is only required for the secretReference sample +# The name of the key vault to use in the samples. +# Create a Key Vault in the Azure Portal and enter its URI (e.g. https://mytest.vault.azure.net/) here. +KEYVAULT_URI= + +# Used to authenticate using Azure AD as a service principal for role-based authentication. +# +# See the documentation for `EnvironmentCredential` at the following link: +# https://docs.microsoft.com/javascript/api/@azure/identity/environmentcredential +AZURE_TENANT_ID= +AZURE_CLIENT_ID= +AZURE_CLIENT_SECRET= diff --git a/sdk/appconfiguration/app-configuration/samples/v1/javascript/secretReference.js b/sdk/appconfiguration/app-configuration/samples/v1/javascript/secretReference.js new file mode 100644 index 000000000000..23bfc533157f --- /dev/null +++ b/sdk/appconfiguration/app-configuration/samples/v1/javascript/secretReference.js @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** + * @summary SecretReference represents a configuration setting that references as KeyVault secret. + * + */ +const { + AppConfigurationClient, + isSecretReference, + secretReferenceContentType +} = require("@azure/app-configuration"); +const { SecretClient } = require("@azure/keyvault-secrets"); +const { DefaultAzureCredential } = require("@azure/identity"); + +// Load the .env file if it exists +const dotenv = require("dotenv"); +dotenv.config(); + +async function main() { + console.log(`Running secretReference sample`); + const secretReference = { + key: `secret${new Date().getTime()}`, + secretId: `secret-key${Math.ceil(100 + Math.random() * 900)}`, + isReadOnly: false, + contentType: secretReferenceContentType + }; + + if ( + !process.env["AZURE_TENANT_ID"] || + !process.env["AZURE_CLIENT_ID"] || + !process.env["AZURE_CLIENT_SECRET"] || + !process.env["KEYVAULT_URI"] + ) { + console.log(`At least one of the AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, and KEYVAULT_URI variables is not present, + please add the missing ones in your environment and rerun the sample.`); + return; + } + // DefaultAzureCredential expects the following three environment variables: + // - AZURE_TENANT_ID: The tenant ID in Azure Active Directory + // - AZURE_CLIENT_ID: The application (client) ID registered in the AAD tenant + // - AZURE_CLIENT_SECRET: The client secret for the registered application + const credential = new DefaultAzureCredential(); + const url = process.env["KEYVAULT_URI"] || ""; + + const secretClient = new SecretClient(url, credential); + // Create a secret + console.log( + `Create a keyvault secret with key: ${secretReference.secretId} and value: "MySecretValue"` + ); + await secretClient.setSecret(secretReference.secretId, "MySecretValue"); + + // Set the following environment variable or edit the value on the following line. + const connectionString = process.env["APPCONFIG_CONNECTION_STRING"] || ""; + const appConfigClient = new AppConfigurationClient(connectionString); + + await cleanupSampleValues([secretReference.key], appConfigClient); + + console.log( + `Add a new secretReference with key: ${secretReference.key} and secretId: ${secretReference.secretId}` + ); + await appConfigClient.addConfigurationSetting(secretReference); + + console.log(`Get the added secretReference from App Config with key: ${secretReference.key}`); + const getResponse = await appConfigClient.getConfigurationSetting({ + key: secretReference.key + }); + + if (isSecretReference(getResponse)) { + // isSecretReference() check for type inference to narrow down the configuration setting as a SecretReference + // setting is a `SecretReference` + // Read the secret we created + const secret = await secretClient.getSecret(getResponse.secretId); + console.log(`Get the secret from keyvault key: ${secret.name}, value: ${secret.value}`); + + console.log(`Deleting the secret from keyvault`); + await secretClient.beginDeleteSecret(getResponse.secretId); + } + + await cleanupSampleValues([secretReference.key], appConfigClient); +} + +async function cleanupSampleValues(keys, client) { + const settingsIterator = client.listConfigurationSettings({ + keyFilter: keys.join(",") + }); + + for await (const setting of settingsIterator) { + await client.deleteConfigurationSetting({ key: setting.key, label: setting.label }); + } +} + +/** + * Returns the environment variable, throws an error if not defined. + * + * @export + * @param {string} name + */ +export function getEnvVar(name) { + const val = process.env[name]; + if (!val) { + throw `Environment variable ${name} is not defined.`; + } + return val; +} + +main().catch((err) => { + console.error("Failed to run sample:", err); + process.exit(1); +}); diff --git a/sdk/appconfiguration/app-configuration/samples/v1/typescript/README.md b/sdk/appconfiguration/app-configuration/samples/v1/typescript/README.md index a87eca8e1987..d92780083530 100644 --- a/sdk/appconfiguration/app-configuration/samples/v1/typescript/README.md +++ b/sdk/appconfiguration/app-configuration/samples/v1/typescript/README.md @@ -21,6 +21,8 @@ These sample programs show how to use the TypeScript client libraries for Azure | [getSettingOnlyIfChanged.ts][getsettingonlyifchanged] | Demonstrates getting a setting only if it has changed from what you already have. (This allows your app to avoid downloading the contents of a setting if the value is unchanged.) | | [listConfigurationSettings.ts][listconfigurationsettings] | Demonstrates listing multiple configuration settings using a filter for a key or label. | | [listRevisions.ts][listrevisions] | Demonstrates listing revisions for a configuration setting. | +| [secretReference.ts][secretreference] | SecretReference represents a configuration setting that references as KeyVault secret. | +| [featureFlag.ts][featureflag] | Feature flags are settings that follow specific JSON schema for the value. | ## Prerequisites @@ -81,6 +83,8 @@ Take a look at our [API Documentation][apiref] for more information about the AP [getsettingonlyifchanged]: https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/getSettingOnlyIfChanged.ts [listconfigurationsettings]: https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/listConfigurationSettings.ts [listrevisions]: https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/listRevisions.ts +[secretreference]: https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/secretReference.ts +[featureflag]: https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/featureFlag.ts [apiref]: https://docs.microsoft.com/javascript/api/@azure/app-configuration [freesub]: https://azure.microsoft.com/free/ [createinstance_azureappconfigurationaccount]: https://docs.microsoft.com/azure/azure-app-configuration/quickstart-aspnet-core-app?tabs=core5x#create-an-app-configuration-store diff --git a/sdk/appconfiguration/app-configuration/samples/v1/typescript/package.json b/sdk/appconfiguration/app-configuration/samples/v1/typescript/package.json index 479e8d330209..8699931e59b8 100644 --- a/sdk/appconfiguration/app-configuration/samples/v1/typescript/package.json +++ b/sdk/appconfiguration/app-configuration/samples/v1/typescript/package.json @@ -30,8 +30,10 @@ }, "homepage": "https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/appconfiguration/app-configuration", "dependencies": { - "@azure/app-configuration": "latest", - "dotenv": "latest" + "@azure/app-configuration": "next", + "dotenv": "latest", + "@azure/keyvault-secrets": "^4.2.0-beta.4", + "@azure/identity": "^1.1.0" }, "devDependencies": { "typescript": "~4.2.0", diff --git a/sdk/appconfiguration/app-configuration/samples/v1/typescript/sample.env b/sdk/appconfiguration/app-configuration/samples/v1/typescript/sample.env index db027248c47f..b6206adee5a4 100644 --- a/sdk/appconfiguration/app-configuration/samples/v1/typescript/sample.env +++ b/sdk/appconfiguration/app-configuration/samples/v1/typescript/sample.env @@ -1,3 +1,16 @@ # This is the connection string for your app-configuration resource # You can get this from the Azure portal. APPCONFIG_CONNECTION_STRING= + +# The following is only required for the secretReference sample +# The name of the key vault to use in the samples. +# Create a Key Vault in the Azure Portal and enter its URI (e.g. https://mytest.vault.azure.net/) here. +KEYVAULT_URI= + +# Used to authenticate using Azure AD as a service principal for role-based authentication. +# +# See the documentation for `EnvironmentCredential` at the following link: +# https://docs.microsoft.com/javascript/api/@azure/identity/environmentcredential +AZURE_TENANT_ID= +AZURE_CLIENT_ID= +AZURE_CLIENT_SECRET= diff --git a/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/featureFlag.ts new file mode 100644 index 000000000000..2d94b071859e --- /dev/null +++ b/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/featureFlag.ts @@ -0,0 +1,172 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** + * @summary Feature flags are settings that follow specific JSON schema for the value. + * + * @azsdk-weight 20 + */ +import { + AppConfigurationClient, + FeatureFlag, + featureFlagContentType, + featureFlagPrefix, + isFeatureFlag, + isFeatureFlagClientFilter +} from "@azure/app-configuration"; + +// Load the .env file if it exists +import * as dotenv from "dotenv"; +dotenv.config(); + +export async function main() { + console.log(`Running featureFlag sample`); + + const featureFlag: FeatureFlag = { + conditions: { + clientFilters: [ + { + name: "Microsoft.TimeWindow", + parameters: { + start: "Wed, 01 May 2021 13:59:59 GMT", + end: "Mon, 01 July 2022 00:00:00 GMT" + } + }, + { name: "FilterX" }, + { + name: "Microsoft.Targeting", + parameters: { + audience: { + groups: [ + { name: "group-1", rolloutPercentage: 25 }, + { name: "group-2", rolloutPercentage: 45 } + ], + users: ["userA", "userB"], + defaultRolloutPercentage: 40 + } + } + }, + { name: "Microsoft.Percentage", parameters: { value: 25 } } + ] + }, + enabled: false, + isReadOnly: false, + key: `${featureFlagPrefix}new-feature-flag-${Math.ceil(100 + Math.random() * 900)}`, + contentType: featureFlagContentType, + description: "I'm a description" + }; + + // Set the following environment variable or edit the value on the following line. + const connectionString = process.env["APPCONFIG_CONNECTION_STRING"] || ""; + const appConfigClient = new AppConfigurationClient(connectionString); + + await cleanupSampleValues([featureFlag.key], appConfigClient); + + console.log(`Add a new featureFlag with key: ${featureFlag.key}`); + await appConfigClient.addConfigurationSetting(featureFlag); + + console.log(`Get the added featureFlag with key: ${featureFlag.key}`); + const getResponse = await appConfigClient.getConfigurationSetting({ + key: featureFlag.key + }); + + if (isFeatureFlag(getResponse)) { + // isFeatureFlag() check for type inference to narrow down the configuration setting as a FeatureFlag + // setting is a `FeatureFlag` + const conditions = getResponse.conditions; + + // the client filters are the real meat of the FeatureFlag. + // + for (const clientFilter of conditions.clientFilters) { + if (isFeatureFlagClientFilter("targeting", clientFilter)) { + // some of the fields: + // clientFilter.parameters.audience + // clientFilter.parameters.audience.groups[0].name + // clientFilter.parameters.audience.groups[0].rolloutPercentage + // clientFilter.parameters.audience.users[0] // string + // clientFilter.parameters.audience.defaultRolloutPercentage + console.log( + ` targeting feature flag client filter => name: ${clientFilter.name}, defaultRolloutPercentage: ${clientFilter.parameters.audience.defaultRolloutPercentage}` + ); + clientFilter.parameters.audience.defaultRolloutPercentage = 85; + } else if (isFeatureFlagClientFilter("timeWindow", clientFilter)) { + // clientFilter.parameters.end; + // clientFilter.parameters.start; + console.log( + ` timeWindow feature flag client filter => name: ${clientFilter.name}, start time: ${clientFilter.parameters.start}` + ); + clientFilter.parameters.start = "Wed, 01 June 2021 13:59:59 GMT"; + } else if (isFeatureFlagClientFilter("percentage", clientFilter)) { + console.log( + ` percentage feature flag client filter => name: ${clientFilter.name}, value: ${clientFilter.parameters.value}` + ); + clientFilter.parameters.value = 56; + } else { + console.log( + ` name of the custom feature flag client filter => name : ${clientFilter.name}` + ); + clientFilter.name = "FilterY"; + } + } + } + + console.log(`===> Update the featureFlag`); + await appConfigClient.setConfigurationSetting(getResponse); + const getResponseAfterUpdate = await appConfigClient.getConfigurationSetting({ + key: featureFlag.key + }); + console.log(`Get the updated featureFlag with key: ${featureFlag.key}`); + + if (isFeatureFlag(getResponseAfterUpdate)) { + const conditions = getResponseAfterUpdate.conditions; + for (const clientFilter of conditions.clientFilters) { + if (isFeatureFlagClientFilter("targeting", clientFilter)) { + console.log( + ` targeting feature flag client filter => name: ${clientFilter.name}, defaultRolloutPercentage: ${clientFilter.parameters.audience.defaultRolloutPercentage}` + ); + } else if (isFeatureFlagClientFilter("timeWindow", clientFilter)) { + console.log( + ` timeWindow feature flag client filter => name: ${clientFilter.name}, start time: ${clientFilter.parameters.start}` + ); + } else if (isFeatureFlagClientFilter("percentage", clientFilter)) { + console.log( + ` percentage feature flag client filter => name: ${clientFilter.name}, value: ${clientFilter.parameters.value}` + ); + } else { + console.log( + ` name of the custom feature flag client filter => name : ${clientFilter.name}` + ); + } + } + } + await cleanupSampleValues([featureFlag.key], appConfigClient); +} + +async function cleanupSampleValues(keys: string[], client: AppConfigurationClient) { + const settingsIterator = client.listConfigurationSettings({ + keyFilter: keys.join(",") + }); + + for await (const setting of settingsIterator) { + await client.deleteConfigurationSetting({ key: setting.key, label: setting.label }); + } +} + +/** + * Returns the environment variable, throws an error if not defined. + * + * @export + * @param {string} name + */ +export function getEnvVar(name: string) { + const val = process.env[name]; + if (!val) { + throw `Environment variable ${name} is not defined.`; + } + return val; +} + +main().catch((err) => { + console.error("Failed to run sample:", err); + process.exit(1); +}); diff --git a/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/secretReference.ts b/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/secretReference.ts new file mode 100644 index 000000000000..481a87fd3371 --- /dev/null +++ b/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/secretReference.ts @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** + * @summary SecretReference represents a configuration setting that references as KeyVault secret. + * + * @azsdk-weight 30 + */ +import { + AppConfigurationClient, + isSecretReference, + SecretReference, + secretReferenceContentType +} from "@azure/app-configuration"; +import { SecretClient } from "@azure/keyvault-secrets"; +import { DefaultAzureCredential } from "@azure/identity"; + +// Load the .env file if it exists +import * as dotenv from "dotenv"; +dotenv.config(); + +export async function main() { + console.log(`Running secretReference sample`); + const secretReference: SecretReference = { + key: `secret${new Date().getTime()}`, + secretId: `secret-key${Math.ceil(100 + Math.random() * 900)}`, + isReadOnly: false, + contentType: secretReferenceContentType + }; + + if ( + !process.env["AZURE_TENANT_ID"] || + !process.env["AZURE_CLIENT_ID"] || + !process.env["AZURE_CLIENT_SECRET"] || + !process.env["KEYVAULT_URI"] + ) { + console.log( + `At least one of the AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, and KEYVAULT_URI variables is not present, + please add the missing ones in your environment and rerun the sample.` + ); + return; + } + // DefaultAzureCredential expects the following three environment variables: + // - AZURE_TENANT_ID: The tenant ID in Azure Active Directory + // - AZURE_CLIENT_ID: The application (client) ID registered in the AAD tenant + // - AZURE_CLIENT_SECRET: The client secret for the registered application + const credential = new DefaultAzureCredential(); + const url = process.env["KEYVAULT_URI"] || ""; + + const secretClient = new SecretClient(url, credential); + // Create a secret + console.log( + `Create a keyvault secret with key: ${secretReference.secretId} and value: "MySecretValue"` + ); + await secretClient.setSecret(secretReference.secretId, "MySecretValue"); + + // Set the following environment variable or edit the value on the following line. + const connectionString = process.env["APPCONFIG_CONNECTION_STRING"] || ""; + const appConfigClient = new AppConfigurationClient(connectionString); + + await cleanupSampleValues([secretReference.key], appConfigClient); + + console.log( + `Add a new secretReference with key: ${secretReference.key} and secretId: ${secretReference.secretId}` + ); + await appConfigClient.addConfigurationSetting(secretReference); + + console.log(`Get the added secretReference from App Config with key: ${secretReference.key}`); + const getResponse = await appConfigClient.getConfigurationSetting({ + key: secretReference.key + }); + + if (isSecretReference(getResponse)) { + // isSecretReference() check for type inference to narrow down the configuration setting as a SecretReference + // setting is a `SecretReference` + // Read the secret we created + const secret = await secretClient.getSecret(getResponse.secretId); + console.log(`Get the secret from keyvault key: ${secret.name}, value: ${secret.value}`); + + console.log(`Deleting the secret from keyvault`); + await secretClient.beginDeleteSecret(getResponse.secretId); + } + + await cleanupSampleValues([secretReference.key], appConfigClient); +} + +async function cleanupSampleValues(keys: string[], client: AppConfigurationClient) { + const settingsIterator = client.listConfigurationSettings({ + keyFilter: keys.join(",") + }); + + for await (const setting of settingsIterator) { + await client.deleteConfigurationSetting({ key: setting.key, label: setting.label }); + } +} + +/** + * Returns the environment variable, throws an error if not defined. + * + * @export + * @param {string} name + */ +export function getEnvVar(name: string) { + const val = process.env[name]; + if (!val) { + throw `Environment variable ${name} is not defined.`; + } + return val; +} + +main().catch((err) => { + console.error("Failed to run sample:", err); + process.exit(1); +}); diff --git a/sdk/appconfiguration/app-configuration/samples/v1/typescript/tsconfig.json b/sdk/appconfiguration/app-configuration/samples/v1/typescript/tsconfig.json index 4e7be7e1a850..27b4e24c6a0f 100644 --- a/sdk/appconfiguration/app-configuration/samples/v1/typescript/tsconfig.json +++ b/sdk/appconfiguration/app-configuration/samples/v1/typescript/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "ES6", + "target": "ES2018", "module": "commonjs", "moduleResolution": "node", "esModuleInterop": true, From d25f2b0aa1f4820e8484fad6492d90147010ffa4 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Fri, 2 Apr 2021 13:53:37 -0700 Subject: [PATCH 47/50] added links in readme --- sdk/appconfiguration/app-configuration/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/appconfiguration/app-configuration/README.md b/sdk/appconfiguration/app-configuration/README.md index e488970ef812..abad9d0d5be8 100644 --- a/sdk/appconfiguration/app-configuration/README.md +++ b/sdk/appconfiguration/app-configuration/README.md @@ -159,6 +159,8 @@ The following samples show you the various ways you can interact with App Config - [`setReadOnlySample.ts`](https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/setReadOnlySample.ts) - Marking settings as read-only to prevent modification. - [`getSettingOnlyIfChanged.ts`](https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/getSettingOnlyIfChanged.ts) - Get a setting only if it changed from the last time you got it. - [`listRevisions.ts`](https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/listRevisions.ts) - List the revisions of a key, allowing you to see previous values and when they were set. +- [`secretReference.ts`](https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/secretReference.ts) - SecretReference represents a configuration setting that references as KeyVault secret. +- [`featureFlag.ts`](https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/appconfiguration/app-configuration/samples/v1/typescript/src/featureFlag.ts) - Feature flags are settings that follow specific JSON schema for the value. More in-depth examples can be found in the [samples](https://github.com/Azure/azure-sdk-for-js/tree/master/sdk/appconfiguration/app-configuration/samples/v1/) folder on GitHub. From 478e77d425ca3038b57031f09fdea0c5bb1e85b3 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Fri, 2 Apr 2021 13:55:44 -0700 Subject: [PATCH 48/50] delete the samplesForApiReview file --- .../test/samplesForApiReview.ts | 107 ------------------ 1 file changed, 107 deletions(-) delete mode 100644 sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts diff --git a/sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts b/sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts deleted file mode 100644 index a850052666e6..000000000000 --- a/sdk/appconfiguration/app-configuration/test/samplesForApiReview.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { AppConfigurationClient, isFeatureFlag, isSecretReference } from "../src"; -import { isFeatureFlagClientFilter } from "../src/featureFlag"; - -const appConfigClient = new AppConfigurationClient("connection-string"); - -export async function sampleSyncTokenExample() { - // get sync token from EventGrid event (example event shown, based on .net sample) - const eventGridEvent = { - key: "key for setting", - label: "label for setting", - syncToken: "opaque sync token" - }; - - // user treats token as an opaque value (note - this _does_ mutate the appconfigclient) - // however, this mutations happens regardless since the system can and does return - // sync token headers during normal operation. - appConfigClient.updateSyncToken(eventGridEvent.syncToken); - - // and now retrieving the value should be consistent with what happened - // in EventGrid. - const retrievedSetting = await appConfigClient.getConfigurationSetting({ - key: eventGridEvent.key, - label: eventGridEvent.label - }); - - console.log(retrievedSetting); -} - -export async function sampleKeyVaultReference() { - const secretClient: SecretClient = {} as any; // An actual KeyVault SecretClient gotten from "somewhere" else - - const setting = await appConfigClient.getConfigurationSetting({ - key: "my keyvault reference" - }); - - if (isSecretReference(setting)) { - // setting is a `KeyVaultReference` - - // use KeyVault to parse secret ID and retrieve it. - const parsedSecretId = parseKeyVaultSecretId(setting.secretId); - const actualSecret = await secretClient.getSecret(parsedSecretId.name); - - console.log(`Retrieved secret value: ${actualSecret.value}`); - } else { - // otherwise it's potentially a feature flag or just a plain ConfigurationSetting - } -} - -export async function sampleFeatureFlag() { - const setting = await appConfigClient.getConfigurationSetting({ - key: "my feature flag" - }); - - if (isFeatureFlag(setting)) { - // setting is a `FeatureFlag` - - const conditions = setting.conditions; - - // FeatureFlag specific properties: - // - // setting.displayName - // setting.enabled - - // - // the client filters are the real meat of the FeatureFlag. - // - for (const clientFilter of conditions.clientFilters) { - if (isFeatureFlagClientFilter("targeting", clientFilter)) { - // some of the fields: - // clientFilter.parameters.audience - // clientFilter.parameters.audience.groups[0].name - // clientFilter.parameters.audience.groups[0].rolloutPercentage - // clientFilter.parameters.audience.users[0] // string - // clientFilter.parameters.defaultRolloutPercentage - } else if (isFeatureFlagClientFilter("timeWindow", clientFilter)) { - // clientFilter.parameters.end; - // clientFilter.parameters.start; - } else if (isFeatureFlagClientFilter("percentage", clientFilter)) { - // TODO: didn't analyze this one. Just comes back as key/value pairs, perhaps with a - // numeric constraint? - } else { - // we return a generic object and they get whatever was deserialized. - } - } - } else { - // otherwise it's a KeyVaultReference or just a ConfigurationSetting - } -} - -/** BEGIN: Interfaces from KeyVault */ -export interface SecretClient { - getSecret(secretName: string): Promise; -} - -export function parseKeyVaultSecretId(_id: string): KeyVaultSecretId { - throw new Error("For sample only"); -} - -export interface KeyVaultSecretId { - /** other fields elided ... */ - name: string; -} -/** END: Interfaces from KeyVault */ - -export interface KeyVaultSecret { - value?: string; -} From 3cc458e1ad84a8dac412240fe7e4f6c30fc8afe8 Mon Sep 17 00:00:00 2001 From: HarshaNalluru Date: Fri, 2 Apr 2021 14:21:29 -0700 Subject: [PATCH 49/50] Mark /** * @internal */ --- sdk/appconfiguration/app-configuration/src/featureFlag.ts | 6 ++++++ .../app-configuration/src/keyvaultReference.ts | 3 +++ 2 files changed, 9 insertions(+) diff --git a/sdk/appconfiguration/app-configuration/src/featureFlag.ts b/sdk/appconfiguration/app-configuration/src/featureFlag.ts index 714e4b1af044..02c42bfcdac0 100644 --- a/sdk/appconfiguration/app-configuration/src/featureFlag.ts +++ b/sdk/appconfiguration/app-configuration/src/featureFlag.ts @@ -211,6 +211,9 @@ export function isFeatureFlagClientFilter Date: Fri, 2 Apr 2021 14:36:47 -0700 Subject: [PATCH 50/50] API Report --- .../app-configuration/review/app-configuration.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md index 8398fffc3057..7c392d7f68f3 100644 --- a/sdk/appconfiguration/app-configuration/review/app-configuration.api.md +++ b/sdk/appconfiguration/app-configuration/review/app-configuration.api.md @@ -168,7 +168,7 @@ export interface HttpResponseFields { export function isFeatureFlag(setting: ConfigurationSetting | FeatureFlag): setting is FeatureFlag; // @public -export function isFeatureFlagClientFilter(type: T, obj: any): obj is FeatureFlagType; +export function isFeatureFlagClientFilter(type: T, obj: unknown): obj is FeatureFlagType; // @public export function isSecretReference(setting: ConfigurationSetting): setting is SecretReference;