Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dedup exact same selectors while keep original precedence #31

Merged
merged 5 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions src/AzureAppConfigurationImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { AzureAppConfiguration } from "./AzureAppConfiguration";
import { AzureAppConfigurationOptions } from "./AzureAppConfigurationOptions";
import { IKeyValueAdapter } from "./IKeyValueAdapter";
import { JsonKeyValueAdapter } from "./JsonKeyValueAdapter";
import { KeyFilter } from "./KeyFilter";
import { LabelFilter } from "./LabelFilter";
import { KeyFilter, LabelFilter } from "./types";
import { AzureKeyVaultKeyValueAdapter } from "./keyvault/AzureKeyVaultKeyValueAdapter";
import { CorrelationContextHeaderName } from "./requestTracing/constants";
import { createCorrelationContextHeader, requestTracingEnabled } from "./requestTracing/utils";
import { SettingSelector } from "./types";

export class AzureAppConfigurationImpl extends Map<string, unknown> implements AzureAppConfiguration {
private adapters: IKeyValueAdapter[] = [];
Expand Down Expand Up @@ -109,12 +109,23 @@ export class AzureAppConfigurationImpl extends Map<string, unknown> implements A
}
}

function getValidSelectors(selectors?: { keyFilter: string, labelFilter?: string }[]) {
function getValidSelectors(selectors?: SettingSelector[]) {
if (!selectors || selectors.length === 0) {
// Default selector: key: *, label: \0
return [{ keyFilter: KeyFilter.Any, labelFilter: LabelFilter.Null }];
}
return selectors.map(selectorCandidate => {

// below code dedupes selectors by keyFilter and labelFilter, the latter selector wins
const dedupedSelectors: SettingSelector[] = [];
for (const selector of selectors) {
const existingSelectorIndex = dedupedSelectors.findIndex(s => s.keyFilter === selector.keyFilter && s.labelFilter === selector.labelFilter);
if (existingSelectorIndex >= 0) {
dedupedSelectors.splice(existingSelectorIndex, 1);
}
dedupedSelectors.push(selector);
}

return dedupedSelectors.map(selectorCandidate => {
const selector = { ...selectorCandidate };
if (!selector.keyFilter) {
throw new Error("Key filter cannot be null or empty.");
Expand Down
22 changes: 4 additions & 18 deletions src/AzureAppConfigurationOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,17 @@

import { AppConfigurationClientOptions } from "@azure/app-configuration";
import { AzureAppConfigurationKeyVaultOptions } from "./keyvault/AzureAppConfigurationKeyVaultOptions";
import { SettingSelector } from "./types";

export const MaxRetries = 2;
export const MaxRetryDelayInMs = 60000;

export interface AzureAppConfigurationOptions {
/**
* Specify what key-values to include in the configuration provider. include multiple sets of key-values
*
* @property keyFilter:
* The key filter to apply when querying Azure App Configuration for key-values.
* An asterisk `*` can be added to the end to return all key-values whose key begins with the key filter.
* e.g. key filter `abc*` returns all key-values whose key starts with `abc`.
* A comma `,` can be used to select multiple key-values. Comma separated filters must exactly match a key to select it.
* Using asterisk to select key-values that begin with a key filter while simultaneously using comma separated key filters is not supported.
* E.g. the key filter `abc*,def` is not supported. The key filters `abc*` and `abc,def` are supported.
* For all other cases the characters: asterisk `*`, comma `,`, and backslash `\` are reserved. Reserved characters must be escaped using a backslash (\).
* e.g. the key filter `a\\b\,\*c*` returns all key-values whose key starts with `a\b,*c`.
*
* @property labelFilter:
* The label filter to apply when querying Azure App Configuration for key-values.
* By default, the "null label" will be used, matching key-values without a label.
* The characters asterisk `*` and comma `,` are not supported.
* Backslash `\` character is reserved and must be escaped using another backslash `\`.
* Specify what key-values to include in the configuration provider.
* If no selectors are specified then all key-values with no label will be included.
*/
selectors?: { keyFilter: string, labelFilter?: string }[];
selectors?: SettingSelector[];

/**
* Specifies prefixes to be trimmed from the keys of all key-values retrieved from Azure App Configuration.
Expand Down
6 changes: 0 additions & 6 deletions src/KeyFilter.ts

This file was deleted.

6 changes: 0 additions & 6 deletions src/LabelFilter.ts

This file was deleted.

3 changes: 1 addition & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@

export { load } from "./load";
export { AzureAppConfiguration } from "./AzureAppConfiguration";
export { KeyFilter } from "./KeyFilter";
export { LabelFilter } from "./LabelFilter";
export { KeyFilter, LabelFilter } from "./types";
44 changes: 44 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

/**
* SettingSelector is used to select key-values from Azure App Configuration.
* It is used to filter key-values based on keys and labels.
*
* @property keyFilter:
* The key filter to apply when querying Azure App Configuration for key-values.
* An asterisk `*` can be added to the end to return all key-values whose key begins with the key filter.
* e.g. key filter `abc*` returns all key-values whose key starts with `abc`.
* A comma `,` can be used to select multiple key-values. Comma separated filters must exactly match a key to select it.
* Using asterisk to select key-values that begin with a key filter while simultaneously using comma separated key filters is not supported.
* E.g. the key filter `abc*,def` is not supported. The key filters `abc*` and `abc,def` are supported.
* For all other cases the characters: asterisk `*`, comma `,`, and backslash `\` are reserved. Reserved characters must be escaped using a backslash (\).
* e.g. the key filter `a\\b\,\*c*` returns all key-values whose key starts with `a\b,*c`.
*
* @property labelFilter:
* The label filter to apply when querying Azure App Configuration for key-values.
* By default, the "null label" will be used, matching key-values without a label.
* The characters asterisk `*` and comma `,` are not supported.
* Backslash `\` character is reserved and must be escaped using another backslash `\`.
*/
export type SettingSelector = { keyFilter: string, labelFilter?: string };

/**
* KeyFilter is used to filter key-values based on keys.
*
* @property Any:
* Matches all key-values.
*/
export enum KeyFilter {
Any = "*"
}

/**
* LabelFilter is used to filter key-values based on labels.
*
* @property Null:
* Matches key-values without a label.
*/
export enum LabelFilter {
Null = "\0"
}
22 changes: 21 additions & 1 deletion test/load.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,24 @@ describe("load", function () {
expect(settings.has("TestKey")).eq(true);
expect(settings.get("TestKey")).eq("TestValueForProd");
});
})

it("should dedup exact same selectors but keeping the precedence", async () => {
const connectionString = createMockedConnectionString();
const settings = await load(connectionString, {
selectors: [{
keyFilter: "Test*",
labelFilter: "Prod"
}, {
keyFilter: "Test*",
labelFilter: "Test"
}, {
keyFilter: "Test*",
labelFilter: "Prod"
}]
});
expect(settings).not.undefined;
expect(settings.has("TestKey")).eq(true);
expect(settings.get("TestKey")).eq("TestValueForProd");
});

});