Skip to content

Commit f62bc58

Browse files
committed
resolve comments
1 parent 376ce71 commit f62bc58

14 files changed

Lines changed: 260 additions & 123 deletions

src/appConfigurationClient.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
import {
5+
AppConfigurationClient as ConfigurationClient,
6+
AppConfigurationClientOptions as ConfigurationClientOptions,
7+
CheckConfigurationSettingsOptions,
8+
ConfigurationSettingId,
9+
FeatureFlagClient,
10+
FeatureFlagClientOptions,
11+
GetConfigurationSettingOptions,
12+
GetSnapshotOptions,
13+
ListConfigurationSettingsForSnapshotOptions,
14+
ListConfigurationSettingsOptions,
15+
ListFeatureFlagsOptions
16+
} from "@azure/app-configuration";
17+
import { TokenCredential } from "@azure/identity";
18+
import { instanceOfTokenCredential } from "./common/utils.js";
19+
import { RequestTracingOptions, applyRequestTracing } from "./requestTracing/utils.js";
20+
21+
/**
22+
* A client for the operations the provider needs from both the @see ConfigurationClient and
23+
* @see FeatureFlagClient SDK clients. Request tracing is applied before delegating each operation.
24+
*/
25+
export class AppConfigurationClient {
26+
#configurationClient: ConfigurationClient;
27+
#featureFlagClient: FeatureFlagClient;
28+
29+
constructor(connectionString: string, options?: ConfigurationClientOptions);
30+
constructor(endpoint: string, credential: TokenCredential, options?: ConfigurationClientOptions);
31+
constructor(
32+
connectionStringOrEndpoint: string,
33+
credentialOrOptions?: TokenCredential | ConfigurationClientOptions,
34+
options?: ConfigurationClientOptions
35+
) {
36+
const credentialPassed = instanceOfTokenCredential(credentialOrOptions);
37+
const configurationClientOptions = credentialPassed
38+
? options
39+
: credentialOrOptions as ConfigurationClientOptions | undefined;
40+
const featureFlagClientOptions = getFeatureFlagClientOptions(configurationClientOptions);
41+
42+
if (credentialPassed) {
43+
const credential = credentialOrOptions as TokenCredential;
44+
this.#configurationClient = new ConfigurationClient(connectionStringOrEndpoint, credential, configurationClientOptions);
45+
this.#featureFlagClient = new FeatureFlagClient(connectionStringOrEndpoint, credential, featureFlagClientOptions);
46+
} else {
47+
this.#configurationClient = new ConfigurationClient(connectionStringOrEndpoint, configurationClientOptions);
48+
this.#featureFlagClient = new FeatureFlagClient(connectionStringOrEndpoint, featureFlagClientOptions);
49+
}
50+
}
51+
52+
listConfigurationSettings(listOptions: ListConfigurationSettingsOptions, tracingOptions: RequestTracingOptions) {
53+
return this.#configurationClient.listConfigurationSettings(applyRequestTracing(tracingOptions, listOptions));
54+
}
55+
56+
checkConfigurationSettings(checkOptions: CheckConfigurationSettingsOptions, tracingOptions: RequestTracingOptions) {
57+
return this.#configurationClient.checkConfigurationSettings(applyRequestTracing(tracingOptions, checkOptions));
58+
}
59+
60+
getConfigurationSetting(configurationSettingId: ConfigurationSettingId, getOptions: GetConfigurationSettingOptions | undefined, tracingOptions: RequestTracingOptions) {
61+
return this.#configurationClient.getConfigurationSetting(configurationSettingId, applyRequestTracing(tracingOptions, getOptions));
62+
}
63+
64+
getSnapshot(snapshotName: string, getOptions: GetSnapshotOptions | undefined, tracingOptions: RequestTracingOptions) {
65+
return this.#configurationClient.getSnapshot(snapshotName, applyRequestTracing(tracingOptions, getOptions));
66+
}
67+
68+
listConfigurationSettingsForSnapshot(snapshotName: string, listOptions: ListConfigurationSettingsForSnapshotOptions | undefined, tracingOptions: RequestTracingOptions) {
69+
return this.#configurationClient.listConfigurationSettingsForSnapshot(snapshotName, applyRequestTracing(tracingOptions, listOptions));
70+
}
71+
72+
listFeatureFlags(listOptions: ListFeatureFlagsOptions, tracingOptions: RequestTracingOptions) {
73+
return this.#featureFlagClient.listFeatureFlags(applyRequestTracing(tracingOptions, listOptions));
74+
}
75+
}
76+
77+
export function getFeatureFlagClientOptions(options?: ConfigurationClientOptions): FeatureFlagClientOptions | undefined {
78+
if (options === undefined) {
79+
return undefined;
80+
}
81+
82+
return {
83+
...options,
84+
...(options.retryOptions && { retryOptions: { ...options.retryOptions } }),
85+
...(options.proxyOptions && { proxyOptions: { ...options.proxyOptions } }),
86+
...(options.tlsOptions && { tlsOptions: { ...options.tlsOptions } }),
87+
...(options.userAgentOptions && { userAgentOptions: { ...options.userAgentOptions } }),
88+
...(options.telemetryOptions && { telemetryOptions: { ...options.telemetryOptions } }),
89+
...(options.additionalPolicies && { additionalPolicies: [...options.additionalPolicies] })
90+
};
91+
}
Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33

