UNPKG

@azure/app-configuration

Version:

An isomorphic client library for the Azure App Configuration service.

579 lines • 29.8 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import { getPagedAsyncIterator } from "@azure/core-paging"; import { bearerTokenAuthenticationPolicy } from "@azure/core-rest-pipeline"; import { SyncTokens, syncTokenPolicy } from "./internal/synctokenpolicy.js"; import { isTokenCredential } from "@azure/core-auth"; import { assertResponse, checkAndFormatIfAndIfNoneMatch, extractAfterTokenFromLinkHeader, extractAfterTokenFromNextLink, formatAcceptDateTime, formatConfigurationSettingsFiltersAndSelect, formatFieldsForSelect, formatFiltersAndSelect, formatLabelsFiltersAndSelect, formatSnapshotFiltersAndSelect, getScope, makeConfigurationSettingEmpty, serializeAsConfigurationSettingParam, transformKeyValue, transformKeyValueResponse, transformKeyValueResponseWithStatusCode, transformSnapshotResponse, } from "./internal/helpers.js"; import { AppConfiguration } from "./generated/src/appConfiguration.js"; import { appConfigKeyCredentialPolicy } from "./appConfigCredential.js"; import { tracingClient } from "./internal/tracing.js"; import { logger } from "./logger.js"; import { appConfigurationApiVersion } from "./internal/constants.js"; const ConnectionStringRegex = /Endpoint=(.*);Id=(.*);Secret=(.*)/; const deserializationContentTypes = { json: [ "application/vnd.microsoft.appconfig.kvset+json", "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.snapshotset+json", "application/vnd.microsoft.appconfig.snapshot+json", "application/vnd.microsoft.appconfig.labelset+json", "application/json", ], }; /** * Client for the Azure App Configuration service. */ export class AppConfigurationClient { constructor(connectionStringOrEndpoint, tokenCredentialOrOptions, options) { var _a; let appConfigOptions = {}; let appConfigCredential; let appConfigEndpoint; let authPolicy; let scope; if (isTokenCredential(tokenCredentialOrOptions)) { appConfigOptions = options || {}; appConfigCredential = tokenCredentialOrOptions; appConfigEndpoint = connectionStringOrEndpoint.endsWith("/") ? connectionStringOrEndpoint.slice(0, -1) : connectionStringOrEndpoint; scope = getScope(appConfigEndpoint, appConfigOptions.audience); authPolicy = bearerTokenAuthenticationPolicy({ scopes: scope, credential: appConfigCredential, }); } else { appConfigOptions = tokenCredentialOrOptions || {}; const regexMatch = connectionStringOrEndpoint === null || connectionStringOrEndpoint === void 0 ? void 0 : connectionStringOrEndpoint.match(ConnectionStringRegex); if (regexMatch) { appConfigEndpoint = regexMatch[1]; authPolicy = appConfigKeyCredentialPolicy(regexMatch[2], regexMatch[3]); } else { throw new Error(`Invalid connection string. Valid connection strings should match the regex '${ConnectionStringRegex.source}'.` + ` To mitigate the issue, please refer to the troubleshooting guide here at https://aka.ms/azsdk/js/app-configuration/troubleshoot.`); } } const internalClientPipelineOptions = Object.assign(Object.assign({}, appConfigOptions), { loggingOptions: { logger: logger.info, }, deserializationOptions: { expectedContentTypes: deserializationContentTypes, } }); this._syncTokens = appConfigOptions.syncTokens || new SyncTokens(); this.client = new AppConfiguration(appConfigEndpoint, (_a = options === null || options === void 0 ? void 0 : options.apiVersion) !== null && _a !== void 0 ? _a : appConfigurationApiVersion, internalClientPipelineOptions); this.client.pipeline.addPolicy(authPolicy, { phase: "Sign" }); this.client.pipeline.addPolicy(syncTokenPolicy(this._syncTokens), { afterPhase: "Retry" }); } /** * Add a setting into the Azure App Configuration service, failing if it * already exists. * * Example usage: * ```ts snippet:AddConfigurationSetting * import { DefaultAzureCredential } from "@azure/identity"; * import { AppConfigurationClient } from "@azure/app-configuration"; * * // The endpoint for your App Configuration resource * const endpoint = "https://example.azconfig.io"; * const credential = new DefaultAzureCredential(); * const client = new AppConfigurationClient(endpoint, credential); * * const result = await client.addConfigurationSetting({ * key: "MyKey", * label: "MyLabel", * value: "MyValue", * }); * ``` * @param configurationSetting - A configuration setting. * @param options - Optional parameters for the request. */ addConfigurationSetting(configurationSetting, options = {}) { return tracingClient.withSpan("AppConfigurationClient.addConfigurationSetting", options, async (updatedOptions) => { const keyValue = serializeAsConfigurationSettingParam(configurationSetting); logger.info("[addConfigurationSetting] Creating a key value pair"); try { const originalResponse = await this.client.putKeyValue(configurationSetting.key, Object.assign({ ifNoneMatch: "*", label: configurationSetting.label, entity: keyValue }, updatedOptions)); const response = transformKeyValueResponse(originalResponse); assertResponse(response); return response; } catch (error) { const err = error; // Service does not return an error message. Raise a 412 error similar to .NET if (err.statusCode === 412) { err.message = `Status 412: Setting was already present`; } throw err; } throw new Error("Unreachable code"); }); } /** * Delete a setting from the Azure App Configuration service * * Example usage: * ```ts snippet:DeleteConfigurationSetting * import { DefaultAzureCredential } from "@azure/identity"; * import { AppConfigurationClient } from "@azure/app-configuration"; * * // The endpoint for your App Configuration resource * const endpoint = "https://example.azconfig.io"; * const credential = new DefaultAzureCredential(); * const client = new AppConfigurationClient(endpoint, credential); * * const deletedSetting = await client.deleteConfigurationSetting({ * key: "MyKey", * label: "MyLabel", * }); * ``` * @param id - The id of the configuration setting to delete. * @param options - Optional parameters for the request (ex: etag, label) */ deleteConfigurationSetting(id, options = {}) { return tracingClient.withSpan("AppConfigurationClient.deleteConfigurationSetting", options, async (updatedOptions) => { let status; logger.info("[deleteConfigurationSetting] Deleting key value pair"); const originalResponse = await this.client.deleteKeyValue(id.key, Object.assign(Object.assign(Object.assign({ label: id.label }, updatedOptions), checkAndFormatIfAndIfNoneMatch(id, options)), { onResponse: (response) => { status = response.status; } })); const response = transformKeyValueResponseWithStatusCode(originalResponse, status); assertResponse(response); return response; }); } /** * Gets a setting from the Azure App Configuration service. * * Example code: * ```ts snippet:GetConfigurationSetting * import { DefaultAzureCredential } from "@azure/identity"; * import { AppConfigurationClient } from "@azure/app-configuration"; * * // The endpoint for your App Configuration resource * const endpoint = "https://example.azconfig.io"; * const credential = new DefaultAzureCredential(); * const client = new AppConfigurationClient(endpoint, credential); * * const setting = await client.getConfigurationSetting({ key: "MyKey", label: "MyLabel" }); * ``` * @param id - The id of the configuration setting to get. * @param options - Optional parameters for the request. */ async getConfigurationSetting(id, options = {}) { return tracingClient.withSpan("AppConfigurationClient.getConfigurationSetting", options, async (updatedOptions) => { let status; logger.info("[getConfigurationSetting] Getting key value pair"); const originalResponse = await this.client.getKeyValue(id.key, Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, updatedOptions), { label: id.label, select: formatFieldsForSelect(options.fields) }), formatAcceptDateTime(options)), checkAndFormatIfAndIfNoneMatch(id, options)), { onResponse: (response) => { status = response.status; } })); const response = transformKeyValueResponseWithStatusCode(originalResponse, status); // 304 only comes back if the user has passed a conditional option in their // request _and_ the remote object has the same etag as what the user passed. if (response.statusCode === 304) { // this is one of our few 'required' fields so we'll make sure it does get initialized // with a value response.key = id.key; // and now we'll undefine all the other properties that are not HTTP related makeConfigurationSettingEmpty(response); } assertResponse(response); return response; }); } /** * Lists settings from the Azure App Configuration service, optionally * filtered by key names, labels and accept datetime. * * Example code: * ```ts snippet:ListConfigurationSettings * import { DefaultAzureCredential } from "@azure/identity"; * import { AppConfigurationClient } from "@azure/app-configuration"; * * // The endpoint for your App Configuration resource * const endpoint = "https://example.azconfig.io"; * const credential = new DefaultAzureCredential(); * const client = new AppConfigurationClient(endpoint, credential); * * const allSettingsWithLabel = client.listConfigurationSettings({ labelFilter: "MyLabel" }); * ``` * @param options - Optional parameters for the request. */ listConfigurationSettings(options = {}) { const pageEtags = options.pageEtags ? [...options.pageEtags] : undefined; delete options.pageEtags; const pagedResult = { firstPageLink: undefined, getPage: async (pageLink) => { var _a, _b, _c; const etag = pageEtags === null || pageEtags === void 0 ? void 0 : pageEtags.shift(); try { const response = await this.sendConfigurationSettingsRequest(Object.assign(Object.assign({}, options), { etag }), pageLink); const currentResponse = Object.assign(Object.assign({}, response), { items: response.items != null ? (_a = response.items) === null || _a === void 0 ? void 0 : _a.map(transformKeyValue) : [], continuationToken: response.nextLink ? extractAfterTokenFromNextLink(response.nextLink) : undefined, _response: response._response }); return { page: currentResponse, nextPageLink: currentResponse.continuationToken, }; } catch (error) { const err = error; const link = (_c = (_b = err.response) === null || _b === void 0 ? void 0 : _b.headers) === null || _c === void 0 ? void 0 : _c.get("link"); const continuationToken = link ? extractAfterTokenFromLinkHeader(link) : undefined; if (err.statusCode === 304) { err.message = `Status 304: No updates for this page`; logger.info(`[listConfigurationSettings] No updates for this page. The current etag for the page is ${etag}`); return { page: { items: [], etag, _response: Object.assign(Object.assign({}, err.response), { status: 304 }), }, nextPageLink: continuationToken, }; } throw err; } }, toElements: (page) => page.items, }; return getPagedAsyncIterator(pagedResult); } /** * Lists settings from the Azure App Configuration service for snapshots based on name, optionally * filtered by key names, labels and accept datetime. * * Example code: * ```ts snippet:ListConfigurationSettingsForSnashots * import { DefaultAzureCredential } from "@azure/identity"; * import { AppConfigurationClient } from "@azure/app-configuration"; * * // The endpoint for your App Configuration resource * const endpoint = "https://example.azconfig.io"; * const credential = new DefaultAzureCredential(); * const client = new AppConfigurationClient(endpoint, credential); * * const allSettingsWithLabel = client.listConfigurationSettingsForSnashots({ * snapshotName: "MySnapshot", * }); * ``` * @param options - Optional parameters for the request. */ listConfigurationSettingsForSnapshot(snapshotName, options = {}) { const pagedResult = { firstPageLink: undefined, getPage: async (pageLink) => { var _a; const response = await this.sendConfigurationSettingsRequest(Object.assign({ snapshotName }, options), pageLink); const currentResponse = Object.assign(Object.assign({}, response), { items: response.items != null ? (_a = response.items) === null || _a === void 0 ? void 0 : _a.map(transformKeyValue) : [], continuationToken: response.nextLink ? extractAfterTokenFromNextLink(response.nextLink) : undefined }); return { page: currentResponse, nextPageLink: currentResponse.continuationToken, }; }, toElements: (page) => page.items, }; return getPagedAsyncIterator(pagedResult); } /** * Get a list of labels from the Azure App Configuration service * * Example code: * ```ts snippet:ListLabels * import { DefaultAzureCredential } from "@azure/identity"; * import { AppConfigurationClient } from "@azure/app-configuration"; * * // The endpoint for your App Configuration resource * const endpoint = "https://example.azconfig.io"; * const credential = new DefaultAzureCredential(); * const client = new AppConfigurationClient(endpoint, credential); * * const allSettingsWithLabel = client.listLabels({ nameFilter: "prod*" }); * ``` * @param options - Optional parameters for the request. */ listLabels(options = {}) { const pagedResult = { firstPageLink: undefined, getPage: async (pageLink) => { var _a; const response = await this.sendLabelsRequest(options, pageLink); const currentResponse = Object.assign(Object.assign({}, response), { items: (_a = response.items) !== null && _a !== void 0 ? _a : [], continuationToken: response.nextLink ? extractAfterTokenFromNextLink(response.nextLink) : undefined, _response: response._response }); return { page: currentResponse, nextPageLink: currentResponse.continuationToken, }; }, toElements: (page) => page.items, }; return getPagedAsyncIterator(pagedResult); } async sendLabelsRequest(options = {}, pageLink) { return tracingClient.withSpan("AppConfigurationClient.listConfigurationSettings", options, async (updatedOptions) => { const response = await this.client.getLabels(Object.assign(Object.assign(Object.assign(Object.assign({}, updatedOptions), formatAcceptDateTime(options)), formatLabelsFiltersAndSelect(options)), { after: pageLink })); return response; }); } async sendConfigurationSettingsRequest(options = {}, pageLink) { return tracingClient.withSpan("AppConfigurationClient.listConfigurationSettings", options, async (updatedOptions) => { const response = await this.client.getKeyValues(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, updatedOptions), formatAcceptDateTime(options)), formatConfigurationSettingsFiltersAndSelect(options)), checkAndFormatIfAndIfNoneMatch({ etag: options.etag }, { onlyIfChanged: true })), { after: pageLink })); return response; }); } /** * Lists revisions of a set of keys, optionally filtered by key names, * labels and accept datetime. * * Example code: * ```ts snippet:ListRevisions * import { DefaultAzureCredential } from "@azure/identity"; * import { AppConfigurationClient } from "@azure/app-configuration"; * * // The endpoint for your App Configuration resource * const endpoint = "https://example.azconfig.io"; * const credential = new DefaultAzureCredential(); * const client = new AppConfigurationClient(endpoint, credential); * * const revisionsIterator = client.listRevisions({ keys: ["MyKey"] }); * ``` * @param options - Optional parameters for the request. */ listRevisions(options) { const pagedResult = { firstPageLink: undefined, getPage: async (pageLink) => { const response = await this.sendRevisionsRequest(options, pageLink); const currentResponse = Object.assign(Object.assign({}, response), { items: response.items != null ? response.items.map(transformKeyValue) : [], continuationToken: response.nextLink ? extractAfterTokenFromNextLink(response.nextLink) : undefined }); // let itemList = currentResponse.items; return { page: currentResponse, nextPageLink: currentResponse.continuationToken, }; }, toElements: (page) => page.items, }; return getPagedAsyncIterator(pagedResult); } async sendRevisionsRequest(options = {}, pageLink) { return tracingClient.withSpan("AppConfigurationClient.listRevisions", options, async (updatedOptions) => { const response = await this.client.getRevisions(Object.assign(Object.assign(Object.assign(Object.assign({}, updatedOptions), formatAcceptDateTime(options)), formatFiltersAndSelect(updatedOptions)), { after: pageLink })); return response; }); } /** * Sets the value of a key in the Azure App Configuration service, allowing for an optional etag. * @param key - The name of the key. * @param configurationSetting - A configuration value. * @param options - Optional parameters for the request. * * Example code: * ```ts snippet:SetConfigurationSetting * import { DefaultAzureCredential } from "@azure/identity"; * import { AppConfigurationClient } from "@azure/app-configuration"; * * // The endpoint for your App Configuration resource * const endpoint = "https://example.azconfig.io"; * const credential = new DefaultAzureCredential(); * const client = new AppConfigurationClient(endpoint, credential); * * await client.setConfigurationSetting({ key: "MyKey", value: "MyValue" }); * ``` */ async setConfigurationSetting(configurationSetting, options = {}) { return tracingClient.withSpan("AppConfigurationClient.setConfigurationSetting", options, async (updatedOptions) => { const keyValue = serializeAsConfigurationSettingParam(configurationSetting); logger.info("[setConfigurationSetting] Setting new key value"); const response = transformKeyValueResponse(await this.client.putKeyValue(configurationSetting.key, Object.assign(Object.assign(Object.assign({}, updatedOptions), { label: configurationSetting.label, entity: keyValue }), checkAndFormatIfAndIfNoneMatch(configurationSetting, options)))); assertResponse(response); return response; }); } /** * Sets or clears a key's read-only status. * @param id - The id of the configuration setting to modify. */ async setReadOnly(id, readOnly, options = {}) { return tracingClient.withSpan("AppConfigurationClient.setReadOnly", options, async (newOptions) => { let response; if (readOnly) { logger.info("[setReadOnly] Setting read-only status to ${readOnly}"); response = await this.client.putLock(id.key, Object.assign(Object.assign(Object.assign({}, newOptions), { label: id.label }), checkAndFormatIfAndIfNoneMatch(id, options))); } else { logger.info("[setReadOnly] Deleting read-only lock"); response = await this.client.deleteLock(id.key, Object.assign(Object.assign(Object.assign({}, newOptions), { label: id.label }), checkAndFormatIfAndIfNoneMatch(id, options))); } response = transformKeyValueResponse(response); assertResponse(response); return response; }); } /** * Adds an external synchronization token to ensure service requests receive up-to-date values. * * @param syncToken - The synchronization token value. */ updateSyncToken(syncToken) { this._syncTokens.addSyncTokenFromHeaderValue(syncToken); } /** * Begins creating a snapshot for Azure App Configuration service, fails if it * already exists. */ beginCreateSnapshot(snapshot, // eslint-disable-next-line @azure/azure-sdk/ts-naming-options options = {}) { return tracingClient.withSpan(`${AppConfigurationClient.name}.beginCreateSnapshot`, options, (updatedOptions) => this.client.beginCreateSnapshot(snapshot.name, snapshot, Object.assign({}, updatedOptions))); } /** * Begins creating a snapshot for Azure App Configuration service, waits until it is done, * fails if it already exists. */ beginCreateSnapshotAndWait(snapshot, // eslint-disable-next-line @azure/azure-sdk/ts-naming-options options = {}) { return tracingClient.withSpan(`${AppConfigurationClient.name}.beginCreateSnapshotAndWait`, options, (updatedOptions) => this.client.beginCreateSnapshotAndWait(snapshot.name, snapshot, Object.assign({}, updatedOptions))); } /** * Get a snapshot from Azure App Configuration service * * Example usage: * ```ts snippet:GetSnapshot * import { DefaultAzureCredential } from "@azure/identity"; * import { AppConfigurationClient } from "@azure/app-configuration"; * * // The endpoint for your App Configuration resource * const endpoint = "https://example.azconfig.io"; * const credential = new DefaultAzureCredential(); * const client = new AppConfigurationClient(endpoint, credential); * * const retrievedSnapshot = await client.getSnapshot("testsnapshot"); * console.log("Retrieved snapshot:", retrievedSnapshot); * ``` * @param name - The name of the snapshot. * @param options - Optional parameters for the request. */ getSnapshot(name, options = {}) { return tracingClient.withSpan("AppConfigurationClient.getSnapshot", options, async (updatedOptions) => { logger.info("[getSnapshot] Get a snapshot"); const originalResponse = await this.client.getSnapshot(name, Object.assign({}, updatedOptions)); const response = transformSnapshotResponse(originalResponse); assertResponse(response); return response; }); } /** * Recover an archived snapshot back to ready status * * Example usage: * ```ts snippet:RecoverSnapshot * import { DefaultAzureCredential } from "@azure/identity"; * import { AppConfigurationClient } from "@azure/app-configuration"; * * // The endpoint for your App Configuration resource * const endpoint = "https://example.azconfig.io"; * const credential = new DefaultAzureCredential(); * const client = new AppConfigurationClient(endpoint, credential); * * const result = await client.recoverSnapshot("MySnapshot"); * ``` * @param name - The name of the snapshot. * @param options - Optional parameters for the request. */ recoverSnapshot(name, // eslint-disable-next-line @azure/azure-sdk/ts-naming-options options = {}) { return tracingClient.withSpan("AppConfigurationClient.recoverSnapshot", options, async (updatedOptions) => { logger.info("[recoverSnapshot] Recover a snapshot"); const originalResponse = await this.client.updateSnapshot(name, { status: "ready" }, Object.assign(Object.assign({}, updatedOptions), checkAndFormatIfAndIfNoneMatch({ etag: options.etag }, Object.assign({ onlyIfUnchanged: true }, options)))); const response = transformSnapshotResponse(originalResponse); assertResponse(response); return response; }); } /** * Archive a ready snapshot * * Example usage: * ```ts snippet:ArchiveSnapshot * import { DefaultAzureCredential } from "@azure/identity"; * import { AppConfigurationClient } from "@azure/app-configuration"; * * // The endpoint for your App Configuration resource * const endpoint = "https://example.azconfig.io"; * const credential = new DefaultAzureCredential(); * const client = new AppConfigurationClient(endpoint, credential); * * const result = await client.archiveSnapshot({ name: "MySnapshot" }); * ``` * @param name - The name of the snapshot. * @param options - Optional parameters for the request. */ archiveSnapshot(name, // eslint-disable-next-line @azure/azure-sdk/ts-naming-options options = {}) { return tracingClient.withSpan("AppConfigurationClient.archiveSnapshot", options, async (updatedOptions) => { logger.info("[archiveSnapshot] Archive a snapshot"); const originalResponse = await this.client.updateSnapshot(name, { status: "archived" }, Object.assign(Object.assign({}, updatedOptions), checkAndFormatIfAndIfNoneMatch({ etag: options.etag }, Object.assign({ onlyIfUnchanged: true }, options)))); const response = transformSnapshotResponse(originalResponse); assertResponse(response); return response; }); } /** * List all snapshots from Azure App Configuration service * * Example usage: * ```ts snippet:ListSnapshots * import { DefaultAzureCredential } from "@azure/identity"; * import { AppConfigurationClient } from "@azure/app-configuration"; * * // The endpoint for your App Configuration resource * const endpoint = "https://example.azconfig.io"; * const credential = new DefaultAzureCredential(); * const client = new AppConfigurationClient(endpoint, credential); * * const snapshots = await client.listSnapshots(); * * for await (const snapshot of snapshots) { * console.log(`Found snapshot: ${snapshot.name}`); * } * ``` * @param options - Optional parameters for the request. */ listSnapshots(options = {}) { const pagedResult = { firstPageLink: undefined, getPage: async (pageLink) => { const response = await this.sendSnapShotsRequest(options, pageLink); const currentResponse = Object.assign(Object.assign({}, response), { items: response.items != null ? response.items : [], continuationToken: response.nextLink ? extractAfterTokenFromNextLink(response.nextLink) : undefined }); return { page: currentResponse, nextPageLink: currentResponse.continuationToken, }; }, toElements: (page) => page.items, }; return getPagedAsyncIterator(pagedResult); } async sendSnapShotsRequest(options = {}, pageLink) { return tracingClient.withSpan("AppConfigurationClient.listSnapshots", options, async (updatedOptions) => { const response = await this.client.getSnapshots(Object.assign(Object.assign(Object.assign({}, updatedOptions), formatSnapshotFiltersAndSelect(options)), { after: pageLink })); return response; }); } } //# sourceMappingURL=appConfigurationClient.js.map