4-
import { AppConfigurationClient, AppConfigurationClientOptions, FeatureFlagClient } from "@azure/app-configuration";
5-
import { AppConfigClient } from "./appConfigClient.js";
6-
import { ConfigurationClientWrapper } from "./configurationClientWrapper.js";
4+
import { AppConfigurationClientOptions as ConfigurationClientOptions } from "@azure/app-configuration";
5+
import { AppConfigurationClient } from "./appConfigurationClient.js";
6+
import { AppConfigurationClientWrapper } from "./appConfigurationClientWrapper.js";
77
import { TokenCredential } from "@azure/identity";
88
import { AzureAppConfigurationOptions } from "./appConfigurationOptions.js";
99
import { isBrowser, isWebWorker } from "./requestTracing/utils.js";
@@ -29,18 +29,18 @@ const DNS_RESOLVER_TIMEOUT_IN_MS = 3_000;
2929
const DNS_RESOLVER_TRIES = 2;
3030
const MAX_ALTNATIVE_SRV_COUNT = 10;
3131

32-
export class ConfigurationClientManager {
32+
export class AppConfigurationClientManager {
3333
readonly endpoint: URL; // primary endpoint, which is the one specified in the connection string or passed in as a parameter
3434
#isFailoverable: boolean;
3535
#dns: any;
3636
#secret : string;
3737
#id : string;
3838
#credential: TokenCredential;
39-
#clientOptions: AppConfigurationClientOptions | undefined;
39+
#clientOptions: ConfigurationClientOptions | undefined;
4040
#appConfigOptions: AzureAppConfigurationOptions | undefined;
4141
#validDomain: string; // valid domain for the primary endpoint, which is used to discover replicas
42-
#staticClients: ConfigurationClientWrapper[]; // there should always be only one static client
43-
#dynamicClients: ConfigurationClientWrapper[];
42+
#staticClients: AppConfigurationClientWrapper[]; // there should always be only one static client
43+
#dynamicClients: AppConfigurationClientWrapper[];
4444
#replicaCount: number = 0;
4545
#lastFallbackClientUpdateTime: number = 0; // enforce to discover fallback client when it is expired
4646
#lastFallbackClientRefreshAttempt: number = 0; // avoid refreshing clients before the minimal refresh interval
@@ -50,8 +50,7 @@ export class ConfigurationClientManager {
5050
credentialOrOptions?: TokenCredential | AzureAppConfigurationOptions,
5151
appConfigOptions?: AzureAppConfigurationOptions
5252
) {
53-
let staticConfigurationClient: AppConfigurationClient;
54-
let staticFeatureFlagClient: FeatureFlagClient;
53+
let staticClient: AppConfigurationClient;
5554
const credentialPassed = instanceOfTokenCredential(credentialOrOptions);
5655

5756
if (typeof connectionStringOrEndpoint === "string" && !credentialPassed) {
@@ -68,8 +67,7 @@ export class ConfigurationClientManager {
6867
} else {
6968
throw new ArgumentError(`Invalid connection string. Valid connection strings should match the regex '${ConnectionStringRegex.source}'.`);
7069
}
71-
staticConfigurationClient = new AppConfigurationClient(connectionString, this.#clientOptions);
72-
staticFeatureFlagClient = new FeatureFlagClient(connectionString, this.#clientOptions);
70+
staticClient = new AppConfigurationClient(connectionString, this.#clientOptions);
7371
} else if ((connectionStringOrEndpoint instanceof URL || typeof connectionStringOrEndpoint === "string") && credentialPassed) {
7472
let endpoint = connectionStringOrEndpoint;
7573
// ensure string is a valid URL.
@@ -82,13 +80,12 @@ export class ConfigurationClientManager {
8280
this.#clientOptions = getClientOptions(this.#appConfigOptions);
8381
this.endpoint = endpoint;
8482
this.#credential = credential;
85-
staticConfigurationClient = new AppConfigurationClient(this.endpoint.origin, this.#credential, this.#clientOptions);
86-
staticFeatureFlagClient = new FeatureFlagClient(this.endpoint.origin, this.#credential, this.#clientOptions);
83+
staticClient = new AppConfigurationClient(this.endpoint.origin, this.#credential, this.#clientOptions);
8784
} else {
8885
throw new ArgumentError(ErrorMessages.CONNECTION_STRING_OR_ENDPOINT_MISSED);
8986
}
9087

91-
this.#staticClients = [new ConfigurationClientWrapper(this.endpoint.origin, new AppConfigClient(this.endpoint.origin, staticConfigurationClient, staticFeatureFlagClient))];
88+
this.#staticClients = [new AppConfigurationClientWrapper(this.endpoint.origin, staticClient)];
9289
this.#validDomain = getValidDomain(this.endpoint.hostname.toLowerCase());
9390
}
9491

@@ -117,7 +114,7 @@ export class ConfigurationClientManager {
117114
return this.#replicaCount;
118115
}
119116

120-
async getClients(): Promise<ConfigurationClientWrapper[]> {
117+
async getClients(): Promise<AppConfigurationClientWrapper[]> {
121118
if (!this.#isFailoverable) {
122119
return this.#staticClients;
123120
}
@@ -163,20 +160,17 @@ export class ConfigurationClientManager {
163160
}
164161

165162
const srvTargetHosts = shuffleList(result);
166-
const newDynamicClients: ConfigurationClientWrapper[] = [];
163+
const newDynamicClients: AppConfigurationClientWrapper[] = [];
167164
for (const host of srvTargetHosts) {
168165
if (isValidEndpoint(host, this.#validDomain)) {
169166
const targetEndpoint = `https://${host}`;
170167
if (host.toLowerCase() === this.endpoint.hostname.toLowerCase()) {
171168
continue;
172169
}
173-
const configurationClient = this.#credential ?
170+
const appConfigurationClient = this.#credential ?
174171
new AppConfigurationClient(targetEndpoint, this.#credential, this.#clientOptions) :
175172
new AppConfigurationClient(buildConnectionString(targetEndpoint, this.#secret, this.#id), this.#clientOptions);
176-
const featureFlagClient = this.#credential ?
177-
new FeatureFlagClient(targetEndpoint, this.#credential, this.#clientOptions) :
178-
new FeatureFlagClient(buildConnectionString(targetEndpoint, this.#secret, this.#id), this.#clientOptions);
179-
newDynamicClients.push(new ConfigurationClientWrapper(targetEndpoint, new AppConfigClient(targetEndpoint, configurationClient, featureFlagClient)));
173+
newDynamicClients.push(new AppConfigurationClientWrapper(targetEndpoint, appConfigurationClient));
180174
}
181175
}
182176

@@ -263,7 +257,7 @@ export function isValidEndpoint(host: string, validDomain: string): boolean {
263257
return host.toLowerCase().endsWith(validDomain.toLowerCase());
264258
}
265259

266-
function getClientOptions(options?: AzureAppConfigurationOptions): AppConfigurationClientOptions | undefined {
260+
export function getClientOptions(options?: AzureAppConfigurationOptions): ConfigurationClientOptions {
267261
// user-agent
268262
let userAgentPrefix = RequestTracing.USER_AGENT_PREFIX; // Default UA for JavaScript Provider
269263
const userAgentOptions = options?.clientOptions?.userAgentOptions;
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33

4-
import { IAppConfigurationClient } from "./appConfigClient.js";
4+
import { AppConfigurationClient } from "./appConfigurationClient.js";
55
import { getExponentialBackoffDuration } from "./common/backoffUtils.js";
66

7-
export class ConfigurationClientWrapper {
7+
export class AppConfigurationClientWrapper {
88
endpoint: string;
9-
client: IAppConfigurationClient;
9+
client: AppConfigurationClient;
1010
backoffEndTime: number = 0; // Timestamp
1111
#failedAttempts: number = 0;
1212

13-
constructor(endpoint: string, client: IAppConfigurationClient) {
13+
constructor(endpoint: string, client: AppConfigurationClient) {
1414
this.endpoint = endpoint;
1515
this.client = client;
1616
}

0 commit comments

Comments
 (0)