diff --git a/sdk/keyvault/keyvault-admin/CHANGELOG.md b/sdk/keyvault/keyvault-admin/CHANGELOG.md index 2b4ea0acb304..983475dbd9ef 100644 --- a/sdk/keyvault/keyvault-admin/CHANGELOG.md +++ b/sdk/keyvault/keyvault-admin/CHANGELOG.md @@ -2,6 +2,7 @@ ## 4.0.0-beta.2 (Unreleased) +- Updated the Key Vault Admin Long Running Operation Pollers to follow a more compact and meaningful approach moving forward. ## 4.0.0-beta.1 (2020-09-11) diff --git a/sdk/keyvault/keyvault-admin/review/keyvault-admin.api.md b/sdk/keyvault/keyvault-admin/review/keyvault-admin.api.md index 9c5b309d7985..c8e4c9c4272e 100644 --- a/sdk/keyvault/keyvault-admin/review/keyvault-admin.api.md +++ b/sdk/keyvault/keyvault-admin/review/keyvault-admin.api.md @@ -21,13 +21,7 @@ export interface BackupClientOptions extends coreHttp.PipelineOptions { } // @public -export interface BackupOperationState extends PollOperationState { - endTime?: Date; - jobId?: string; - startTime?: Date; - status?: string; - statusDetails?: string; -} +export type BackupOperationState = KeyVaultAdminPollOperationState; // @public export interface BackupPollerOptions extends coreHttp.OperationOptions { @@ -70,6 +64,15 @@ export class KeyVaultAccessControlClient { readonly vaultUrl: string; } +// @public +export interface KeyVaultAdminPollOperationState extends PollOperationState { + endTime?: Date; + jobId?: string; + startTime?: Date; + status?: string; + statusDetails?: string; +} + // @public export class KeyVaultBackupClient { constructor(vaultUrl: string, credential: TokenCredential, pipelineOptions?: BackupClientOptions); @@ -142,12 +145,7 @@ export interface ListRoleDefinitionsPageSettings { } // @public -export interface RestoreOperationState extends PollOperationState { - endTime?: Date; - jobId?: string; - startTime?: Date; - status?: string; - statusDetails?: string; +export interface RestoreOperationState extends KeyVaultAdminPollOperationState { } // @public @@ -157,12 +155,7 @@ export type RoleAssignmentScope = "/" | "/keys" | string; export const SDK_VERSION: string; // @public -export interface SelectiveRestoreOperationState extends PollOperationState { - endTime?: Date; - jobId?: string; - startTime?: Date; - status?: string; - statusDetails?: string; +export interface SelectiveRestoreOperationState extends KeyVaultAdminPollOperationState { } // @public diff --git a/sdk/keyvault/keyvault-admin/src/accessControlClient.ts b/sdk/keyvault/keyvault-admin/src/accessControlClient.ts index 852e7ea1cda8..c59c2d87853c 100644 --- a/sdk/keyvault/keyvault-admin/src/accessControlClient.ts +++ b/sdk/keyvault/keyvault-admin/src/accessControlClient.ts @@ -33,7 +33,7 @@ import { } from "./accessControlModels"; import { SDK_VERSION, LATEST_API_VERSION } from "./constants"; -import { createSpan, setParentSpan } from "./tracing"; +import { createSpan, setParentSpan } from "../../keyvault-common/src"; import { mappings } from "./mappings"; import { logger } from "./log"; diff --git a/sdk/keyvault/keyvault-admin/src/backupClient.ts b/sdk/keyvault/keyvault-admin/src/backupClient.ts index 94ee65b2185c..43459e750686 100644 --- a/sdk/keyvault/keyvault-admin/src/backupClient.ts +++ b/sdk/keyvault/keyvault-admin/src/backupClient.ts @@ -19,9 +19,15 @@ import { RestorePoller } from "./lro/restore/poller"; import { SelectiveRestorePoller } from "./lro/selectiveRestore/poller"; import { BackupOperationState } from "./lro/backup/operation"; import { RestoreOperationState } from "./lro/restore/operation"; +import { KeyVaultAdminPollOperationState } from "./lro/keyVaultAdminPoller"; import { SelectiveRestoreOperationState } from "./lro/selectiveRestore/operation"; -export { BackupOperationState, RestoreOperationState, SelectiveRestoreOperationState }; +export { + BackupOperationState, + RestoreOperationState, + SelectiveRestoreOperationState, + KeyVaultAdminPollOperationState +}; /** * The KeyVaultBackupClient provides methods to generate backups diff --git a/sdk/keyvault/keyvault-admin/src/lro/backup/operation.ts b/sdk/keyvault/keyvault-admin/src/lro/backup/operation.ts index f39bc4c7b1d2..e576ce202955 100644 --- a/sdk/keyvault/keyvault-admin/src/lro/backup/operation.ts +++ b/sdk/keyvault/keyvault-admin/src/lro/backup/operation.ts @@ -2,7 +2,6 @@ // Licensed under the MIT license. import { AbortSignalLike } from "@azure/abort-controller"; -import { PollOperationState, PollOperation } from "@azure/core-lro"; import { RequestOptionsBase } from "@azure/core-http"; import { KeyVaultClient } from "../../generated/keyVaultClient"; import { @@ -10,52 +9,22 @@ import { KeyVaultClientFullBackupResponse, KeyVaultClientFullBackupStatusResponse } from "../../generated/models"; -import { createSpan, setParentSpan } from "../../tracing"; +import { createSpan, setParentSpan } from "../../../../keyvault-common/src"; import { BeginBackupOptions } from "../../backupClientModels"; +import { + KeyVaultAdminPollOperation, + KeyVaultAdminPollOperationState +} from "../keyVaultAdminPoller"; /** * An interface representing the publicly available properties of the state of a backup Key Vault's poll operation. */ -export interface BackupOperationState extends PollOperationState { - /** - * Identifier for the full backup operation. - */ - jobId?: string; - /** - * Status of the backup operation. - */ - status?: string; - /** - * The status details of backup operation. - */ - statusDetails?: string; - /** - * The start time of the backup operation in UTC - */ - startTime?: Date; - /** - * The end time of the backup operation in UTC - */ - endTime?: Date; -} +export type BackupOperationState = KeyVaultAdminPollOperationState; /** * An internal interface representing the state of a backup Key Vault's poll operation. - * @internal */ -export interface BackupPollOperationState extends PollOperationState { - /** - * Options for the core-http requests. - */ - requestOptions: RequestOptionsBase; - /** - * An interface representing the internal KeyVaultClient. - */ - client: KeyVaultClient; - /** - * The base URL to the vault. - */ - vaultUrl: string; +export interface BackupPollOperationState extends KeyVaultAdminPollOperationState { /** * The URI of the blob storage account. */ @@ -64,187 +33,141 @@ export interface BackupPollOperationState extends PollOperationState { * The SAS token. */ sasToken: string; - /** - * The id returned as part of the backup request - */ - jobId?: string; - /** - * Status of the backup operation. - */ - status?: string; - /** - * The status details of backup operation. - */ - statusDetails?: string; - /** - * The start time of the backup operation in UTC - */ - startTime?: Date; - /** - * The end time of the backup operation in UTC - */ - endTime?: Date; } /** - * An interface representing a backup Key Vault's poll operation. + * The backup Key Vault's poll operation. */ -export interface BackupPollOperation extends PollOperation {} - -/** - * Tracing the fullBackup operation - */ -async function fullBackup( - client: KeyVaultClient, - vaultUrl: string, - options: KeyVaultClientFullBackupOptionalParams -): Promise { - const span = createSpan("generatedClient.fullBackup", options); - try { - return await client.fullBackup(vaultUrl, setParentSpan(span, options)); - } finally { - span.end(); +export class BackupPollOperation extends KeyVaultAdminPollOperation< + BackupPollOperationState, + string +> { + constructor( + public state: BackupPollOperationState, + private vaultUrl: string, + private client: KeyVaultClient, + private requestOptions: RequestOptionsBase = {} + ) { + super(state, { cancelMessage: "Cancelling a full Key Vault backup is not supported." }); } -} -/** - * Tracing the fullBackupStatus operation - */ -async function fullBackupStatus( - client: KeyVaultClient, - vaultUrl: string, - jobId: string, - options: BeginBackupOptions -): Promise { - const span = createSpan("generatedClient.fullBackupStatus", options); - try { - return await client.fullBackupStatus(vaultUrl, jobId, setParentSpan(span, options)); - } finally { - span.end(); + /** + * Tracing the fullBackup operation + */ + private async fullBackup( + options: KeyVaultClientFullBackupOptionalParams + ): Promise { + const span = createSpan("generatedClient.fullBackup", options); + try { + return await this.client.fullBackup(this.vaultUrl, setParentSpan(span, options)); + } finally { + span.end(); + } } -} -/** - * @summary Reaches to the service and updates the backup's poll operation. - * @param [options] The optional parameters, which are an abortSignal from @azure/abort-controller and a function that triggers the poller's onProgress function. - */ -async function update( - this: BackupPollOperation, - options: { - abortSignal?: AbortSignalLike; - fireProgress?: (state: BackupPollOperationState) => void; - } = {} -): Promise { - const state = this.state; - const { requestOptions, vaultUrl, blobStorageUri, sasToken, client } = state; - - if (options.abortSignal) { - requestOptions.abortSignal = options.abortSignal; + /** + * Tracing the fullBackupStatus operation + */ + private async fullBackupStatus( + jobId: string, + options: BeginBackupOptions + ): Promise { + const span = createSpan("generatedClient.fullBackupStatus", options); + try { + return await this.client.fullBackupStatus(this.vaultUrl, jobId, setParentSpan(span, options)); + } finally { + span.end(); + } } - if (!state.isStarted) { - const serviceOperation = await fullBackup(client, vaultUrl!, { - ...requestOptions, - azureStorageBlobContainerUri: { - storageResourceUri: blobStorageUri!, - token: sasToken! - } - }); - - const { - startTime, - jobId, - azureStorageBlobContainerUri, - endTime, - error, - status, - statusDetails - } = serviceOperation; - - if (!startTime) { - state.error = new Error(`Missing "startTime" from the full backup operation.`); - state.isCompleted = true; - return makeBackupPollOperation(state); + /** + * Reaches to the service and updates the backup's poll operation. + */ + async update( + options: { + abortSignal?: AbortSignalLike; + fireProgress?: (state: BackupPollOperationState) => void; + } = {} + ): Promise { + const state = this.state; + const { blobStorageUri, sasToken } = state; + + if (options.abortSignal) { + this.requestOptions.abortSignal = options.abortSignal; } - state.isStarted = true; - state.jobId = jobId; - state.endTime = endTime; - state.startTime = startTime; - state.status = status; - state.statusDetails = statusDetails; - state.result = azureStorageBlobContainerUri; - - if (endTime) { - state.isCompleted = true; - } - if (error && error.message) { - state.isCompleted = true; - state.error = new Error(error.message); - } - } + if (!state.isStarted) { + const serviceOperation = await this.fullBackup({ + ...this.requestOptions, + azureStorageBlobContainerUri: { + storageResourceUri: blobStorageUri!, + token: sasToken! + } + }); + + const { + startTime, + jobId, + azureStorageBlobContainerUri, + endTime, + error, + status, + statusDetails + } = serviceOperation; + + if (!startTime) { + state.error = new Error(`Missing "startTime" from the full backup operation.`); + state.isCompleted = true; + return this; + } - if (!state.jobId) { - state.error = new Error(`Missing "jobId" from the full backup operation.`); - state.isCompleted = true; - return makeBackupPollOperation(state); - } + state.isStarted = true; + state.jobId = jobId; + state.endTime = endTime; + state.startTime = startTime; + state.status = status; + state.statusDetails = statusDetails; + state.result = azureStorageBlobContainerUri; - if (!state.isCompleted) { - const serviceOperation = await fullBackupStatus(client, vaultUrl!, state.jobId, requestOptions); - const { - azureStorageBlobContainerUri, - endTime, - status, - statusDetails, - error - } = serviceOperation; - - state.endTime = endTime; - state.status = status; - state.statusDetails = statusDetails; - state.result = azureStorageBlobContainerUri; - - if (endTime) { - state.isCompleted = true; + if (endTime) { + state.isCompleted = true; + } + if (error && error.message) { + state.isCompleted = true; + state.error = new Error(error.message); + } } - if (error && error.message) { + + if (!state.jobId) { + state.error = new Error(`Missing "jobId" from the full backup operation.`); state.isCompleted = true; - state.error = new Error(error.message); + return this; } - } - - return makeBackupPollOperation(state); -} - -/** - * @summary Reaches to the service and cancels the key's operation, also updating the key's poll operation - * @param [options] The optional parameters, which is only an abortSignal from @azure/abort-controller - */ -async function cancel(this: BackupPollOperation): Promise { - throw new Error("Canceling the deletion of a key is not supported."); -} -/** - * @summary Serializes the create key's poll operation - */ -function toString(this: BackupPollOperation): string { - return JSON.stringify({ - state: this.state - }); -} + if (!state.isCompleted) { + const serviceOperation = await this.fullBackupStatus(state.jobId, this.requestOptions); + const { + azureStorageBlobContainerUri, + endTime, + status, + statusDetails, + error + } = serviceOperation; + + state.endTime = endTime; + state.status = status; + state.statusDetails = statusDetails; + state.result = azureStorageBlobContainerUri; + + if (endTime) { + state.isCompleted = true; + } + if (error && error.message) { + state.isCompleted = true; + state.error = new Error(error.message); + } + } -/** - * @summary Builds a create key's poll operation - * @param [state] A poll operation's state, in case the new one is intended to follow up where the previous one was left. - */ -export function makeBackupPollOperation(state: BackupPollOperationState): BackupPollOperation { - return { - state: { - ...state - }, - update, - cancel, - toString - }; + return this; + } } diff --git a/sdk/keyvault/keyvault-admin/src/lro/backup/poller.ts b/sdk/keyvault/keyvault-admin/src/lro/backup/poller.ts index 1fce9b8ed28b..23223b862176 100644 --- a/sdk/keyvault/keyvault-admin/src/lro/backup/poller.ts +++ b/sdk/keyvault/keyvault-admin/src/lro/backup/poller.ts @@ -1,36 +1,18 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { delay } from "@azure/core-http"; -import { Poller } from "@azure/core-lro"; -import { BeginBackupOptions } from "../../backupClientModels"; -import { KeyVaultClient } from "../../generated/keyVaultClient"; -import { - BackupOperationState, - BackupPollOperationState, - makeBackupPollOperation -} from "./operation"; +import { BackupPollOperation, BackupOperationState, BackupPollOperationState } from "./operation"; +import { KeyVaultAdminPollerOptions, KeyVaultAdminPoller } from "../keyVaultAdminPoller"; -export interface BackupPollerOptions { - client: KeyVaultClient; - vaultUrl: string; +export interface BackupPollerOptions extends KeyVaultAdminPollerOptions { blobStorageUri: string; sasToken: string; - requestOptions?: BeginBackupOptions; - intervalInMs?: number; - resumeFrom?: string; } /** * Class that creates a poller that waits until the backup of a Key Vault ends up being generated. */ -export class BackupPoller extends Poller { - /** - * Defines how much time the poller is going to wait before making a new request to the service. - * @memberof BackupPoller - */ - public intervalInMs: number; - +export class BackupPoller extends KeyVaultAdminPoller { constructor(options: BackupPollerOptions) { const { client, @@ -48,44 +30,19 @@ export class BackupPoller extends Poller { state = JSON.parse(resumeFrom).state; } - const operation = makeBackupPollOperation({ - ...state, - blobStorageUri, - sasToken, - requestOptions: requestOptions || {}, + const operation = new BackupPollOperation( + { + ...state, + blobStorageUri, + sasToken + }, + vaultUrl, client, - vaultUrl - }); + requestOptions + ); super(operation); this.intervalInMs = intervalInMs; } - - /** - * The method used by the poller to wait before attempting to update its operation. - * @memberof BackupPoller - */ - async delay(): Promise { - return delay(this.intervalInMs); - } - - /** - * Gets the public state of the polling operation - */ - public getOperationState(): BackupOperationState { - const state: BackupOperationState = this.operation.state; - return { - isStarted: state.isStarted, - isCompleted: state.isCompleted, - isCancelled: state.isCancelled, - error: state.error, - result: state.result, - jobId: state.jobId, - endTime: state.endTime, - startTime: state.startTime, - status: state.status, - statusDetails: state.statusDetails - }; - } } diff --git a/sdk/keyvault/keyvault-admin/src/lro/keyVaultAdminPoller.ts b/sdk/keyvault/keyvault-admin/src/lro/keyVaultAdminPoller.ts new file mode 100644 index 000000000000..34f52b69670a --- /dev/null +++ b/sdk/keyvault/keyvault-admin/src/lro/keyVaultAdminPoller.ts @@ -0,0 +1,132 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { delay, RequestOptionsBase } from "@azure/core-http"; +import { Poller, PollOperation, PollOperationState } from "@azure/core-lro"; +import { KeyVaultClient } from "../generated/keyVaultClient"; + +/** + * Common parameters to a Key Vault Admin Poller. + */ +export interface KeyVaultAdminPollerOptions { + vaultUrl: string; + client: KeyVaultClient; + requestOptions?: RequestOptionsBase; + intervalInMs?: number; + resumeFrom?: string; +} + +/** + * An interface representing the state of a Key Vault Admin Poller's operation. + */ +export interface KeyVaultAdminPollOperationState extends PollOperationState { + /** + * Identifier for the full restore operation. + */ + jobId?: string; + /** + * Status of the restore operation. + */ + status?: string; + /** + * The status details of restore operation. + */ + statusDetails?: string; + /** + * The start time of the restore operation in UTC + */ + startTime?: Date; + /** + * The end time of the restore operation in UTC + */ + endTime?: Date; +} + +/** + * Generates a version of the state with only public properties. At least those common for all of the Key Vault Admin pollers. + */ +export function cleanState, TResult>( + state: TState +): KeyVaultAdminPollOperationState { + return { + jobId: state.jobId, + status: state.status, + statusDetails: state.statusDetails, + startTime: state.startTime, + endTime: state.endTime, + isStarted: state.isStarted, + isCancelled: state.isCancelled, + isCompleted: state.isCompleted, + error: state.error, + result: state.result + }; +} + +/** + * Common properties and methods of the Key Vault Admin Pollers. + */ +export abstract class KeyVaultAdminPoller< + TState extends KeyVaultAdminPollOperationState, + TResult +> extends Poller { + /** + * Defines how much time the poller is going to wait before making a new request to the service. + */ + public intervalInMs: number = 2000; + + /** + * The method used by the poller to wait before attempting to update its operation. + * @memberof DeleteKeyPoller + */ + async delay(): Promise { + return delay(this.intervalInMs); + } + + /** + * Gets the public state of the polling operation + */ + public getOperationState(): TState { + return cleanState(this.operation.state) as TState; + } +} + +/** + * Optional parameters to the KeyVaultAdminPollOperation + */ +export interface KeyVaultAdminPollOperationOptions { + cancelMessage: string; +} + +/** + * Common properties and methods of the Key Vault Admin Poller operations. + */ +export class KeyVaultAdminPollOperation implements PollOperation { + private cancelMessage: string; + + constructor(public state: TState, options: KeyVaultAdminPollOperationOptions) { + this.cancelMessage = options.cancelMessage; + } + + /** + * Meant to reach to the service and update the Poller operation. + */ + public async update(): Promise> { + throw new Error("Operation not supported."); + } + + /** + * Meant to reach to the service and cancel the Poller operation. + */ + public async cancel(): Promise> { + throw new Error(this.cancelMessage); + } + + /** + * Serializes the Poller operation. + */ + public toString(): string { + return JSON.stringify({ + state: cleanState(this.state) + }); + } +} diff --git a/sdk/keyvault/keyvault-admin/src/lro/restore/operation.ts b/sdk/keyvault/keyvault-admin/src/lro/restore/operation.ts index 7c02471fcdae..88fb1baaaf50 100644 --- a/sdk/keyvault/keyvault-admin/src/lro/restore/operation.ts +++ b/sdk/keyvault/keyvault-admin/src/lro/restore/operation.ts @@ -2,59 +2,29 @@ // Licensed under the MIT license. import { AbortSignalLike } from "@azure/abort-controller"; -import { PollOperationState, PollOperation } from "@azure/core-lro"; import { OperationOptions, RequestOptionsBase } from "@azure/core-http"; import { KeyVaultClient } from "../../generated/keyVaultClient"; import { KeyVaultClientFullRestoreOperationOptionalParams, KeyVaultClientRestoreStatusResponse } from "../../generated/models"; -import { createSpan, setParentSpan } from "../../tracing"; +import { createSpan, setParentSpan } from "../../../../keyvault-common/src"; import { KeyVaultClientFullRestoreOperationResponse } from "../../generated/models"; +import { + KeyVaultAdminPollOperation, + KeyVaultAdminPollOperationState +} from "../keyVaultAdminPoller"; /** * An interface representing the publicly available properties of the state of a restore Key Vault's poll operation. */ -export interface RestoreOperationState extends PollOperationState { - /** - * Identifier for the full restore operation. - */ - jobId?: string; - /** - * Status of the restore operation. - */ - status?: string; - /** - * The status details of restore operation. - */ - statusDetails?: string; - /** - * The start time of the restore operation in UTC - */ - startTime?: Date; - /** - * The end time of the restore operation in UTC - */ - endTime?: Date; -} +export interface RestoreOperationState extends KeyVaultAdminPollOperationState {} /** * An internal interface representing the state of a restore Key Vault's poll operation. * @internal */ -export interface RestorePollOperationState extends PollOperationState { - /** - * Options for the core-http requests. - */ - requestOptions: RequestOptionsBase; - /** - * An interface representing the internal KeyVaultClient. - */ - client: KeyVaultClient; - /** - * The base URL to the vault. - */ - vaultUrl: string; +export interface RestorePollOperationState extends KeyVaultAdminPollOperationState { /** * The URI of the blob storage account. */ @@ -67,176 +37,130 @@ export interface RestorePollOperationState extends PollOperationState * The Folder name of the blob where the previous successful full backup was stored */ folderName: string; - /** - * The id returned as part of the restore request - */ - jobId?: string; - /** - * Status of the restore operation. - */ - status?: string; - /** - * The status details of restore operation. - */ - statusDetails?: string; - /** - * The start time of the restore operation in UTC - */ - startTime?: Date; - /** - * The end time of the restore operation in UTC - */ - endTime?: Date; } /** * An interface representing a restore Key Vault's poll operation. */ -export interface RestorePollOperation extends PollOperation {} - -/** - * Tracing the fullRestore operation - */ -async function fullRestore( - client: KeyVaultClient, - vaultUrl: string, - options: KeyVaultClientFullRestoreOperationOptionalParams -): Promise { - const span = createSpan("generatedClient.fullRestore", options); - try { - return await client.fullRestoreOperation(vaultUrl, setParentSpan(span, options)); - } finally { - span.end(); +export class RestorePollOperation extends KeyVaultAdminPollOperation< + RestorePollOperationState, + string +> { + constructor( + public state: RestorePollOperationState, + private vaultUrl: string, + private client: KeyVaultClient, + private requestOptions: RequestOptionsBase = {} + ) { + super(state, { + cancelMessage: "Cancelling the restoration full Key Vault backup is not supported." + }); } -} -/** - * Tracing the restoreStatus operation. - */ -async function restoreStatus( - client: KeyVaultClient, - vaultUrl: string, - jobId: string, - options: OperationOptions -): Promise { - const span = createSpan("generatedClient.restoreStatus", options); - try { - return await client.restoreStatus(vaultUrl, jobId, setParentSpan(span, options)); - } finally { - span.end(); + /** + * Tracing the fullRestore operation + */ + private async fullRestore( + options: KeyVaultClientFullRestoreOperationOptionalParams + ): Promise { + const span = createSpan("generatedClient.fullRestore", options); + try { + return await this.client.fullRestoreOperation(this.vaultUrl, setParentSpan(span, options)); + } finally { + span.end(); + } } -} -/** - * @summary Reaches to the service and updates the restore poll operation. - * @param [options] The optional parameters, which are an abortSignal from @azure/abort-controller and a function that triggers the poller's onProgress function. - */ -async function update( - this: RestorePollOperation, - options: { - abortSignal?: AbortSignalLike; - fireProgress?: (state: RestorePollOperationState) => void; - } = {} -): Promise { - const state = this.state; - const { client, requestOptions, vaultUrl, blobStorageUri, sasToken, folderName } = state; - - if (options.abortSignal) { - requestOptions.abortSignal = options.abortSignal; + /** + * Tracing the restoreStatus operation. + */ + private async restoreStatus( + jobId: string, + options: OperationOptions + ): Promise { + const span = createSpan("generatedClient.restoreStatus", options); + try { + return await this.client.restoreStatus(this.vaultUrl, jobId, setParentSpan(span, options)); + } finally { + span.end(); + } } - if (!state.isStarted) { - const serviceOperation = await fullRestore(client, vaultUrl, { - ...requestOptions, - restoreBlobDetails: { - folderToRestore: folderName, - sasTokenParameters: { - storageResourceUri: blobStorageUri, - token: sasToken - } - } - }); - - const { startTime, jobId, endTime, error, status, statusDetails } = serviceOperation; + /** + * Reaches to the service and updates the restore poll operation. + */ + async update( + options: { + abortSignal?: AbortSignalLike; + fireProgress?: (state: RestorePollOperationState) => void; + } = {} + ): Promise { + const state = this.state; + const { blobStorageUri, sasToken, folderName } = state; - if (!startTime) { - state.error = new Error(`Missing "startTime" from the full restore operation.`); - state.isCompleted = true; - return makeRestorePollOperation(state); + if (options.abortSignal) { + this.requestOptions.abortSignal = options.abortSignal; } - state.isStarted = true; - state.jobId = jobId; - state.endTime = endTime; - state.startTime = startTime; - state.status = status; - state.statusDetails = statusDetails; - - if (endTime) { - state.isCompleted = true; - } - if (error && error.message) { - state.isCompleted = true; - state.error = new Error(error.message); - } - } + if (!state.isStarted) { + const serviceOperation = await this.fullRestore({ + ...this.requestOptions, + restoreBlobDetails: { + folderToRestore: folderName, + sasTokenParameters: { + storageResourceUri: blobStorageUri, + token: sasToken + } + } + }); - if (!state.jobId) { - state.error = new Error(`Missing "jobId" from the full restore operation.`); - state.isCompleted = true; - return makeRestorePollOperation(state); - } + const { startTime, jobId, endTime, error, status, statusDetails } = serviceOperation; - if (!state.isCompleted) { - const serviceOperation = await restoreStatus(client, vaultUrl, state.jobId, { - requestOptions - }); - const { endTime, status, statusDetails, error } = serviceOperation; + if (!startTime) { + state.error = new Error(`Missing "startTime" from the full restore operation.`); + state.isCompleted = true; + return this; + } - state.endTime = endTime; - state.status = status; - state.statusDetails = statusDetails; + state.isStarted = true; + state.jobId = jobId; + state.endTime = endTime; + state.startTime = startTime; + state.status = status; + state.statusDetails = statusDetails; - if (endTime) { - state.isCompleted = true; + if (endTime) { + state.isCompleted = true; + } + if (error && error.message) { + state.isCompleted = true; + state.error = new Error(error.message); + } } - if (error && error.message) { + + if (!state.jobId) { + state.error = new Error(`Missing "jobId" from the full restore operation.`); state.isCompleted = true; - state.error = new Error(error.message); + return this; } - } - return makeRestorePollOperation(state); -} + if (!state.isCompleted) { + const serviceOperation = await this.restoreStatus(state.jobId, this.requestOptions); + const { endTime, status, statusDetails, error } = serviceOperation; -/** - * @summary Reaches to the service and cancels the key's operation, also updating the key's poll operation - * @param [options] The optional parameters, which is only an abortSignal from @azure/abort-controller - */ -async function cancel(this: RestorePollOperation): Promise { - throw new Error("Canceling the deletion of a key is not supported."); -} + state.endTime = endTime; + state.status = status; + state.statusDetails = statusDetails; -/** - * @summary Serializes the create key's poll operation - */ -function toString(this: RestorePollOperation): string { - return JSON.stringify({ - state: this.state - }); -} + if (endTime) { + state.isCompleted = true; + } + if (error && error.message) { + state.isCompleted = true; + state.error = new Error(error.message); + } + } -/** - * @summary Builds a create key's poll operation - * @param [state] A poll operation's state, in case the new one is intended to follow up where the previous one was left. - */ -export function makeRestorePollOperation(state: RestorePollOperationState): RestorePollOperation { - return { - state: { - ...state - }, - update, - cancel, - toString - }; + return this; + } } diff --git a/sdk/keyvault/keyvault-admin/src/lro/restore/poller.ts b/sdk/keyvault/keyvault-admin/src/lro/restore/poller.ts index f7add6f11f04..19f2ce2d921e 100644 --- a/sdk/keyvault/keyvault-admin/src/lro/restore/poller.ts +++ b/sdk/keyvault/keyvault-admin/src/lro/restore/poller.ts @@ -1,35 +1,23 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { delay, RequestOptionsBase } from "@azure/core-http"; -import { Poller } from "@azure/core-lro"; -import { KeyVaultClient } from "../../generated/keyVaultClient"; import { + RestorePollOperation, RestoreOperationState, - RestorePollOperationState, - makeRestorePollOperation + RestorePollOperationState } from "./operation"; +import { KeyVaultAdminPollerOptions, KeyVaultAdminPoller } from "../keyVaultAdminPoller"; -export interface RestorePollerOptions { - client: KeyVaultClient; - vaultUrl: string; +export interface RestorePollerOptions extends KeyVaultAdminPollerOptions { blobStorageUri: string; sasToken: string; folderName: string; - requestOptions?: RequestOptionsBase; - intervalInMs?: number; - resumeFrom?: string; } /** * Class that creates a poller that waits until a Key Vault ends up being restored. */ -export class RestorePoller extends Poller { - /** - * Defines how much time the poller is going to wait before making a new request to the service. - * @memberof RestorePoller - */ - public intervalInMs: number; +export class RestorePoller extends KeyVaultAdminPoller { constructor(options: RestorePollerOptions) { const { client, @@ -48,45 +36,20 @@ export class RestorePoller extends Poller { state = JSON.parse(resumeFrom).state; } - const operation = makeRestorePollOperation({ - ...state, - blobStorageUri, - sasToken, - folderName, - requestOptions: requestOptions || {}, + const operation = new RestorePollOperation( + { + ...state, + blobStorageUri, + sasToken, + folderName + }, + vaultUrl, client, - vaultUrl - }); + requestOptions + ); super(operation); this.intervalInMs = intervalInMs; } - - /** - * The method used by the poller to wait before attempting to update its operation. - * @memberof RestorePoller - */ - async delay(): Promise { - return delay(this.intervalInMs); - } - - /** - * Gets the public state of the polling operation - */ - public getOperationState(): RestoreOperationState { - const state: RestoreOperationState = this.operation.state; - return { - isStarted: state.isStarted, - isCompleted: state.isCompleted, - isCancelled: state.isCancelled, - error: state.error, - result: state.result, - jobId: state.jobId, - endTime: state.endTime, - startTime: state.startTime, - status: state.status, - statusDetails: state.statusDetails - }; - } } diff --git a/sdk/keyvault/keyvault-admin/src/lro/selectiveRestore/operation.ts b/sdk/keyvault/keyvault-admin/src/lro/selectiveRestore/operation.ts index fdd7cc87c9ad..832b8edae317 100644 --- a/sdk/keyvault/keyvault-admin/src/lro/selectiveRestore/operation.ts +++ b/sdk/keyvault/keyvault-admin/src/lro/selectiveRestore/operation.ts @@ -2,7 +2,6 @@ // Licensed under the MIT license. import { AbortSignalLike } from "@azure/abort-controller"; -import { PollOperationState, PollOperation } from "@azure/core-lro"; import { OperationOptions, operationOptionsToRequestOptionsBase, @@ -14,252 +13,168 @@ import { KeyVaultClientSelectiveKeyRestoreOperationOptionalParams, KeyVaultClientSelectiveKeyRestoreOperationResponse } from "../../generated/models"; -import { createSpan, setParentSpan } from "../../tracing"; +import { createSpan, setParentSpan } from "../../../../keyvault-common/src"; +import { + KeyVaultAdminPollOperation, + KeyVaultAdminPollOperationState +} from "../keyVaultAdminPoller"; /** * An interface representing the publicly available properties of the state of a restore Key Vault's poll operation. */ -export interface SelectiveRestoreOperationState extends PollOperationState { - /** - * Identifier for the full restore operation. - */ - jobId?: string; - /** - * Status of the restore operation. - */ - status?: string; - /** - * The status details of restore operation. - */ - statusDetails?: string; - /** - * The start time of the restore operation in UTC - */ - startTime?: Date; - /** - * The end time of the restore operation in UTC - */ - endTime?: Date; -} +export interface SelectiveRestoreOperationState + extends KeyVaultAdminPollOperationState {} /** * An internal interface representing the state of a restore Key Vault's poll operation. - * @internal */ -export interface SelectiveRestorePollOperationState extends PollOperationState { - /** - * Options for the core-http requests. - */ - requestOptions: RequestOptionsBase; - /** - * An interface representing the internal KeyVaultClient. - */ - client: KeyVaultClient; - /** - * The base URL to the vault - */ - vaultUrl: string; +export interface SelectiveRestorePollOperationState + extends KeyVaultAdminPollOperationState { /** * The name of a Key Vault Key. */ keyName: string; - /** - * The URI of the blob storage account. - */ - blobStorageUri: string; - /** - * The SAS token. - */ - sasToken: string; /** * The Folder name of the blob where the previous successful full backup was stored */ folderName: string; /** - * The id returned as part of the restore request - */ - jobId?: string; - /** - * Status of the restore operation. - */ - status?: string; - /** - * The status details of restore operation. - */ - statusDetails?: string; - /** - * The start time of the restore operation in UTC + * The URI of the blob storage account. */ - startTime?: Date; + blobStorageUri: string; /** - * The end time of the restore operation in UTC + * The SAS token. */ - endTime?: Date; + sasToken: string; } /** - * An interface representing a restore Key Vault's poll operation. - */ -export interface SelectiveRestorePollOperation - extends PollOperation {} - -/** - * Tracing the selectiveRestore operation + * The selective restore Key Vault's poll operation. */ -async function selectiveRestore( - client: KeyVaultClient, - vaultUrl: string, - keyName: string, - options: KeyVaultClientSelectiveKeyRestoreOperationOptionalParams -): Promise { - const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = createSpan("generatedClient.selectiveRestore", requestOptions); - try { - return await client.selectiveKeyRestoreOperation( - vaultUrl, - keyName, - setParentSpan(span, requestOptions) - ); - } finally { - span.end(); +export class SelectiveRestorePollOperation extends KeyVaultAdminPollOperation< + SelectiveRestorePollOperationState, + string +> { + constructor( + public state: SelectiveRestorePollOperationState, + private vaultUrl: string, + private client: KeyVaultClient, + private requestOptions: RequestOptionsBase = {} + ) { + super(state, { cancelMessage: "Cancelling a selective Key Vault restore is not supported." }); } -} -/** - * Tracing the restoreStatus operation. - */ -async function restoreStatus( - client: KeyVaultClient, - vaultUrl: string, - jobId: string, - options: OperationOptions -): Promise { - const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = createSpan("generatedClient.restoreStatus", requestOptions); - try { - return await client.restoreStatus(vaultUrl, jobId, options); - } finally { - span.end(); + /** + * Tracing the selectiveRestore operation + */ + private async selectiveRestore( + keyName: string, + options: KeyVaultClientSelectiveKeyRestoreOperationOptionalParams + ): Promise { + const requestOptions = operationOptionsToRequestOptionsBase(options); + const span = createSpan("generatedClient.selectiveRestore", requestOptions); + try { + return await this.client.selectiveKeyRestoreOperation( + this.vaultUrl, + keyName, + setParentSpan(span, requestOptions) + ); + } finally { + span.end(); + } } -} - -/** - * @summary Reaches to the service and updates the delete key's poll operation. - * @param [options] The optional parameters, which are an abortSignal from @azure/abort-controller and a function that triggers the poller's onProgress function. - */ -async function update( - this: SelectiveRestorePollOperation, - options: { - abortSignal?: AbortSignalLike; - fireProgress?: (state: SelectiveRestorePollOperationState) => void; - } = {} -): Promise { - const state = this.state; - const { vaultUrl, keyName, blobStorageUri, sasToken, folderName } = state; - - // Internal properties, - // the reference is only potentially undefined in the public representation of the poller. - const client = state.client!; - const requestOptions = state.requestOptions || {}; - if (options.abortSignal) { - requestOptions.abortSignal = options.abortSignal; + /** + * Tracing the restoreStatus operation. + */ + private async restoreStatus( + jobId: string, + options: OperationOptions + ): Promise { + const requestOptions = operationOptionsToRequestOptionsBase(options); + const span = createSpan("generatedClient.restoreStatus", requestOptions); + try { + return await this.client.restoreStatus(this.vaultUrl, jobId, options); + } finally { + span.end(); + } } - if (!state.isStarted) { - const selectiveRestoreOperation = await selectiveRestore(client, vaultUrl, keyName, { - ...requestOptions, - restoreBlobDetails: { - folder: folderName, - sasTokenParameters: { - storageResourceUri: blobStorageUri, - token: sasToken - } - } - }); - - const { startTime, jobId, endTime, error, status, statusDetails } = selectiveRestoreOperation; + /** + * Reaches to the service and updates the selective restore poll operation. + */ + async update( + options: { + abortSignal?: AbortSignalLike; + fireProgress?: (state: SelectiveRestorePollOperationState) => void; + } = {} + ): Promise { + const state = this.state; + const { keyName, blobStorageUri, sasToken, folderName } = state; - if (!startTime) { - state.error = new Error(`Missing "startTime" from the full restore operation.`); - state.isCompleted = true; - return makeSelectiveRestorePollOperation(state); + if (options.abortSignal) { + this.requestOptions.abortSignal = options.abortSignal; } - state.isStarted = true; - state.jobId = jobId; - state.endTime = endTime; - state.startTime = startTime; - state.status = status; - state.statusDetails = statusDetails; - - if (endTime) { - state.isCompleted = true; - } - if (error && error.message) { - state.isCompleted = true; - state.error = new Error(error.message); - } - } + if (!state.isStarted) { + const selectiveRestoreOperation = await this.selectiveRestore(keyName, { + ...this.requestOptions, + restoreBlobDetails: { + folder: folderName, + sasTokenParameters: { + storageResourceUri: blobStorageUri, + token: sasToken + } + } + }); - if (!state.jobId) { - state.error = new Error(`Missing "jobId" from the full restore operation.`); - state.isCompleted = true; - return makeSelectiveRestorePollOperation(state); - } + const { startTime, jobId, endTime, error, status, statusDetails } = selectiveRestoreOperation; - if (!state.isCompleted) { - const selectiveRestoreOperation = await restoreStatus(client, vaultUrl, state.jobId, { - requestOptions - }); - const { endTime, status, statusDetails, error } = selectiveRestoreOperation; + if (!startTime) { + state.error = new Error(`Missing "startTime" from the full restore operation.`); + state.isCompleted = true; + return this; + } - state.endTime = endTime; - state.status = status; - state.statusDetails = statusDetails; + state.isStarted = true; + state.jobId = jobId; + state.endTime = endTime; + state.startTime = startTime; + state.status = status; + state.statusDetails = statusDetails; - if (endTime) { - state.isCompleted = true; + if (endTime) { + state.isCompleted = true; + } + if (error && error.message) { + state.isCompleted = true; + state.error = new Error(error.message); + } } - if (error && error.message) { + + if (!state.jobId) { + state.error = new Error(`Missing "jobId" from the full restore operation.`); state.isCompleted = true; - state.error = new Error(error.message); + return this; } - } - return makeSelectiveRestorePollOperation(state); -} + if (!state.isCompleted) { + const selectiveRestoreOperation = await this.restoreStatus(state.jobId, this.requestOptions); + const { endTime, status, statusDetails, error } = selectiveRestoreOperation; -/** - * @summary Reaches to the service and cancels the key's operation, also updating the key's poll operation - * @param [options] The optional parameters, which is only an abortSignal from @azure/abort-controller - */ -async function cancel(this: SelectiveRestorePollOperation): Promise { - throw new Error("Canceling the deletion of a key is not supported."); -} + state.endTime = endTime; + state.status = status; + state.statusDetails = statusDetails; -/** - * @summary Serializes the SelectiveRestorePollOperation - */ -function toString(this: SelectiveRestorePollOperation): string { - return JSON.stringify({ - state: this.state - }); -} + if (endTime) { + state.isCompleted = true; + } + if (error && error.message) { + state.isCompleted = true; + state.error = new Error(error.message); + } + } -/** - * @summary Builds the SelectiveRestorePollOperation - * @param [state] A poll operation's state, in case the new one is intended to follow up where the previous one was left. - */ -export function makeSelectiveRestorePollOperation( - state: SelectiveRestorePollOperationState -): SelectiveRestorePollOperation { - return { - state: { - ...state - }, - update, - cancel, - toString - }; + return this; + } } diff --git a/sdk/keyvault/keyvault-admin/src/lro/selectiveRestore/poller.ts b/sdk/keyvault/keyvault-admin/src/lro/selectiveRestore/poller.ts index b34974330b07..22725a7d3586 100644 --- a/sdk/keyvault/keyvault-admin/src/lro/selectiveRestore/poller.ts +++ b/sdk/keyvault/keyvault-admin/src/lro/selectiveRestore/poller.ts @@ -1,37 +1,27 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { delay, RequestOptionsBase } from "@azure/core-http"; -import { Poller } from "@azure/core-lro"; -import { KeyVaultClient } from "../../generated/keyVaultClient"; import { + SelectiveRestorePollOperation, SelectiveRestoreOperationState, - SelectiveRestorePollOperationState, - makeSelectiveRestorePollOperation + SelectiveRestorePollOperationState } from "./operation"; +import { KeyVaultAdminPollerOptions, KeyVaultAdminPoller } from "../keyVaultAdminPoller"; -export interface SelectiveRestorePollerOptions { - client: KeyVaultClient; +export interface SelectiveRestorePollerOptions extends KeyVaultAdminPollerOptions { keyName: string; - vaultUrl: string; blobStorageUri: string; sasToken: string; folderName: string; - requestOptions?: RequestOptionsBase; - intervalInMs?: number; - resumeFrom?: string; } /** * Class that creates a poller that waits until a key of a Key Vault backup ends up being restored. */ -export class SelectiveRestorePoller extends Poller { - /** - * Defines how much time the poller is going to wait before making a new request to the service. - * @memberof SelectiveRestorePoller - */ - public intervalInMs: number; - +export class SelectiveRestorePoller extends KeyVaultAdminPoller< + SelectiveRestoreOperationState, + undefined +> { constructor(options: SelectiveRestorePollerOptions) { const { client, @@ -51,46 +41,21 @@ export class SelectiveRestorePoller extends Poller { - return delay(this.intervalInMs); - } - - /** - * Gets the public state of the polling operation - */ - public getOperationState(): SelectiveRestoreOperationState { - const state: SelectiveRestoreOperationState = this.operation.state; - return { - isStarted: state.isStarted, - isCompleted: state.isCompleted, - isCancelled: state.isCancelled, - error: state.error, - result: state.result, - jobId: state.jobId, - endTime: state.endTime, - startTime: state.startTime, - status: state.status, - statusDetails: state.statusDetails - }; - } } diff --git a/sdk/keyvault/keyvault-admin/src/tracing.ts b/sdk/keyvault/keyvault-admin/src/tracing.ts deleted file mode 100644 index efad53af1033..000000000000 --- a/sdk/keyvault/keyvault-admin/src/tracing.ts +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import { getTracer } from "@azure/core-tracing"; -import { RequestOptionsBase } from "@azure/core-http"; -import { Span } from "@opentelemetry/api"; - -/** - * @internal - * @ignore - * Creates a span using the tracer that was set by the user. - * @param {string} methodName The name of the method creating the span. - * @param {RequestOptionsBase} [options] The options for the underlying HTTP request. - */ -export function createSpan(methodName: string, requestOptions?: RequestOptionsBase): Span { - const tracer = getTracer(); - const span = tracer.startSpan(methodName, requestOptions && requestOptions.spanOptions); - span.setAttribute("az.namespace", "Microsoft.KeyVault"); - return span; -} - -/** - * @internal - * @ignore - * Returns updated HTTP options with the given span as the parent of future spans, - * if applicable. - * @param {Span} span The span for the current operation. - * @param {RequestOptionsBase} [options] The options for the underlying HTTP request. - */ -export function setParentSpan(span: Span, options: RequestOptionsBase = {}): RequestOptionsBase { - if (span.isRecording()) { - const spanOptions = options.spanOptions || {}; - return { - ...options, - spanOptions: { - ...spanOptions, - parent: span.context(), - attributes: { - ...spanOptions.attributes, - "az.namespace": "Microsoft.KeyVault" - } - } - }; - } else { - return options; - } -} diff --git a/sdk/keyvault/keyvault-certificates/CHANGELOG.md b/sdk/keyvault/keyvault-certificates/CHANGELOG.md index a4a443881cd1..3767a51cd1bf 100644 --- a/sdk/keyvault/keyvault-certificates/CHANGELOG.md +++ b/sdk/keyvault/keyvault-certificates/CHANGELOG.md @@ -2,6 +2,7 @@ ## 4.2.0-beta.2 (Unreleased) +- Updated the Key Vault Certificates Long Running Operation Pollers to follow a more compact and meaningful approach moving forward. ## 4.2.0-beta.1 (2020-09-11) diff --git a/sdk/keyvault/keyvault-certificates/review/keyvault-certificates.api.md b/sdk/keyvault/keyvault-certificates/review/keyvault-certificates.api.md index ddc81b75266d..f89edbb29407 100644 --- a/sdk/keyvault/keyvault-certificates/review/keyvault-certificates.api.md +++ b/sdk/keyvault/keyvault-certificates/review/keyvault-certificates.api.md @@ -45,6 +45,9 @@ export type BeginDeleteCertificateOptions = CertificatePollerOptions; // @public export type BeginRecoverDeletedCertificateOptions = CertificatePollerOptions; +// @public +export type CancelCertificateOperationOptions = coreHttp.OperationOptions; + // @public export class CertificateClient { constructor(vaultUrl: string, credential: TokenCredential, pipelineOptions?: CertificateClientOptions); @@ -138,8 +141,7 @@ export interface CertificateOperationError { } // @public -export interface CertificateOperationState extends PollOperationState { - certificateName: string; +export interface CertificateOperationState extends KeyVaultCertificatePollOperationState { certificateOperation?: CertificateOperation; } @@ -209,7 +211,7 @@ export interface CreateCertificateOptions extends CertificateProperties, coreHtt } // @public -export type CreateCertificateState = PollOperationState; +export type CreateCertificateState = KeyVaultCertificatePollOperationState; // @public export interface CreateIssuerOptions extends coreHttp.OperationOptions { @@ -230,7 +232,7 @@ export const DefaultCertificatePolicy: { export type DeleteCertificateOperationOptions = coreHttp.OperationOptions; // @public -export type DeleteCertificateState = PollOperationState; +export type DeleteCertificateState = KeyVaultCertificatePollOperationState; // @public export type DeleteContactsOptions = coreHttp.OperationOptions; @@ -335,6 +337,11 @@ export interface KeyVaultCertificateId { version?: string; } +// @public +export interface KeyVaultCertificatePollOperationState extends PollOperationState { + certificateName: string; +} + // @public export interface KeyVaultCertificateWithPolicy extends KeyVaultCertificate { readonly policy?: CertificatePolicy; @@ -389,7 +396,7 @@ export { PollerLike } export type PurgeDeletedCertificateOptions = coreHttp.OperationOptions; // @public -export type RecoverDeletedCertificateState = PollOperationState; +export type RecoverDeletedCertificateState = KeyVaultCertificatePollOperationState; // @public export type RequireAtLeastOne = { diff --git a/sdk/keyvault/keyvault-certificates/src/certificatesModels.ts b/sdk/keyvault/keyvault-certificates/src/certificatesModels.ts index f5c0cf78c291..71c9ae936c16 100644 --- a/sdk/keyvault/keyvault-certificates/src/certificatesModels.ts +++ b/sdk/keyvault/keyvault-certificates/src/certificatesModels.ts @@ -20,63 +20,6 @@ export type CertificateKeyType = "EC" | "EC-HSM" | "RSA" | "RSA-HSM" | "oct"; */ export type CertificateKeyCurveName = "P-256" | "P-384" | "P-521" | "P-256K"; -/** - * @internal - * @ignore - * An interface representing the CertificateClient. For internal use. - */ -export interface CertificateClientInterface { - /** - * Creates a new certificate. If this is the first version, the certificate resource is created. - * Requires the certificates/create permission. - */ - createCertificate( - certificateName: string, - certificatePolicy: CertificatePolicy, - options: CreateCertificateOptions - ): Promise; - /** - * Gets the certificate operation. - */ - getPlainCertificateOperation( - certificateName: string, - options?: GetPlainCertificateOperationOptions - ): Promise; - /** - * Recovers the deleted certificate in the specified vault. This operation can only be performed on a soft-delete enabled vault. - * Requires the certificate/recover permission. - */ - recoverDeletedCertificate( - certificateName: string, - options?: RecoverDeletedCertificateOptions - ): Promise; - /** - * Updates a certificate creation operation that is already in progress. This operation requires the certificates/update permission. - */ - cancelCertificateOperation( - certificateName: string, - options?: CancelCertificateOperationOptions - ): Promise; - /** - * The get method gets a specified certificate and is applicable to any certificate stored in Azure Certificate Vault. - * This operation requires the certificates/get permission. - */ - getCertificate(name: string, options?: GetCertificateOptions): Promise; - /** - * The delete operation applies to any certificate stored in Azure Certificate Vault. Individual versions - * of a certificate can not be deleted, only all versions of a given certificate at once. - */ - deleteCertificate(name: string, options?: DeleteCertificateOptions): Promise; - /** - * The getDeletedCertificate method returns the specified deleted certificate along with its properties. - * This operation requires the certificates/get permission. - */ - getDeletedCertificate( - name: string, - options?: GetDeletedCertificateOptions - ): Promise; -} - /** * The latest supported KeyVault service API version */ @@ -564,7 +507,6 @@ export interface CreateCertificateOptions /** * Options for {@link cancelCertificateOperation}. - * @internal */ export type CancelCertificateOperationOptions = coreHttp.OperationOptions; diff --git a/sdk/keyvault/keyvault-certificates/src/index.ts b/sdk/keyvault/keyvault-certificates/src/index.ts index 420697a83c01..dbe796720a3a 100644 --- a/sdk/keyvault/keyvault-certificates/src/index.ts +++ b/sdk/keyvault/keyvault-certificates/src/index.ts @@ -19,8 +19,6 @@ import { createPipelineFromOptions } from "@azure/core-http"; -import { getTracer } from "@azure/core-tracing"; -import { Span } from "@opentelemetry/api"; import { logger } from "./log"; import { PollerLike, PollOperationState } from "@azure/core-lro"; @@ -32,7 +30,6 @@ import { BeginCreateCertificateOptions, BeginDeleteCertificateOptions, BeginRecoverDeletedCertificateOptions, - CancelCertificateOperationOptions, CertificateIssuer, CertificateContact, CertificateContentType, @@ -40,7 +37,6 @@ import { CertificateProperties, CreateCertificateOptions, DeleteCertificateOperationOptions, - DeleteCertificateOptions, DeleteContactsOptions, DeleteIssuerOptions, DeletedCertificate, @@ -62,7 +58,6 @@ import { ListDeletedCertificatesOptions, MergeCertificateOptions, PurgeDeletedCertificateOptions, - RecoverDeletedCertificateOptions, RestoreCertificateBackupOptions, SetContactsOptions, CreateIssuerOptions, @@ -73,7 +68,6 @@ import { UpdateCertificatePropertiesOptions, UpdateCertificatePolicyOptions, WellKnownIssuerNames, - CertificateClientInterface, CertificatePollerOptions, IssuerProperties, CertificateContactAll, @@ -86,29 +80,23 @@ import { PolicySubjectProperties, DefaultCertificatePolicy, CertificateClientOptions, - LATEST_API_VERSION + LATEST_API_VERSION, + CancelCertificateOperationOptions } from "./certificatesModels"; import { - CertificateBundle, KeyVaultClientGetCertificatesOptionalParams, KeyVaultClientGetCertificateIssuersOptionalParams, KeyVaultClientGetCertificateVersionsOptionalParams, KeyVaultClientSetCertificateIssuerOptionalParams, - CertificateOperation as CoreCertificateOperation, - CertificateAttributes as CoreCertificateAttributes, - CertificatePolicy as CoreCertificatePolicy, BackupCertificateResult, KeyVaultClientGetDeletedCertificatesOptionalParams, - DeletedCertificateItem, - DeletedCertificateBundle, ErrorModel, IssuerParameters, IssuerCredentials, IssuerAttributes, KeyUsageType, X509CertificateProperties, - DeleteCertificateResponse, DeleteCertificateContactsResponse, SetCertificateContactsResponse, GetCertificateContactsResponse, @@ -116,32 +104,29 @@ import { UpdateCertificateIssuerResponse, GetCertificateIssuerResponse, DeleteCertificateIssuerResponse, - CreateCertificateResponse, GetCertificateResponse, ImportCertificateResponse, GetCertificatePolicyResponse, UpdateCertificatePolicyResponse, UpdateCertificateResponse, - UpdateCertificateOperationResponse, - GetCertificateOperationResponse, DeleteCertificateOperationResponse, MergeCertificateResponse, BackupCertificateResponse, RestoreCertificateResponse, GetDeletedCertificateResponse, - RecoverDeletedCertificateResponse, SubjectAlternativeNames as CoreSubjectAlternativeNames, ActionType, - DeletionRecoveryLevel, - CertificateAttributes, - Contacts as CoreContacts, - IssuerBundle + DeletionRecoveryLevel } from "./generated/models"; import { KeyVaultClient } from "./generated/keyVaultClient"; import { SDK_VERSION } from "./constants"; import "@azure/core-paging"; import { PageSettings, PagedAsyncIterableIterator } from "@azure/core-paging"; -import { challengeBasedAuthenticationPolicy } from "../../keyvault-common/src"; +import { + challengeBasedAuthenticationPolicy, + createSpan, + setParentSpan +} from "../../keyvault-common/src"; import { CreateCertificatePoller } from "./lro/create/poller"; import { CertificateOperationPoller } from "./lro/operation/poller"; import { DeleteCertificatePoller } from "./lro/delete/poller"; @@ -152,6 +137,20 @@ import { CreateCertificateState } from "./lro/create/operation"; import { RecoverDeletedCertificateState } from "./lro/recover/operation"; import { parseCertificateBytes } from "./utils"; import { parseKeyVaultCertificateId, KeyVaultCertificateId } from "./identifier"; +import { + coreContactsToCertificateContacts, + getCertificateFromCertificateBundle, + getCertificateOperationFromCoreOperation, + getCertificateWithPolicyFromCertificateBundle, + getDeletedCertificateFromDeletedCertificateBundle, + getDeletedCertificateFromItem, + getPropertiesFromCertificateBundle, + toCoreAttributes, + toCorePolicy, + toPublicIssuer, + toPublicPolicy +} from "./transformations"; +import { KeyVaultCertificatePollOperationState } from "./lro/keyVaultCertificatePoller"; export { CertificateClientOptions, @@ -229,7 +228,9 @@ export { UpdateCertificatePolicyOptions, WellKnownIssuerNames as WellKnownIssuer, X509CertificateProperties, - logger + logger, + CancelCertificateOperationOptions, + KeyVaultCertificatePollOperationState }; /** @@ -240,165 +241,6 @@ export type KVPollerLike, TResult> = TResult >; -function toCoreAttributes(properties: CertificateProperties): CoreCertificateAttributes { - return { - recoveryLevel: properties.recoveryLevel, - enabled: properties.enabled, - notBefore: properties.notBefore, - expires: properties.expiresOn, - created: properties.createdOn, - updated: properties.updatedOn - }; -} - -function toCorePolicy( - id: string | undefined, - policy: CertificatePolicy, - attributes: CertificateAttributes = {} -): CoreCertificatePolicy { - let subjectAlternativeNames: CoreSubjectAlternativeNames = {}; - if (policy.subjectAlternativeNames) { - subjectAlternativeNames = { - emails: policy.subjectAlternativeNames.emails, - dnsNames: policy.subjectAlternativeNames.dnsNames, - upns: policy.subjectAlternativeNames.userPrincipalNames - }; - } - - return { - id, - lifetimeActions: policy.lifetimeActions - ? policy.lifetimeActions.map((action) => ({ - action: { actionType: action.action }, - trigger: { - lifetimePercentage: action.lifetimePercentage, - daysBeforeExpiry: action.daysBeforeExpiry - } - })) - : undefined, - keyProperties: { - keyType: policy.keyType, - keySize: policy.keySize, - reuseKey: policy.reuseKey, - curve: policy.keyCurveName, - exportable: policy.exportable - }, - secretProperties: { - contentType: policy.contentType - }, - x509CertificateProperties: { - subject: policy.subject, - ekus: policy.enhancedKeyUsage, - subjectAlternativeNames, - keyUsage: policy.keyUsage, - validityInMonths: policy.validityInMonths - }, - issuerParameters: { - name: policy.issuerName, - certificateType: policy.certificateType, - certificateTransparency: policy.certificateTransparency - }, - attributes - }; -} - -function toPublicPolicy(policy: CoreCertificatePolicy = {}): CertificatePolicy { - let subjectAlternativeNames: SubjectAlternativeNames | undefined; - const x509Properties: X509CertificateProperties = policy.x509CertificateProperties || {}; - - if (policy.x509CertificateProperties) { - if (x509Properties.subjectAlternativeNames) { - const names = x509Properties.subjectAlternativeNames; - if (names.emails && names.emails.length) { - subjectAlternativeNames = { - ...subjectAlternativeNames, - emails: names.emails as ArrayOneOrMore - }; - } - if (names.dnsNames && names.dnsNames.length) { - subjectAlternativeNames = { - ...subjectAlternativeNames, - dnsNames: names.dnsNames as ArrayOneOrMore - }; - } - if (names.upns && names.upns.length) { - subjectAlternativeNames = { - ...subjectAlternativeNames, - userPrincipalNames: names.upns as ArrayOneOrMore - }; - } - } - } - - const certificatePolicy: CertificatePolicy = { - lifetimeActions: policy.lifetimeActions - ? policy.lifetimeActions.map((action) => ({ - action: action.action ? action.action.actionType : undefined, - daysBeforeExpiry: action.trigger ? action.trigger.daysBeforeExpiry : undefined, - lifetimePercentage: action.trigger ? action.trigger.lifetimePercentage : undefined - })) - : undefined, - contentType: policy.secretProperties - ? (policy.secretProperties.contentType as CertificateContentType) - : undefined, - enhancedKeyUsage: x509Properties.ekus, - keyUsage: x509Properties.keyUsage, - validityInMonths: x509Properties.validityInMonths, - subject: x509Properties.subject, - subjectAlternativeNames: subjectAlternativeNames! - }; - - if (policy.attributes) { - certificatePolicy.enabled = policy.attributes.enabled; - } - - if (policy.keyProperties) { - certificatePolicy.keyType = policy.keyProperties.keyType as CertificateKeyType; - certificatePolicy.keySize = policy.keyProperties.keySize; - certificatePolicy.reuseKey = policy.keyProperties.reuseKey; - certificatePolicy.keyCurveName = policy.keyProperties.curve; - certificatePolicy.exportable = policy.keyProperties.exportable; - } - - if (policy.issuerParameters) { - certificatePolicy.issuerName = policy.issuerParameters && policy.issuerParameters.name; - certificatePolicy.certificateType = policy.issuerParameters - .certificateType as CertificateContentType; - certificatePolicy.certificateTransparency = policy.issuerParameters.certificateTransparency; - } - - return certificatePolicy; -} - -function toPublicIssuer(issuer: IssuerBundle = {}): CertificateIssuer { - const parsedId = parseKeyVaultCertificateId(issuer.id!); - const attributes: IssuerAttributes = issuer.attributes || {}; - - const publicIssuer: CertificateIssuer = { - id: issuer.id, - name: parsedId.name, - provider: issuer.provider, - accountId: issuer.credentials && issuer.credentials.accountId, - password: issuer.credentials && issuer.credentials.password, - enabled: attributes.enabled, - createdOn: attributes.created, - updatedOn: attributes.updated - }; - - if (issuer.organizationDetails) { - publicIssuer.organizationId = issuer.organizationDetails.id; - publicIssuer.administratorContacts = issuer.organizationDetails.adminDetails - ? issuer.organizationDetails.adminDetails.map((x) => ({ - email: x.emailAddress, - phone: x.phone, - firstName: x.firstName, - lastName: x.lastName - })) - : undefined; - } - return publicIssuer; -} - /** * The client to interact with the KeyVault certificates functionality */ @@ -410,21 +252,6 @@ export class CertificateClient { private readonly client: KeyVaultClient; - /** - * @internal - * @ignore - * A self reference that bypasses private methods, for the pollers. - */ - private readonly pollerClient: CertificateClientInterface = { - createCertificate: this.createCertificate.bind(this), - getPlainCertificateOperation: this.getPlainCertificateOperation.bind(this), - recoverDeletedCertificate: this.recoverDeletedCertificate.bind(this), - cancelCertificateOperation: this.cancelCertificateOperation.bind(this), - getCertificate: this.getCertificate.bind(this), - deleteCertificate: this.deleteCertificate.bind(this), - getDeletedCertificate: this.getDeletedCertificate.bind(this) - }; - /** * Creates an instance of CertificateClient. * @param {string} vaultUrl the base URL to the vault. @@ -491,7 +318,7 @@ export class CertificateClient { const currentSetResponse = await this.client.getCertificates(this.vaultUrl, optionsComplete); continuationState.continuationToken = currentSetResponse.nextLink; if (currentSetResponse.value) { - yield currentSetResponse.value.map(this.getPropertiesFromCertificateBundle, this); + yield currentSetResponse.value.map(getPropertiesFromCertificateBundle, this); } } while (continuationState.continuationToken) { @@ -501,7 +328,7 @@ export class CertificateClient { ); continuationState.continuationToken = currentSetResponse.nextLink; if (currentSetResponse.value) { - yield currentSetResponse.value.map(this.getPropertiesFromCertificateBundle, this); + yield currentSetResponse.value.map(getPropertiesFromCertificateBundle, this); } else { break; } @@ -546,8 +373,8 @@ export class CertificateClient { ): PagedAsyncIterableIterator { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("listPropertiesOfCertificates", requestOptions); - const updatedOptions = this.setParentSpan(span, requestOptions); + const span = createSpan("listPropertiesOfCertificates", requestOptions); + const updatedOptions = setParentSpan(span, requestOptions); const iter = this.listPropertiesOfCertificatesAll(updatedOptions); @@ -583,7 +410,7 @@ export class CertificateClient { ); continuationState.continuationToken = currentSetResponse.nextLink; if (currentSetResponse.value) { - yield currentSetResponse.value.map(this.getPropertiesFromCertificateBundle, this); + yield currentSetResponse.value.map(getPropertiesFromCertificateBundle, this); } } while (continuationState.continuationToken) { @@ -594,7 +421,7 @@ export class CertificateClient { ); continuationState.continuationToken = currentSetResponse.nextLink; if (currentSetResponse.value) { - yield currentSetResponse.value.map(this.getPropertiesFromCertificateBundle, this); + yield currentSetResponse.value.map(getPropertiesFromCertificateBundle, this); } else { break; } @@ -638,8 +465,8 @@ export class CertificateClient { options: ListPropertiesOfCertificateVersionsOptions = {} ): PagedAsyncIterableIterator { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("listPropertiesOfCertificateVersions", requestOptions); - const updatedOptions = this.setParentSpan(span, requestOptions); + const span = createSpan("listPropertiesOfCertificateVersions", requestOptions); + const updatedOptions = setParentSpan(span, requestOptions); const iter = this.listPropertiesOfCertificateVersionsAll(certificateName, updatedOptions); @@ -697,7 +524,8 @@ export class CertificateClient { const requestOptions = operationOptionsToRequestOptionsBase(options); const poller = new DeleteCertificatePoller({ certificateName, - client: this.pollerClient, + client: this.client, + vaultUrl: this.vaultUrl, ...options, requestOptions }); @@ -727,20 +555,20 @@ export class CertificateClient { ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("deleteContacts", requestOptions); + const span = createSpan("deleteContacts", requestOptions); let result: DeleteCertificateContactsResponse; try { result = await this.client.deleteCertificateContacts( this.vaultUrl, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); } - return this.coreContactsToCertificateContacts(result._response.parsedBody); + return coreContactsToCertificateContacts(result._response.parsedBody); } /** @@ -770,7 +598,7 @@ export class CertificateClient { })); const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("setCertificateContacts", requestOptions); + const span = createSpan("setCertificateContacts", requestOptions); let result: SetCertificateContactsResponse; @@ -778,12 +606,12 @@ export class CertificateClient { result = await this.client.setCertificateContacts( this.vaultUrl, { contactList: coreContacts }, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); } - return this.coreContactsToCertificateContacts(result._response.parsedBody); + return coreContactsToCertificateContacts(result._response.parsedBody); } /** @@ -807,19 +635,19 @@ export class CertificateClient { options: GetContactsOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("getCertificateContacts", requestOptions); + const span = createSpan("getCertificateContacts", requestOptions); let result: GetCertificateContactsResponse; try { result = await this.client.getCertificateContacts( this.vaultUrl, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); } - return this.coreContactsToCertificateContacts(result); + return coreContactsToCertificateContacts(result); } private async *listPropertiesOfIssuersPage( @@ -891,8 +719,8 @@ export class CertificateClient { options: ListPropertiesOfIssuersOptions = {} ): PagedAsyncIterableIterator { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("listIssuers", requestOptions); - const updatedOptions = this.setParentSpan(span, requestOptions); + const span = createSpan("listIssuers", requestOptions); + const updatedOptions = setParentSpan(span, requestOptions); const iter = this.listPropertiesOfIssuersAll(updatedOptions); @@ -937,7 +765,7 @@ export class CertificateClient { }; const requestOptions = operationOptionsToRequestOptionsBase(unflattenedOptions); - const span = this.createSpan("createIssuer", requestOptions); + const span = createSpan("createIssuer", requestOptions); const properties: IssuerProperties = requestOptions.properties || {}; const credentials: IssuerCredentials = requestOptions.credentials || {}; @@ -982,7 +810,7 @@ export class CertificateClient { this.vaultUrl, issuerName, provider, - this.setParentSpan(span, generatedOptions) + setParentSpan(span, generatedOptions) ); } finally { span.end(); @@ -1011,7 +839,7 @@ export class CertificateClient { options: UpdateIssuerOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("updateIssuer", requestOptions); + const span = createSpan("updateIssuer", requestOptions); const properties: IssuerProperties = requestOptions.properties || {}; const credentials: IssuerCredentials = requestOptions.credentials || {}; @@ -1055,7 +883,7 @@ export class CertificateClient { result = await this.client.updateCertificateIssuer( this.vaultUrl, issuerName, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); @@ -1085,7 +913,7 @@ export class CertificateClient { options: GetIssuerOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("getCertificateIssuer", requestOptions); + const span = createSpan("getCertificateIssuer", requestOptions); let result: GetCertificateIssuerResponse; @@ -1093,7 +921,7 @@ export class CertificateClient { result = await this.client.getCertificateIssuer( this.vaultUrl, issuerName, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); @@ -1120,7 +948,7 @@ export class CertificateClient { options: DeleteIssuerOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("deleteCertificateIssuer", requestOptions); + const span = createSpan("deleteCertificateIssuer", requestOptions); let result: DeleteCertificateIssuerResponse; @@ -1128,7 +956,7 @@ export class CertificateClient { result = await this.client.deleteCertificateIssuer( this.vaultUrl, issuerName, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); @@ -1183,7 +1011,8 @@ export class CertificateClient { certificatePolicy: policy, createCertificateOptions: options, requestOptions, - client: this.pollerClient, + client: this.client, + vaultUrl: this.vaultUrl, intervalInMs: options.intervalInMs, resumeFrom: options.resumeFrom }); @@ -1215,7 +1044,7 @@ export class CertificateClient { options: GetCertificateOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("getCertificate", requestOptions); + const span = createSpan("getCertificate", requestOptions); let result: GetCertificateResponse; @@ -1224,13 +1053,13 @@ export class CertificateClient { this.vaultUrl, certificateName, "", - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); } - return this.getCertificateWithPolicyFromCertificateBundle(result); + return getCertificateWithPolicyFromCertificateBundle(result); } /** @@ -1263,7 +1092,7 @@ export class CertificateClient { throw new Error("The 'version' cannot be empty."); } - const span = this.createSpan("getCertificateVersion", requestOptions); + const span = createSpan("getCertificateVersion", requestOptions); let result: GetCertificateResponse; @@ -1272,13 +1101,13 @@ export class CertificateClient { this.vaultUrl, certificateName, version, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); } - return this.getCertificateFromCertificateBundle(result); + return getCertificateFromCertificateBundle(result); } /** @@ -1313,7 +1142,7 @@ export class CertificateClient { ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("importCertificate", requestOptions); + const span = createSpan("importCertificate", requestOptions); const base64EncodedCertificate = parseCertificateBytes( certificateBytes, @@ -1327,13 +1156,13 @@ export class CertificateClient { this.vaultUrl, certificateName, base64EncodedCertificate, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); } - return this.getCertificateWithPolicyFromCertificateBundle(result); + return getCertificateWithPolicyFromCertificateBundle(result); } /** @@ -1358,7 +1187,7 @@ export class CertificateClient { options: GetCertificatePolicyOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("getCertificatePolicy", requestOptions); + const span = createSpan("getCertificatePolicy", requestOptions); let result: GetCertificatePolicyResponse; @@ -1366,7 +1195,7 @@ export class CertificateClient { result = await this.client.getCertificatePolicy( this.vaultUrl, certificateName, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); @@ -1388,7 +1217,7 @@ export class CertificateClient { options: UpdateCertificatePolicyOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("updateCertificatePolicy", requestOptions); + const span = createSpan("updateCertificatePolicy", requestOptions); const corePolicy = toCorePolicy(undefined, policy); @@ -1398,7 +1227,7 @@ export class CertificateClient { this.vaultUrl, certificateName, corePolicy, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); @@ -1434,56 +1263,20 @@ export class CertificateClient { options: UpdateCertificatePropertiesOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("updateCertificateProperties", requestOptions); + const span = createSpan("updateCertificateProperties", requestOptions); let result: UpdateCertificateResponse; try { result = await this.client.updateCertificate(this.vaultUrl, certificateName, version, { - ...this.setParentSpan(span, requestOptions), + ...setParentSpan(span, requestOptions), certificateAttributes: toCoreAttributes(options) }); } finally { span.end(); } - return this.getCertificateFromCertificateBundle(result._response.parsedBody); - } - - /** - * @internal - * @ignore - * Cancels a certificate creation operation that is already in progress. This operation requires the certificates/update permission. - * - * @summary Cancels a certificate's operation - * @param certificateName The name of the certificate - * @param cancel Whether to cancel the operation or not - * @param {CancelCertificateOperationOptions} [options] The optional parameters - */ - private async cancelCertificateOperation( - certificateName: string, - options: CancelCertificateOperationOptions = {} - ): Promise { - const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("cancelCertificateOperation", requestOptions); - - let result: UpdateCertificateOperationResponse; - try { - result = await this.client.updateCertificateOperation( - this.vaultUrl, - certificateName, - true, - this.setParentSpan(span, requestOptions) - ); - } finally { - span.end(); - } - - return this.getCertificateOperationFromCoreOperation( - certificateName, - this.vaultUrl, - result._response.parsedBody - ); + return getCertificateFromCertificateBundle(result._response.parsedBody); } /** @@ -1515,7 +1308,8 @@ export class CertificateClient { const requestOptions = operationOptionsToRequestOptionsBase(options); const poller = new CertificateOperationPoller({ certificateName, - client: this.pollerClient, + client: this.client, + vaultUrl: this.vaultUrl, intervalInMs: options.intervalInMs, resumeFrom: options.resumeFrom, requestOptions @@ -1548,7 +1342,7 @@ export class CertificateClient { options: DeleteCertificateOperationOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("deleteCertificateOperation", requestOptions); + const span = createSpan("deleteCertificateOperation", requestOptions); let result: DeleteCertificateOperationResponse; @@ -1556,13 +1350,13 @@ export class CertificateClient { result = await this.client.deleteCertificateOperation( this.vaultUrl, certificateName, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); } - return this.getCertificateOperationFromCoreOperation( + return getCertificateOperationFromCoreOperation( certificateName, this.vaultUrl, result._response.parsedBody @@ -1609,7 +1403,7 @@ export class CertificateClient { options: MergeCertificateOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("mergeCertificate", requestOptions); + const span = createSpan("mergeCertificate", requestOptions); let result: MergeCertificateResponse; try { @@ -1617,12 +1411,12 @@ export class CertificateClient { this.vaultUrl, certificateName, x509Certificates, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); } - return this.getCertificateWithPolicyFromCertificateBundle(result._response.parsedBody); + return getCertificateWithPolicyFromCertificateBundle(result._response.parsedBody); } /** @@ -1647,14 +1441,14 @@ export class CertificateClient { options: BackupCertificateOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("backupCertificate", requestOptions); + const span = createSpan("backupCertificate", requestOptions); let result: BackupCertificateResponse; try { result = await this.client.backupCertificate( this.vaultUrl, certificateName, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); @@ -1688,7 +1482,7 @@ export class CertificateClient { options: RestoreCertificateBackupOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("restoreCertificate", requestOptions); + const span = createSpan("restoreCertificate", requestOptions); let result: RestoreCertificateResponse; @@ -1696,13 +1490,13 @@ export class CertificateClient { result = await this.client.restoreCertificate( this.vaultUrl, backup, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); } - return this.getCertificateWithPolicyFromCertificateBundle(result._response.parsedBody); + return getCertificateWithPolicyFromCertificateBundle(result._response.parsedBody); } private async *listDeletedCertificatesPage( @@ -1720,7 +1514,7 @@ export class CertificateClient { ); continuationState.continuationToken = currentSetResponse.nextLink; if (currentSetResponse.value) { - yield currentSetResponse.value.map(this.getDeletedCertificateFromItem, this); + yield currentSetResponse.value.map(getDeletedCertificateFromItem, this); } } while (continuationState.continuationToken) { @@ -1730,7 +1524,7 @@ export class CertificateClient { ); continuationState.continuationToken = currentSetResponse.nextLink; if (currentSetResponse.value) { - yield currentSetResponse.value.map(this.getDeletedCertificateFromItem, this); + yield currentSetResponse.value.map(getDeletedCertificateFromItem, this); } else { break; } @@ -1772,8 +1566,8 @@ export class CertificateClient { options: ListDeletedCertificatesOptions = {} ): PagedAsyncIterableIterator { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("listPropertiesOfDeletedCertificates", requestOptions); - const updatedOptions = this.setParentSpan(span, requestOptions); + const span = createSpan("listPropertiesOfDeletedCertificates", requestOptions); + const updatedOptions = setParentSpan(span, requestOptions); const iter = this.listDeletedCertificatesAll(updatedOptions); @@ -1811,20 +1605,20 @@ export class CertificateClient { options: GetDeletedCertificateOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("getDeletedCertificate", requestOptions); + const span = createSpan("getDeletedCertificate", requestOptions); let result: GetDeletedCertificateResponse; try { result = await this.client.getDeletedCertificate( this.vaultUrl, certificateName, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); } - return this.getDeletedCertificateFromDeletedCertificateBundle(result._response.parsedBody); + return getDeletedCertificateFromDeletedCertificateBundle(result._response.parsedBody); } /** @@ -1848,13 +1642,13 @@ export class CertificateClient { options: PurgeDeletedCertificateOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("purgeDeletedCertificate", requestOptions); + const span = createSpan("purgeDeletedCertificate", requestOptions); try { await this.client.purgeDeletedCertificate( this.vaultUrl, certificateName, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); @@ -1899,7 +1693,8 @@ export class CertificateClient { const requestOptions = operationOptionsToRequestOptionsBase(options); const poller = new RecoverDeletedCertificatePoller({ certificateName, - client: this.pollerClient, + client: this.client, + vaultUrl: this.vaultUrl, ...options, requestOptions }); @@ -1907,348 +1702,4 @@ export class CertificateClient { await poller.poll(); return poller; } - - /** - * @internal - * @ignore - * Creates a new certificate. If this is the first version, the certificate resource is created. This operation requires the certificates/create permission. - * @summary Creates a certificate - * @param certificateName The name of the certificate - * @param certificatePolicy The certificate's policy - * @param {CreateCertificateOptions} [options] Optional parameters - */ - private async createCertificate( - certificateName: string, - certificatePolicy: CertificatePolicy, - options: CreateCertificateOptions = {} - ): Promise { - const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("createCertificate", requestOptions); - - const id = options.id; - const certificateAttributes = toCoreAttributes(options); - const corePolicy = toCorePolicy(id, certificatePolicy, certificateAttributes); - - const updatedOptions = { - ...this.setParentSpan(span, requestOptions), - certificatePolicy: corePolicy, - certificateAttributes - }; - - let result: CreateCertificateResponse; - - try { - result = await this.client.createCertificate(this.vaultUrl, certificateName, updatedOptions); - } finally { - span.end(); - } - - return this.getCertificateWithPolicyFromCertificateBundle(result); - } - - /** - * @internal - * @ignore - * The DELETE operation applies to any certificate stored in Azure Key Vault. DELETE cannot be applied - * to an individual version of a certificate. This operation requires the certificates/delete permission. - * @summary Deletes a certificate from a specified key vault. - * @param certificateName The name of the certificate. - * @param {DeleteCertificateOptions} [options] The optional parameters - */ - private async deleteCertificate( - certificateName: string, - options: DeleteCertificateOptions = {} - ): Promise { - const requestOptions = operationOptionsToRequestOptionsBase(options); - - const span = this.createSpan("deleteCertificate", requestOptions); - - let response: DeleteCertificateResponse; - try { - response = await this.client.deleteCertificate( - this.vaultUrl, - certificateName, - this.setParentSpan(span, requestOptions) - ); - } finally { - span.end(); - } - - return this.getDeletedCertificateFromDeletedCertificateBundle(response); - } - - /** - * @internal - * @ignore - * Recovers the deleted certificate in the specified vault. This operation can only be performed on a soft-delete enabled vault. This operation - * requires the certificate/recover permission. - * @summary Recovers a deleted certificate - * @param certificateName The name of the deleted certificate - * @param {RecoverDeletedCertificateOptions} [options] The optional parameters - */ - private async recoverDeletedCertificate( - certificateName: string, - options: RecoverDeletedCertificateOptions = {} - ): Promise { - const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("recoverDeletedCertificate", requestOptions); - - let result: RecoverDeletedCertificateResponse; - - try { - result = await this.client.recoverDeletedCertificate( - this.vaultUrl, - certificateName, - this.setParentSpan(span, requestOptions) - ); - } finally { - span.end(); - } - - return this.getCertificateWithPolicyFromCertificateBundle(result._response.parsedBody); - } - - private getPropertiesFromCertificateBundle( - certificateBundle: CertificateBundle - ): CertificateProperties { - const parsedId = parseKeyVaultCertificateId(certificateBundle.id!); - const attributes: CertificateAttributes = certificateBundle.attributes || {}; - - const abstractProperties: CertificateProperties = { - createdOn: attributes.created, - updatedOn: attributes.updated, - expiresOn: attributes.expires, - id: certificateBundle.id, - name: parsedId.name, - enabled: attributes.enabled, - notBefore: attributes.notBefore, - recoveryLevel: attributes.recoveryLevel, - vaultUrl: parsedId.vaultUrl, - version: parsedId.version, - tags: certificateBundle.tags, - x509Thumbprint: certificateBundle.x509Thumbprint, - recoverableDays: attributes.recoverableDays - }; - - return abstractProperties; - } - - /** - * @internal - * @ignore - * Gets the certificate operation. - * @summary Gets the certificate operation - * @param certificateName The name of the certificate - * @param {GetPlainCertificateOperationOptions} [options] The optional parameters - */ - private async getPlainCertificateOperation( - certificateName: string, - options?: GetPlainCertificateOperationOptions - ): Promise { - const span = this.createSpan("getPlainCertificateOperation", options); - - let result: GetCertificateOperationResponse; - - try { - result = await this.client.getCertificateOperation( - this.vaultUrl, - certificateName, - this.setParentSpan(span, options) - ); - } finally { - span.end(); - } - - return this.getCertificateOperationFromCoreOperation( - certificateName, - this.vaultUrl, - result._response.parsedBody - ); - } - - private getCertificateFromCertificateBundle( - certificateBundle: CertificateBundle - ): KeyVaultCertificate { - const parsedId = parseKeyVaultCertificateId(certificateBundle.id!); - - const attributes: CertificateAttributes = certificateBundle.attributes || {}; - - const abstractProperties: CertificateProperties = { - createdOn: attributes.created, - updatedOn: attributes.updated, - expiresOn: attributes.expires, - id: certificateBundle.id, - name: parsedId.name, - enabled: attributes.enabled, - notBefore: attributes.notBefore, - recoveryLevel: attributes.recoveryLevel, - vaultUrl: parsedId.vaultUrl, - version: parsedId.version, - tags: certificateBundle.tags, - x509Thumbprint: certificateBundle.x509Thumbprint, - recoverableDays: attributes.recoverableDays - }; - - return { - keyId: certificateBundle.kid, - secretId: certificateBundle.sid, - name: parsedId.name, - cer: certificateBundle.cer, - properties: abstractProperties - }; - } - - private getCertificateWithPolicyFromCertificateBundle( - certificateBundle: CertificateBundle - ): KeyVaultCertificateWithPolicy { - const parsedId = parseKeyVaultCertificateId(certificateBundle.id!); - - const attributes: CertificateAttributes = certificateBundle.attributes || {}; - const policy = toPublicPolicy(certificateBundle.policy || {}); - - const abstractProperties: CertificateProperties = { - createdOn: attributes.created, - updatedOn: attributes.updated, - expiresOn: attributes.expires, - id: certificateBundle.id, - name: parsedId.name, - enabled: attributes.enabled, - notBefore: attributes.notBefore, - recoveryLevel: attributes.recoveryLevel, - vaultUrl: parsedId.vaultUrl, - version: parsedId.version, - tags: certificateBundle.tags, - x509Thumbprint: certificateBundle.x509Thumbprint, - recoverableDays: attributes.recoverableDays - }; - - return { - keyId: certificateBundle.kid, - secretId: certificateBundle.sid, - name: parsedId.name, - cer: certificateBundle.cer, - policy, - properties: abstractProperties - }; - } - - private getDeletedCertificateFromDeletedCertificateBundle( - certificateBundle: DeletedCertificateBundle - ): DeletedCertificate { - const certificate: KeyVaultCertificateWithPolicy = this.getCertificateWithPolicyFromCertificateBundle( - certificateBundle - ); - - return { - ...certificate, - recoveryId: certificateBundle.recoveryId, - scheduledPurgeDate: certificateBundle.scheduledPurgeDate, - deletedOn: certificateBundle.deletedDate - }; - } - - private getDeletedCertificateFromItem(item: DeletedCertificateItem): DeletedCertificate { - const parsedId = parseKeyVaultCertificateId(item.id!); - - const attributes: any = item.attributes || {}; - - const abstractProperties: any = { - createdOn: attributes.created, - updatedOn: attributes.updated, - expiresOn: attributes.expires, - ...parsedId, - ...item, - ...item.attributes - }; - - if (abstractProperties.deletedDate) { - delete abstractProperties.deletedDate; - } - - if (abstractProperties.expires) { - delete abstractProperties.expires; - } - if (abstractProperties.created) { - delete abstractProperties.created; - } - if (abstractProperties.updated) { - delete abstractProperties.updated; - } - - return { - name: parsedId.name, - properties: abstractProperties - }; - } - - private getCertificateOperationFromCoreOperation( - certificateName: string, - vaultUrl: string, - operation: CoreCertificateOperation - ): CertificateOperation { - return { - cancellationRequested: operation.cancellationRequested, - name: certificateName, - issuerName: operation.issuerParameters ? operation.issuerParameters.name : undefined, - certificateTransparency: operation.issuerParameters - ? operation.issuerParameters.certificateTransparency - : undefined, - certificateType: operation.issuerParameters - ? operation.issuerParameters.certificateType - : undefined, - csr: operation.csr, - error: operation.error, - id: operation.id, - requestId: operation.requestId, - status: operation.status, - statusDetails: operation.statusDetails, - target: operation.target, - vaultUrl: vaultUrl - }; - } - - private coreContactsToCertificateContacts(contacts: CoreContacts): CertificateContact[] { - return contacts.contactList - ? contacts.contactList.map( - (x) => ({ email: x.emailAddress, phone: x.phone, name: x.name } as CertificateContact) - ) - : []; - } - - /** - * Creates a span using the tracer that was set by the user - * @param methodName The name of the method for which the span is being created. - * @param requestOptions The options for the underlying http request. - */ - private createSpan(methodName: string, requestOptions: RequestOptionsBase = {}): Span { - const tracer = getTracer(); - const span = tracer.startSpan(methodName, requestOptions && requestOptions.spanOptions); - span.setAttribute("az.namespace", "Microsoft.KeyVault"); - return span; - } - - /** - * Returns updated HTTP options with the given span as the parent of future spans, - * if applicable. - * @param span The span for the current operation - * @param options The options for the underlying http request - */ - private setParentSpan(span: Span, options: RequestOptionsBase = {}): RequestOptionsBase { - if (span.isRecording()) { - const spanOptions = options.spanOptions || {}; - return { - ...options, - spanOptions: { - ...spanOptions, - parent: span.context(), - attributes: { - ...spanOptions.attributes, - "az.namespace": "Microsoft.KeyVault" - } - } - }; - } else { - return options; - } - } } diff --git a/sdk/keyvault/keyvault-certificates/src/lro/create/operation.ts b/sdk/keyvault/keyvault-certificates/src/lro/create/operation.ts index 601a62779d9d..d7a94bbb10e0 100644 --- a/sdk/keyvault/keyvault-certificates/src/lro/create/operation.ts +++ b/sdk/keyvault/keyvault-certificates/src/lro/create/operation.ts @@ -2,31 +2,48 @@ // Licensed under the MIT license. import { AbortSignalLike, AbortSignal } from "@azure/abort-controller"; -import { PollOperationState, PollOperation } from "@azure/core-lro"; -import { RequestOptionsBase } from "@azure/core-http"; +import { operationOptionsToRequestOptionsBase, RequestOptionsBase } from "@azure/core-http"; import { KeyVaultCertificateWithPolicy, CreateCertificateOptions, CertificatePolicy, - CertificateClientInterface + GetCertificateOptions, + GetPlainCertificateOperationOptions, + CancelCertificateOperationOptions } from "../../certificatesModels"; -import { CertificateOperation } from "../../generated/models"; +import { + CertificateOperation, + CreateCertificateResponse, + GetCertificateOperationResponse, + GetCertificateResponse, + UpdateCertificateOperationResponse +} from "../../generated/models"; +import { + KeyVaultCertificatePollOperation, + KeyVaultCertificatePollOperationState +} from "../keyVaultCertificatePoller"; +import { KeyVaultClient } from "../../generated/keyVaultClient"; +import { + getCertificateOperationFromCoreOperation, + getCertificateWithPolicyFromCertificateBundle, + toCoreAttributes, + toCorePolicy +} from "../../transformations"; +import { createSpan } from "../../../../keyvault-common"; +import { setParentSpan } from "../../../../keyvault-common"; /** * The public representation of the CreateCertificatePoller operation state. */ -export type CreateCertificateState = PollOperationState; +export type CreateCertificateState = KeyVaultCertificatePollOperationState< + KeyVaultCertificateWithPolicy +>; /** * An interface representing the state of a create certificate's poll operation - * @internal */ export interface CreateCertificatePollOperationState - extends PollOperationState { - /** - * The name of the certificate. - */ - certificateName: string; + extends KeyVaultCertificatePollOperationState { /** * The policy of the certificate. */ @@ -35,14 +52,6 @@ export interface CreateCertificatePollOperationState * Optional parameters sent to createCertificates */ createCertificateOptions: CreateCertificateOptions; - /** - * Options for the core-http requests. - */ - requestOptions?: RequestOptionsBase; - /** - * An interface representing a CertificateClient. For internal use. - */ - client: CertificateClientInterface; /** * The operation of the certificate */ @@ -51,115 +60,202 @@ export interface CreateCertificatePollOperationState /** * An interface representing a create certificate's poll operation - * @internal */ -export type CreateCertificatePollOperation = PollOperation< +export class CreateCertificatePollOperation extends KeyVaultCertificatePollOperation< CreateCertificatePollOperationState, KeyVaultCertificateWithPolicy ->; +> { + constructor( + public state: CreateCertificatePollOperationState, + private vaultUrl: string, + private client: KeyVaultClient, + private requestOptions: RequestOptionsBase = {} + ) { + super(state); + } -/** - * @summary Reaches to the service and updates the create certificate's poll operation. - * @param [options] The optional parameters, which are an abortSignal from @azure/abort-controller and a function that triggers the poller's onProgress function. - * @internal - */ -async function update( - this: CreateCertificatePollOperation, - options: { - abortSignal?: AbortSignalLike; - fireProgress?: (state: CreateCertificatePollOperationState) => void; - } = {} -): Promise { - const state = this.state; - const { certificateName, certificatePolicy, createCertificateOptions, client } = state; - - const requestOptions = state.requestOptions || {}; - if (options.abortSignal) { - requestOptions.abortSignal = options.abortSignal; - createCertificateOptions.abortSignal = options.abortSignal; + /** + * Creates a new certificate. If this is the first version, the certificate resource is created. This operation requires the certificates/create permission. + */ + private async createCertificate( + certificateName: string, + certificatePolicy: CertificatePolicy, + options: CreateCertificateOptions = {} + ): Promise { + const requestOptions = operationOptionsToRequestOptionsBase(options); + const span = createSpan("generatedClient.createCertificate", requestOptions); + + const id = options.id; + const certificateAttributes = toCoreAttributes(options); + const corePolicy = toCorePolicy(id, certificatePolicy, certificateAttributes); + + const updatedOptions = { + ...setParentSpan(span, requestOptions), + certificatePolicy: corePolicy, + certificateAttributes + }; + + let result: CreateCertificateResponse; + + try { + result = await this.client.createCertificate(this.vaultUrl, certificateName, updatedOptions); + } finally { + span.end(); + } + + return getCertificateWithPolicyFromCertificateBundle(result); } - if (!state.isStarted) { - state.isStarted = true; - state.result = await client.createCertificate( - certificateName, - certificatePolicy!, - createCertificateOptions - ); - state.certificateOperation = await client.getPlainCertificateOperation( + /** + * Gets the latest information available from a specific certificate, including the certificate's policy. This operation requires the certificates/get permission. + */ + private async getCertificate( + certificateName: string, + options: GetCertificateOptions = {} + ): Promise { + const requestOptions = operationOptionsToRequestOptionsBase(options); + const span = createSpan("generatedClient.getCertificate", requestOptions); + + let result: GetCertificateResponse; + + try { + result = await this.client.getCertificate( + this.vaultUrl, + certificateName, + "", + setParentSpan(span, requestOptions) + ); + } finally { + span.end(); + } + + return getCertificateWithPolicyFromCertificateBundle(result); + } + + /** + * Gets the certificate operation. + */ + private async getPlainCertificateOperation( + certificateName: string, + options?: GetPlainCertificateOperationOptions + ): Promise { + const span = createSpan("generatedClient.getPlainCertificateOperation", options); + + let result: GetCertificateOperationResponse; + + try { + result = await this.client.getCertificateOperation( + this.vaultUrl, + certificateName, + setParentSpan(span, options) + ); + } finally { + span.end(); + } + + return getCertificateOperationFromCoreOperation( certificateName, - requestOptions + this.vaultUrl, + result._response.parsedBody ); - } else if (!state.isCompleted) { - state.certificateOperation = await client.getPlainCertificateOperation( + } + + /** + * Cancels a certificate creation operation that is already in progress. This operation requires the certificates/update permission. + */ + private async cancelCertificateOperation( + certificateName: string, + options: CancelCertificateOperationOptions = {} + ): Promise { + const requestOptions = operationOptionsToRequestOptionsBase(options); + const span = createSpan("generatedClient.cancelCertificateOperation", requestOptions); + + let result: UpdateCertificateOperationResponse; + try { + result = await this.client.updateCertificateOperation( + this.vaultUrl, + certificateName, + true, + setParentSpan(span, requestOptions) + ); + } finally { + span.end(); + } + + return getCertificateOperationFromCoreOperation( certificateName, - requestOptions + this.vaultUrl, + result._response.parsedBody ); } - if (state.certificateOperation && state.certificateOperation.status !== "inProgress") { - state.isCompleted = true; - state.result = await client.getCertificate(certificateName, requestOptions); - if (state.certificateOperation.error) { - state.error = new Error(state.certificateOperation.error.message); + /** + * Reaches to the service and updates the create certificate's poll operation. + */ + async update( + this: CreateCertificatePollOperation, + options: { + abortSignal?: AbortSignalLike; + fireProgress?: (state: CreateCertificatePollOperationState) => void; + } = {} + ): Promise { + const state = this.state; + const { certificateName, certificatePolicy, createCertificateOptions } = state; + + if (options.abortSignal) { + this.requestOptions.abortSignal = options.abortSignal; + createCertificateOptions.abortSignal = options.abortSignal; } - } - return makeCreateCertificatePollOperation(state); -} + if (!state.isStarted) { + state.isStarted = true; + state.result = await this.createCertificate( + certificateName, + certificatePolicy!, + createCertificateOptions + ); + this.state.certificateOperation = await this.getPlainCertificateOperation( + certificateName, + this.requestOptions + ); + } else if (!state.isCompleted) { + this.state.certificateOperation = await this.getPlainCertificateOperation( + certificateName, + this.requestOptions + ); + } -/** - * @summary Reaches to the service and cancels the certificate's operation, also updating the certificate's poll operation - * @param [options] The optional parameters, which is only an abortSignal from @azure/abort-controller - * @internal - */ -async function cancel( - this: CreateCertificatePollOperation, - options: { abortSignal?: AbortSignal } = {} -): Promise { - const state = this.state; - const { client, certificateName } = state; - - const requestOptions = state.requestOptions || {}; - if (options.abortSignal) { - requestOptions.abortSignal = options.abortSignal; + if (state.certificateOperation && state.certificateOperation.status !== "inProgress") { + state.isCompleted = true; + state.result = await this.getCertificate(certificateName, this.requestOptions); + if (state.certificateOperation.error) { + state.error = new Error(state.certificateOperation.error.message); + } + } + + return this; } - state.certificateOperation = await client.cancelCertificateOperation( - certificateName, - requestOptions - ); + /** + * Reaches to the service and cancels the certificate's operation, also updating the certificate's poll operation + */ + async cancel( + this: CreateCertificatePollOperation, + options: { abortSignal?: AbortSignal } = {} + ): Promise { + const state = this.state; + const { certificateName } = state; - return makeCreateCertificatePollOperation({ - ...this.state, - isCancelled: true - }); -} + if (options.abortSignal) { + this.requestOptions.abortSignal = options.abortSignal; + } -/** - * @summary Serializes the create certificate's poll operation - * @internal - */ -function toString(this: CreateCertificatePollOperation): string { - return JSON.stringify({ - state: this.state - }); -} + state.certificateOperation = await this.cancelCertificateOperation( + certificateName, + this.requestOptions + ); -/** - * @summary Builds a create certificate's poll operation - * @param [state] A poll operation's state, in case the new one is intended to follow up where the previous one was left. - * @internal - */ -export function makeCreateCertificatePollOperation( - state: CreateCertificatePollOperationState -): CreateCertificatePollOperation { - return { - state: { - ...state - }, - update, - cancel, - toString - }; + this.state.isCancelled = true; + return this; + } } diff --git a/sdk/keyvault/keyvault-certificates/src/lro/create/poller.ts b/sdk/keyvault/keyvault-certificates/src/lro/create/poller.ts index 461cd9f624ae..a29770dfd412 100644 --- a/sdk/keyvault/keyvault-certificates/src/lro/create/poller.ts +++ b/sdk/keyvault/keyvault-certificates/src/lro/create/poller.ts @@ -1,45 +1,32 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { delay, RequestOptionsBase } from "@azure/core-http"; -import { Poller } from "@azure/core-lro"; -import { - makeCreateCertificatePollOperation, - CreateCertificatePollOperationState -} from "./operation"; +import { CreateCertificatePollOperation, CreateCertificateState } from "./operation"; import { KeyVaultCertificateWithPolicy, CreateCertificateOptions, - CertificateClientInterface, CertificatePolicy } from "../../certificatesModels"; +import { + KeyVaultCertificatePoller, + KeyVaultCertificatePollerOptions +} from "../keyVaultCertificatePoller"; -export interface CreateCertificatePollerOptions { - client: CertificateClientInterface; - certificateName: string; +export interface CreateCertificatePollerOptions extends KeyVaultCertificatePollerOptions { certificatePolicy?: CertificatePolicy; createCertificateOptions: CreateCertificateOptions; - requestOptions?: RequestOptionsBase; - intervalInMs?: number; - resumeFrom?: string; } /** * Class that deletes a poller that waits until a certificate finishes being deleted - * @internal */ -export class CreateCertificatePoller extends Poller< - CreateCertificatePollOperationState, +export class CreateCertificatePoller extends KeyVaultCertificatePoller< + CreateCertificateState, KeyVaultCertificateWithPolicy > { - /** - * Defines how much time the poller is going to wait before making a new request to the service. - * @memberof CreateCertificatePoller - */ - public intervalInMs: number; - constructor(options: CreateCertificatePollerOptions) { const { + vaultUrl, client, certificateName, certificatePolicy, @@ -49,31 +36,26 @@ export class CreateCertificatePoller extends Poller< resumeFrom } = options; - let state: CreateCertificatePollOperationState | undefined; + let state: CreateCertificateState | undefined; if (resumeFrom) { state = JSON.parse(resumeFrom).state; } - const operation = makeCreateCertificatePollOperation({ - ...state, - certificateName, - certificatePolicy, - createCertificateOptions, - requestOptions, - client - }); + const operation = new CreateCertificatePollOperation( + { + ...state, + certificateName, + certificatePolicy, + createCertificateOptions + }, + vaultUrl, + client, + requestOptions + ); super(operation); this.intervalInMs = intervalInMs; } - - /** - * The method used by the poller to wait before attempting to update its operation. - * @memberof CreateCertificatePoller - */ - async delay(): Promise { - return delay(this.intervalInMs); - } } diff --git a/sdk/keyvault/keyvault-certificates/src/lro/delete/operation.ts b/sdk/keyvault/keyvault-certificates/src/lro/delete/operation.ts index c5679db5e116..ce42e9a7cecd 100644 --- a/sdk/keyvault/keyvault-certificates/src/lro/delete/operation.ts +++ b/sdk/keyvault/keyvault-certificates/src/lro/delete/operation.ts @@ -2,124 +2,140 @@ // Licensed under the MIT license. import { AbortSignalLike } from "@azure/abort-controller"; -import { PollOperationState, PollOperation } from "@azure/core-lro"; -import { RequestOptionsBase } from "@azure/core-http"; -import { DeletedCertificate, CertificateClientInterface } from "../../certificatesModels"; +import { operationOptionsToRequestOptionsBase, RequestOptionsBase } from "@azure/core-http"; +import { + DeleteCertificateOptions, + DeletedCertificate, + GetDeletedCertificateOptions +} from "../../certificatesModels"; +import { + KeyVaultCertificatePollOperation, + KeyVaultCertificatePollOperationState +} from "../keyVaultCertificatePoller"; +import { KeyVaultClient } from "../../generated/keyVaultClient"; +import { getDeletedCertificateFromDeletedCertificateBundle } from "../../transformations"; +import { DeleteCertificateResponse, GetDeletedCertificateResponse } from "../../generated/models"; +import { createSpan, setParentSpan } from "../../../../keyvault-common"; /** * The public representation of the DeleteCertificatePoller operation state. */ -export type DeleteCertificateState = PollOperationState; +export type DeleteCertificateState = KeyVaultCertificatePollOperationState; /** * An interface representing the state of a delete certificate's poll operation - * @internal */ export interface DeleteCertificatePollOperationState - extends PollOperationState { - /** - * The name of the certificate. - */ - certificateName: string; - /** - * Options for the core-http requests. - */ - requestOptions?: RequestOptionsBase; - /** - * An interface representing a CertificateClient. For internal use. - */ - client: CertificateClientInterface; -} + extends KeyVaultCertificatePollOperationState {} /** * An interface representing a delete certificate's poll operation - * @internal */ -export type DeleteCertificatePollOperation = PollOperation< +export class DeleteCertificatePollOperation extends KeyVaultCertificatePollOperation< DeleteCertificatePollOperationState, DeletedCertificate ->; - -/** - * @summary Reaches to the service and updates the delete certificate's poll operation. - * @param [options] The optional parameters, which are an abortSignal from @azure/abort-controller and a function that triggers the poller's onProgress function. - * @internal - */ -async function update( - this: DeleteCertificatePollOperation, - options: { - abortSignal?: AbortSignalLike; - fireProgress?: (state: DeleteCertificatePollOperationState) => void; - } = {} -): Promise { - const state = this.state; - const { certificateName, client } = state; - - const requestOptions = state.requestOptions || {}; - if (options.abortSignal) { - requestOptions.abortSignal = options.abortSignal; +> { + constructor( + public state: DeleteCertificatePollOperationState, + private vaultUrl: string, + private client: KeyVaultClient, + private requestOptions: RequestOptionsBase = {} + ) { + super(state, { cancelMessage: "Canceling the deletion of a certificate is not supported." }); } - if (!state.isStarted) { - const deletedCertificate = await client.deleteCertificate(certificateName, requestOptions); - state.isStarted = true; - state.result = deletedCertificate; - if (!deletedCertificate.recoveryId) { - state.isCompleted = true; + /** + * The DELETE operation applies to any certificate stored in Azure Key Vault. DELETE cannot be applied + * to an individual version of a certificate. This operation requires the certificates/delete permission. + */ + private async deleteCertificate( + certificateName: string, + options: DeleteCertificateOptions = {} + ): Promise { + const requestOptions = operationOptionsToRequestOptionsBase(options); + + const span = createSpan("generatedClient.deleteCertificate", requestOptions); + + let response: DeleteCertificateResponse; + try { + response = await this.client.deleteCertificate( + this.vaultUrl, + certificateName, + setParentSpan(span, requestOptions) + ); + } finally { + span.end(); } + + return getDeletedCertificateFromDeletedCertificateBundle(response); } - if (!state.isCompleted) { + /** + * Retrieves the deleted certificate information plus its attributes, such as retention interval, scheduled permanent deletion and the + * current deletion recovery level. This operation requires the certificates/get permission. + */ + public async getDeletedCertificate( + certificateName: string, + options: GetDeletedCertificateOptions = {} + ): Promise { + const requestOptions = operationOptionsToRequestOptionsBase(options); + const span = createSpan("generatedClient.getDeletedCertificate", requestOptions); + + let result: GetDeletedCertificateResponse; try { - state.result = await client.getDeletedCertificate(certificateName, { requestOptions }); - state.isCompleted = true; - } catch (error) { - if (error.statusCode === 403) { - // At this point, the resource exists but the user doesn't have access to it. - state.isCompleted = true; - } else if (error.statusCode !== 404) { - state.error = error; - state.isCompleted = true; - } + result = await this.client.getDeletedCertificate( + this.vaultUrl, + certificateName, + setParentSpan(span, requestOptions) + ); + } finally { + span.end(); } + + return getDeletedCertificateFromDeletedCertificateBundle(result._response.parsedBody); } - return makeDeleteCertificatePollOperation(state); -} + /** + * Reaches to the service and updates the delete certificate's poll operation. + */ + async update( + this: DeleteCertificatePollOperation, + options: { + abortSignal?: AbortSignalLike; + fireProgress?: (state: DeleteCertificatePollOperationState) => void; + } = {} + ): Promise { + const state = this.state; + const { certificateName } = state; -/** - * @summary Reaches to the service and cancels the certificate's operation, also updating the certificate's poll operation - * @param [options] The optional parameters, which is only an abortSignal from @azure/abort-controller - * @internal - */ -async function cancel(): Promise { - throw new Error("Canceling the deletion of a certificate is not supported."); -} + if (options.abortSignal) { + this.requestOptions.abortSignal = options.abortSignal; + } -/** - * @summary Serializes the create certificate's poll operation - * @internal - */ -function toString(this: DeleteCertificatePollOperation): string { - return JSON.stringify({ - state: this.state - }); -} + if (!state.isStarted) { + const deletedCertificate = await this.deleteCertificate(certificateName, this.requestOptions); + state.isStarted = true; + state.result = deletedCertificate; + if (!deletedCertificate.recoveryId) { + state.isCompleted = true; + } + } -/** - * @summary Builds a create certificate's poll operation - * @param [state] A poll operation's state, in case the new one is intended to follow up where the previous one was left. - * @internal - */ -export function makeDeleteCertificatePollOperation( - state: DeleteCertificatePollOperationState -): DeleteCertificatePollOperation { - return { - state: { - ...state - }, - update, - cancel, - toString - }; + if (!state.isCompleted) { + try { + state.result = await this.getDeletedCertificate(certificateName, this.requestOptions); + state.isCompleted = true; + } catch (error) { + if (error.statusCode === 403) { + // At this point, the resource exists but the user doesn't have access to it. + state.isCompleted = true; + } else if (error.statusCode !== 404) { + state.error = error; + state.isCompleted = true; + } + } + } + + return this; + } } diff --git a/sdk/keyvault/keyvault-certificates/src/lro/delete/poller.ts b/sdk/keyvault/keyvault-certificates/src/lro/delete/poller.ts index 76071ccc128a..b1d50e5e3091 100644 --- a/sdk/keyvault/keyvault-certificates/src/lro/delete/poller.ts +++ b/sdk/keyvault/keyvault-certificates/src/lro/delete/poller.ts @@ -1,38 +1,32 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { delay, RequestOptionsBase } from "@azure/core-http"; -import { Poller } from "@azure/core-lro"; +import { DeleteCertificatePollOperation, DeleteCertificatePollOperationState } from "./operation"; +import { DeletedCertificate } from "../../certificatesModels"; import { - DeleteCertificatePollOperationState, - makeDeleteCertificatePollOperation -} from "./operation"; -import { DeletedCertificate, CertificateClientInterface } from "../../certificatesModels"; - -export interface DeleteCertificatePollerOptions { - client: CertificateClientInterface; - certificateName: string; - requestOptions?: RequestOptionsBase; - intervalInMs?: number; - resumeFrom?: string; -} + KeyVaultCertificatePoller, + KeyVaultCertificatePollerOptions +} from "../keyVaultCertificatePoller"; + +export interface DeleteCertificatePollerOptions extends KeyVaultCertificatePollerOptions {} /** * Class that deletes a poller that waits until a certificate finishes being deleted * @internal */ -export class DeleteCertificatePoller extends Poller< +export class DeleteCertificatePoller extends KeyVaultCertificatePoller< DeleteCertificatePollOperationState, DeletedCertificate > { - /** - * Defines how much time the poller is going to wait before making a new request to the service. - * @memberof DeleteCertificatePoller - */ - public intervalInMs: number; - constructor(options: DeleteCertificatePollerOptions) { - const { client, certificateName, requestOptions, intervalInMs = 2000, resumeFrom } = options; + const { + vaultUrl, + client, + certificateName, + requestOptions, + intervalInMs = 2000, + resumeFrom + } = options; let state: DeleteCertificatePollOperationState | undefined; @@ -40,23 +34,18 @@ export class DeleteCertificatePoller extends Poller< state = JSON.parse(resumeFrom).state; } - const operation = makeDeleteCertificatePollOperation({ - ...state, - certificateName, - requestOptions, - client - }); + const operation = new DeleteCertificatePollOperation( + { + ...state, + certificateName + }, + vaultUrl, + client, + requestOptions + ); super(operation); this.intervalInMs = intervalInMs; } - - /** - * The method used by the poller to wait before attempting to update its operation. - * @memberof DeleteCertificatePoller - */ - async delay(): Promise { - return delay(this.intervalInMs); - } } diff --git a/sdk/keyvault/keyvault-certificates/src/lro/keyVaultCertificatePoller.ts b/sdk/keyvault/keyvault-certificates/src/lro/keyVaultCertificatePoller.ts new file mode 100644 index 000000000000..fa4136c472e1 --- /dev/null +++ b/sdk/keyvault/keyvault-certificates/src/lro/keyVaultCertificatePoller.ts @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { delay, RequestOptionsBase } from "@azure/core-http"; +import { Poller, PollOperation, PollOperationState } from "@azure/core-lro"; +import { KeyVaultClient } from "../generated/keyVaultClient"; + +/** + * Common parameters to a Key Vault Certificate Poller. + */ +export interface KeyVaultCertificatePollerOptions { + certificateName: string; + vaultUrl: string; + client: KeyVaultClient; + requestOptions?: RequestOptionsBase; + intervalInMs?: number; + resumeFrom?: string; +} + +/** + * An interface representing the public shape of the state of a Key Vault Certificate Poller's operations. + */ +export interface KeyVaultCertificatePollOperationState + extends PollOperationState { + /** + * The name of the certificate. + */ + certificateName: string; +} + +/** + * Generates a version of the state with only public properties. At least those common for all of the Key Vault Certificates pollers. + */ +export function cleanState, TResult>( + state: TState +): KeyVaultCertificatePollOperationState { + return { + certificateName: state.certificateName, + isStarted: state.isStarted, + isCancelled: state.isCancelled, + isCompleted: state.isCompleted, + error: state.error, + result: state.result + }; +} + +/** + * Common properties and methods of the Key Vault Certificate Pollers. + */ +export abstract class KeyVaultCertificatePoller< + TState extends KeyVaultCertificatePollOperationState, + TResult +> extends Poller { + /** + * Defines how much time the poller is going to wait before making a new request to the service. + */ + public intervalInMs: number = 2000; + + /** + * The method used by the poller to wait before attempting to update its operation. + */ + async delay(): Promise { + return delay(this.intervalInMs); + } + + /** + * Gets the public state of the polling operation + */ + public getOperationState(): TState { + return cleanState(this.operation.state) as TState; + } +} + +/** + * Optional parameters to the KeyVaultCertificatePollOperation + */ +export interface KeyVaultCertificatePollOperationOptions { + cancelMessage?: string; +} + +/** + * Common properties and methods of the Key Vault Certificate Poller operations. + */ +export class KeyVaultCertificatePollOperation< + TState extends KeyVaultCertificatePollOperationState, + TResult +> implements PollOperation { + private cancelMessage: string = ""; + + constructor(public state: TState, options: KeyVaultCertificatePollOperationOptions = {}) { + if (options.cancelMessage) { + this.cancelMessage = options.cancelMessage; + } + } + + /** + * Meant to reach to the service and update the Poller operation. + */ + public async update(): Promise> { + throw new Error("Operation not supported."); + } + + /** + * Meant to reach to the service and cancel the Poller operation. + */ + public async cancel(): Promise> { + throw new Error(this.cancelMessage); + } + + /** + * Serializes the create certificate's poll operation + */ + public toString(): string { + return JSON.stringify({ + state: cleanState(this.state) + }); + } +} diff --git a/sdk/keyvault/keyvault-certificates/src/lro/operation/operation.ts b/sdk/keyvault/keyvault-certificates/src/lro/operation/operation.ts index 21a5fdaa177c..f6375495f2bd 100644 --- a/sdk/keyvault/keyvault-certificates/src/lro/operation/operation.ts +++ b/sdk/keyvault/keyvault-certificates/src/lro/operation/operation.ts @@ -2,23 +2,36 @@ // Licensed under the MIT license. import { AbortSignalLike, AbortSignal } from "@azure/abort-controller"; -import { PollOperationState, PollOperation } from "@azure/core-lro"; -import { RequestOptionsBase } from "@azure/core-http"; +import { operationOptionsToRequestOptionsBase, RequestOptionsBase } from "@azure/core-http"; import { + CancelCertificateOperationOptions, CertificateOperation, - CertificateClientInterface, + GetCertificateOptions, + GetPlainCertificateOperationOptions, KeyVaultCertificateWithPolicy } from "../../certificatesModels"; +import { + cleanState, + KeyVaultCertificatePollOperation, + KeyVaultCertificatePollOperationState +} from "../keyVaultCertificatePoller"; +import { KeyVaultClient } from "../../generated/keyVaultClient"; +import { + getCertificateOperationFromCoreOperation, + getCertificateWithPolicyFromCertificateBundle +} from "../../transformations"; +import { setParentSpan, createSpan } from "../../../../keyvault-common"; +import { + GetCertificateOperationResponse, + GetCertificateResponse, + UpdateCertificateOperationResponse +} from "../../generated/models"; /** * An interface representing the publicly available properties of the state of the CertificateOperationPoller. */ export interface CertificateOperationState - extends PollOperationState { - /** - * The name of the certificate. - */ - certificateName: string; + extends KeyVaultCertificatePollOperationState { /** * The operation of the certificate */ @@ -26,137 +39,179 @@ export interface CertificateOperationState } /** - * An interface representing the state of a create certificate's poll operation - * @internal + * An interface representing the active operation of a certificate's creation, + * which is represented locally as the "operation" of an active LRO Poller. */ -export interface CertificateOperationPollOperationState - extends PollOperationState { - /** - * The name of the certificate. - */ - certificateName: string; +export class CertificateOperationPollOperation extends KeyVaultCertificatePollOperation< + CertificateOperationState, + KeyVaultCertificateWithPolicy +> { + constructor( + public state: CertificateOperationState, + private vaultUrl: string, + private client: KeyVaultClient, + private requestOptions: RequestOptionsBase = {} + ) { + super(state); + } + /** - * The operation of the certificate + * Cancels a certificate creation operation that is already in progress. This operation requires the certificates/update permission. */ - certificateOperation?: CertificateOperation; + private async cancelCertificateOperation( + certificateName: string, + options: CancelCertificateOperationOptions = {} + ): Promise { + const requestOptions = operationOptionsToRequestOptionsBase(options); + const span = createSpan("generatedClient.cancelCertificateOperation", requestOptions); + + let result: UpdateCertificateOperationResponse; + try { + result = await this.client.updateCertificateOperation( + this.vaultUrl, + certificateName, + true, + setParentSpan(span, requestOptions) + ); + } finally { + span.end(); + } + + return getCertificateOperationFromCoreOperation( + certificateName, + this.vaultUrl, + result._response.parsedBody + ); + } + /** - * Options for the core-http requests. + * Gets the latest information available from a specific certificate, including the certificate's policy. This operation requires the certificates/get permission. */ - requestOptions?: RequestOptionsBase; + private async getCertificate( + certificateName: string, + options: GetCertificateOptions = {} + ): Promise { + const requestOptions = operationOptionsToRequestOptionsBase(options); + const span = createSpan("generatedClient.getCertificate", requestOptions); + + let result: GetCertificateResponse; + + try { + result = await this.client.getCertificate( + this.vaultUrl, + certificateName, + "", + setParentSpan(span, requestOptions) + ); + } finally { + span.end(); + } + + return getCertificateWithPolicyFromCertificateBundle(result); + } + /** - * An interface representing a CertificateClient. For internal use. + * Gets the certificate operation. */ - client?: CertificateClientInterface; -} + private async getPlainCertificateOperation( + certificateName: string, + options?: GetPlainCertificateOperationOptions + ): Promise { + const span = createSpan("generatedClient.getPlainCertificateOperation", options); -/** - * An interface representing a create certificate's poll operation - * @internal - */ -export type CertificateOperationPollOperation = PollOperation< - CertificateOperationPollOperationState, - KeyVaultCertificateWithPolicy ->; + let result: GetCertificateOperationResponse; -/** - * @summary Reaches to the service and updates the create certificate's poll operation. - * @param [options] The optional parameters, which are an abortSignal from @azure/abort-controller and a function that triggers the poller's onProgress function. - * @internal - */ -async function update( - this: CertificateOperationPollOperation, - options: { - abortSignal?: AbortSignalLike; - fireProgress?: (state: CertificateOperationPollOperationState) => void; - } = {} -): Promise { - const state = this.state; - const client = state.client!; - const certificateName = state.certificateName!; - - const requestOptions = state.requestOptions || {}; - if (options.abortSignal) { - requestOptions.abortSignal = options.abortSignal; - } + try { + result = await this.client.getCertificateOperation( + this.vaultUrl, + certificateName, + setParentSpan(span, options) + ); + } finally { + span.end(); + } - if (!state.isStarted) { - state.isStarted = true; - state.result = await client.getCertificate(certificateName, requestOptions); - state.certificateOperation = await client.getPlainCertificateOperation( - certificateName, - requestOptions - ); - } else if (!state.isCompleted) { - state.certificateOperation = await client.getPlainCertificateOperation( + return getCertificateOperationFromCoreOperation( certificateName, - requestOptions + this.vaultUrl, + result._response.parsedBody ); } - if (state.certificateOperation && state.certificateOperation.status !== "inProgress") { - state.isCompleted = true; - state.result = await client.getCertificate(certificateName, requestOptions); - if (state.certificateOperation.error) { - state.error = new Error(state.certificateOperation.error.message); + /** + * Reaches to the service and updates the poll operation. + */ + async update( + options: { + abortSignal?: AbortSignalLike; + fireProgress?: (state: CertificateOperationState) => void; + } = {} + ): Promise { + const state = this.state; + const certificateName = state.certificateName!; + + if (options.abortSignal) { + this.requestOptions.abortSignal = options.abortSignal; } - } - return makeCertificateOperationPollOperation(state); -} + if (!state.isStarted) { + state.isStarted = true; + state.result = await this.getCertificate(certificateName, this.requestOptions); + state.certificateOperation = await this.getPlainCertificateOperation( + certificateName, + this.requestOptions + ); + } else if (!state.isCompleted) { + state.certificateOperation = await this.getPlainCertificateOperation( + certificateName, + this.requestOptions + ); + } -/** - * @summary Reaches to the service and cancels the certificate's operation, also updating the certificate's poll operation - * @param [options] The optional parameters, which is only an abortSignal from @azure/abort-controller - * @internal - */ -async function cancel( - this: CertificateOperationPollOperation, - options: { abortSignal?: AbortSignal } = {} -): Promise { - const state = this.state; - const client = state.client!; - const certificateName = state.certificateName!; - - const requestOptions = state.requestOptions || {}; - if (options.abortSignal) { - requestOptions.abortSignal = options.abortSignal; + if (state.certificateOperation && state.certificateOperation.status !== "inProgress") { + state.isCompleted = true; + state.result = await this.getCertificate(certificateName, this.requestOptions); + if (state.certificateOperation.error) { + state.error = new Error(state.certificateOperation.error.message); + } + } + + return this; } - state.certificateOperation = await client.cancelCertificateOperation( - certificateName, - requestOptions - ); + /** + * Reaches to the service and cancels the certificate's operation, also updating the poll operation. + */ + async cancel( + this: CertificateOperationPollOperation, + options: { abortSignal?: AbortSignal } = {} + ): Promise { + const state = this.state; + const certificateName = state.certificateName!; + + if (options.abortSignal) { + this.requestOptions.abortSignal = options.abortSignal; + } - return makeCertificateOperationPollOperation({ - ...this.state, - isCancelled: true - }); -} + state.certificateOperation = await this.cancelCertificateOperation( + certificateName, + this.requestOptions + ); -/** - * @summary Serializes the create certificate's poll operation - * @internal - */ -function toString(this: CertificateOperationPollOperation): string { - return JSON.stringify({ - state: this.state - }); -} + this.state.isCancelled = true; + return this; + } -/** - * @summary Builds a create certificate's poll operation - * @param [state] A poll operation's state, in case the new one is intended to follow up where the previous one was left. - * @internal - */ -export function makeCertificateOperationPollOperation( - state: CertificateOperationPollOperationState -): CertificateOperationPollOperation { - return { - state: { - ...state - }, - update, - cancel, - toString - }; + /** + * Serializes the certificate's poll operation + */ + public toString(): string { + const state: CertificateOperationState = { + certificateOperation: this.state.certificateOperation, + ...cleanState(this.state) + }; + return JSON.stringify({ + state + }); + } } diff --git a/sdk/keyvault/keyvault-certificates/src/lro/operation/poller.ts b/sdk/keyvault/keyvault-certificates/src/lro/operation/poller.ts index e9d695427ac2..4b838df8466f 100644 --- a/sdk/keyvault/keyvault-certificates/src/lro/operation/poller.ts +++ b/sdk/keyvault/keyvault-certificates/src/lro/operation/poller.ts @@ -1,56 +1,48 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { delay, RequestOptionsBase } from "@azure/core-http"; -import { Poller } from "@azure/core-lro"; +import { CertificateOperationPollOperation, CertificateOperationState } from "./operation"; +import { KeyVaultCertificateWithPolicy } from "../../certificatesModels"; import { - CertificateOperationPollOperationState, - makeCertificateOperationPollOperation, - CertificateOperationState -} from "./operation"; -import { - CertificateOperation, - CertificateClientInterface, - KeyVaultCertificateWithPolicy -} from "../../certificatesModels"; + KeyVaultCertificatePoller, + KeyVaultCertificatePollerOptions, + cleanState +} from "../keyVaultCertificatePoller"; -export interface CertificateOperationPollerOptions { - client: CertificateClientInterface; - certificateName: string; - requestOptions?: RequestOptionsBase; - intervalInMs?: number; - resumeFrom?: string; -} +export interface CertificateOperationPollerOptions extends KeyVaultCertificatePollerOptions {} /** - * Class that deletes a poller that waits until a certificate finishes being deleted - * @internal + * Class that creates a poller that waits until a certificate finishes being created */ -export class CertificateOperationPoller extends Poller< - CertificateOperationPollOperationState, +export class CertificateOperationPoller extends KeyVaultCertificatePoller< + CertificateOperationState, KeyVaultCertificateWithPolicy > { - /** - * Defines how much time the poller is going to wait before making a new request to the service. - * @memberof CertificateOperationPoller - */ - public intervalInMs: number; - constructor(options: CertificateOperationPollerOptions) { - const { client, certificateName, requestOptions, intervalInMs = 2000, resumeFrom } = options; + const { + vaultUrl, + client, + certificateName, + requestOptions, + intervalInMs = 2000, + resumeFrom + } = options; - let state: CertificateOperationPollOperationState | undefined; + let state: CertificateOperationState | undefined; if (resumeFrom) { state = JSON.parse(resumeFrom).state; } - const operation = makeCertificateOperationPollOperation({ - ...state, - certificateName, - requestOptions, - client - }); + const operation = new CertificateOperationPollOperation( + { + ...state, + certificateName + }, + vaultUrl, + client, + requestOptions + ); super(operation); @@ -58,33 +50,12 @@ export class CertificateOperationPoller extends Poller< } /** - * The method used by the poller to wait before attempting to update its operation. - * @memberof CertificateOperationPoller - */ - async delay(): Promise { - return delay(this.intervalInMs); - } - - /** - * Method to get the certificate operation - */ - public getCertificateOperation(): CertificateOperation { - return this.operation.state.certificateOperation!; - } - - /** - * Gets the state of the polling operation + * Gets the public state of the polling operation */ public getOperationState(): CertificateOperationState { - const state: CertificateOperationState = this.operation.state; return { - isStarted: state.isStarted, - isCompleted: state.isCompleted, - isCancelled: state.isCancelled, - error: state.error, - result: state.result, - certificateName: state.certificateName, - certificateOperation: state.certificateOperation + ...cleanState(this.operation.state), + certificateOperation: this.operation.state.certificateOperation }; } } diff --git a/sdk/keyvault/keyvault-certificates/src/lro/recover/operation.ts b/sdk/keyvault/keyvault-certificates/src/lro/recover/operation.ts index e496bfff1a72..f1b38fdba762 100644 --- a/sdk/keyvault/keyvault-certificates/src/lro/recover/operation.ts +++ b/sdk/keyvault/keyvault-certificates/src/lro/recover/operation.ts @@ -2,131 +2,142 @@ // Licensed under the MIT license. import { AbortSignalLike } from "@azure/abort-controller"; -import { PollOperationState, PollOperation } from "@azure/core-lro"; -import { RequestOptionsBase } from "@azure/core-http"; +import { operationOptionsToRequestOptionsBase, RequestOptionsBase } from "@azure/core-http"; +import { createSpan, setParentSpan } from "../../../../keyvault-common"; import { + GetCertificateOptions, KeyVaultCertificateWithPolicy, - CertificateClientInterface + RecoverDeletedCertificateOptions } from "../../certificatesModels"; +import { KeyVaultClient } from "../../generated/keyVaultClient"; +import { GetCertificateResponse, RecoverDeletedCertificateResponse } from "../../generated/models"; +import { getCertificateWithPolicyFromCertificateBundle } from "../../transformations"; +import { + KeyVaultCertificatePollOperation, + KeyVaultCertificatePollOperationState +} from "../keyVaultCertificatePoller"; /** - * The public representation of the RecoverDeletedCertificatePoller operation state. - */ -export type RecoverDeletedCertificateState = PollOperationState; - -/** - * An interface representing the state of a delete certificate's poll operation - * @internal - */ -export interface RecoverDeletedCertificatePollOperationState - extends PollOperationState { - /** - * The name of the certificate. - */ - certificateName: string; - /** - * Options for the core-http requests. - */ - requestOptions?: RequestOptionsBase; - /** - * An interface representing a CertificateClient. For internal use. - */ - client: CertificateClientInterface; -} - -/** - * An interface representing a delete certificate's poll operation - * @internal + * Deprecated: Public representation of the recovery of a deleted certificate poll operation */ -export type RecoverDeletedCertificatePollOperation = PollOperation< - RecoverDeletedCertificatePollOperationState, +export type RecoverDeletedCertificateState = KeyVaultCertificatePollOperationState< KeyVaultCertificateWithPolicy >; /** - * @summary Reaches to the service and updates the delete certificate's poll operation. - * @param [options] The optional parameters, which are an abortSignal from @azure/abort-controller and a function that triggers the poller's onProgress function. - * @internal + * An interface representing the recovery of a deleted certificate's poll operation */ -async function update( - this: RecoverDeletedCertificatePollOperation, - options: { - abortSignal?: AbortSignalLike; - fireProgress?: (state: RecoverDeletedCertificatePollOperationState) => void; - } = {} -): Promise { - const state = this.state; - const { certificateName, client } = state; - - const requestOptions = state.requestOptions || {}; - if (options.abortSignal) { - requestOptions.abortSignal = options.abortSignal; +export class RecoverDeletedCertificatePollOperation extends KeyVaultCertificatePollOperation< + RecoverDeletedCertificateState, + KeyVaultCertificateWithPolicy +> { + constructor( + public state: RecoverDeletedCertificateState, + private vaultUrl: string, + private client: KeyVaultClient, + private requestOptions: RequestOptionsBase = {} + ) { + super(state, { + cancelMessage: "Canceling the recovery of a deleted certificate is not supported." + }); } - if (!state.isStarted) { + /** + * Gets the latest information available from a specific certificate, including the certificate's policy. This operation requires the certificates/get permission. + */ + private async getCertificate( + certificateName: string, + options: GetCertificateOptions = {} + ): Promise { + const requestOptions = operationOptionsToRequestOptionsBase(options); + const span = createSpan("generatedClient.getCertificate", requestOptions); + + let result: GetCertificateResponse; + try { - state.result = await client.getCertificate(certificateName, { requestOptions }); - state.isCompleted = true; - } catch (e) { - // getCertificate will only work once the LRO is completed. - } - if (!state.isCompleted) { - state.result = await client.recoverDeletedCertificate(certificateName, { requestOptions }); - state.isStarted = true; + result = await this.client.getCertificate( + this.vaultUrl, + certificateName, + "", + setParentSpan(span, requestOptions) + ); + } finally { + span.end(); } + + return getCertificateWithPolicyFromCertificateBundle(result); } - if (!state.isCompleted) { + /** + * Recovers the deleted certificate in the specified vault. This operation can only be performed on a soft-delete enabled vault. This operation + * requires the certificate/recover permission. + */ + private async recoverDeletedCertificate( + certificateName: string, + options: RecoverDeletedCertificateOptions = {} + ): Promise { + const requestOptions = operationOptionsToRequestOptionsBase(options); + const span = createSpan("generatedClient.recoverDeletedCertificate", requestOptions); + + let result: RecoverDeletedCertificateResponse; + try { - state.result = await client.getCertificate(certificateName, { requestOptions }); - state.isCompleted = true; - } catch (error) { - if (error.statusCode === 403) { - // At this point, the resource exists but the user doesn't have access to it. - state.isCompleted = true; - } else if (error.statusCode !== 404) { - state.error = error; - state.isCompleted = true; - } + result = await this.client.recoverDeletedCertificate( + this.vaultUrl, + certificateName, + setParentSpan(span, requestOptions) + ); + } finally { + span.end(); } + + return getCertificateWithPolicyFromCertificateBundle(result._response.parsedBody); } - return makeRecoverDeletedCertificatePollOperation(state); -} + /** + * Reaches to the service and updates the poll operation. + */ + async update( + options: { + abortSignal?: AbortSignalLike; + fireProgress?: (state: RecoverDeletedCertificateState) => void; + } = {} + ): Promise { + const state = this.state; + const { certificateName } = state; -/** - * @summary Reaches to the service and cancels the certificate's operation, also updating the certificate's poll operation - * @param [options] The optional parameters, which is only an abortSignal from @azure/abort-controller - * @internal - */ -async function cancel(): Promise { - throw new Error("Canceling the deletion of a certificate is not supported."); -} + if (options.abortSignal) { + this.requestOptions.abortSignal = options.abortSignal; + } -/** - * @summary Serializes the create certificate's poll operation - * @internal - */ -function toString(this: RecoverDeletedCertificatePollOperation): string { - return JSON.stringify({ - state: this.state - }); -} + if (!state.isStarted) { + try { + state.result = await this.getCertificate(certificateName, this.requestOptions); + state.isCompleted = true; + } catch (e) { + // getCertificate will only work once the LRO is completed. + } + if (!state.isCompleted) { + state.result = await this.recoverDeletedCertificate(certificateName, this.requestOptions); + state.isStarted = true; + } + } -/** - * @summary Builds a create certificate's poll operation - * @param [state] A poll operation's state, in case the new one is intended to follow up where the previous one was left. - * @internal - */ -export function makeRecoverDeletedCertificatePollOperation( - state: RecoverDeletedCertificatePollOperationState -): RecoverDeletedCertificatePollOperation { - return { - state: { - ...state - }, - update, - cancel, - toString - }; + if (!state.isCompleted) { + try { + state.result = await this.getCertificate(certificateName, this.requestOptions); + state.isCompleted = true; + } catch (error) { + if (error.statusCode === 403) { + // At this point, the resource exists but the user doesn't have access to it. + state.isCompleted = true; + } else if (error.statusCode !== 404) { + state.error = error; + state.isCompleted = true; + } + } + } + + return this; + } } diff --git a/sdk/keyvault/keyvault-certificates/src/lro/recover/poller.ts b/sdk/keyvault/keyvault-certificates/src/lro/recover/poller.ts index 3df6f45fc6d5..3375ddbdacc5 100644 --- a/sdk/keyvault/keyvault-certificates/src/lro/recover/poller.ts +++ b/sdk/keyvault/keyvault-certificates/src/lro/recover/poller.ts @@ -1,65 +1,53 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { delay, RequestOptionsBase } from "@azure/core-http"; -import { Poller } from "@azure/core-lro"; import { - RecoverDeletedCertificatePollOperationState, - makeRecoverDeletedCertificatePollOperation + RecoverDeletedCertificatePollOperation, + RecoverDeletedCertificateState } from "./operation"; +import { KeyVaultCertificateWithPolicy } from "../../certificatesModels"; import { - KeyVaultCertificateWithPolicy, - CertificateClientInterface -} from "../../certificatesModels"; + KeyVaultCertificatePoller, + KeyVaultCertificatePollerOptions +} from "../keyVaultCertificatePoller"; -export interface RecoverDeletedCertificatePollerOptions { - client: CertificateClientInterface; - certificateName: string; - requestOptions?: RequestOptionsBase; - intervalInMs?: number; - resumeFrom?: string; -} +export interface RecoverDeletedCertificatePollerOptions extends KeyVaultCertificatePollerOptions {} /** - * Class that deletes a poller that waits until a certificate finishes being deleted - * @internal + * Class that creates a poller that waits until a deleted certificate is fully recovered. */ -export class RecoverDeletedCertificatePoller extends Poller< - RecoverDeletedCertificatePollOperationState, +export class RecoverDeletedCertificatePoller extends KeyVaultCertificatePoller< + RecoverDeletedCertificateState, KeyVaultCertificateWithPolicy > { - /** - * Defines how much time the poller is going to wait before making a new request to the service. - * @memberof RecoverDeletedCertificatePoller - */ - public intervalInMs: number; - constructor(options: RecoverDeletedCertificatePollerOptions) { - const { client, certificateName, requestOptions, intervalInMs = 2000, resumeFrom } = options; + const { + vaultUrl, + client, + certificateName, + requestOptions, + intervalInMs = 2000, + resumeFrom + } = options; - let state: RecoverDeletedCertificatePollOperationState | undefined; + let state: RecoverDeletedCertificateState | undefined; if (resumeFrom) { state = JSON.parse(resumeFrom).state; } - const operation = makeRecoverDeletedCertificatePollOperation({ - ...state, - certificateName, - requestOptions, - client - }); + const operation = new RecoverDeletedCertificatePollOperation( + { + ...state, + certificateName + }, + vaultUrl, + client, + requestOptions + ); super(operation); this.intervalInMs = intervalInMs; } - - /** - * The method used by the poller to wait before attempting to update its operation. - * @memberof RecoverDeletedCertificatePoller - */ - async delay(): Promise { - return delay(this.intervalInMs); - } } diff --git a/sdk/keyvault/keyvault-certificates/src/transformations.ts b/sdk/keyvault/keyvault-certificates/src/transformations.ts new file mode 100644 index 000000000000..98f1edf996fa --- /dev/null +++ b/sdk/keyvault/keyvault-certificates/src/transformations.ts @@ -0,0 +1,364 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { + ArrayOneOrMore, + CertificateContentType, + CertificateOperation, + CertificateIssuer, + CertificateKeyType, + CertificatePolicy, + CertificateProperties, + DeletedCertificate, + KeyVaultCertificate, + KeyVaultCertificateWithPolicy, + SubjectAlternativeNames, + CertificateContact +} from "./certificatesModels"; +import { + CertificateAttributes, + CertificateBundle, + CertificatePolicy as CoreCertificatePolicy, + DeletedCertificateBundle, + DeletedCertificateItem, + IssuerAttributes, + IssuerBundle, + SubjectAlternativeNames as CoreSubjectAlternativeNames, + X509CertificateProperties, + CertificateOperation as CoreCertificateOperation, + Contacts as CoreContacts +} from "./generated/models"; +import { parseKeyVaultCertificateId } from "./identifier"; + +export function toCoreAttributes(properties: CertificateProperties): CertificateAttributes { + return { + recoveryLevel: properties.recoveryLevel, + enabled: properties.enabled, + notBefore: properties.notBefore, + expires: properties.expiresOn, + created: properties.createdOn, + updated: properties.updatedOn + }; +} + +export function toCorePolicy( + id: string | undefined, + policy: CertificatePolicy, + attributes: CertificateAttributes = {} +): CoreCertificatePolicy { + let subjectAlternativeNames: CoreSubjectAlternativeNames = {}; + if (policy.subjectAlternativeNames) { + subjectAlternativeNames = { + emails: policy.subjectAlternativeNames.emails, + dnsNames: policy.subjectAlternativeNames.dnsNames, + upns: policy.subjectAlternativeNames.userPrincipalNames + }; + } + + return { + id, + lifetimeActions: policy.lifetimeActions + ? policy.lifetimeActions.map((action) => ({ + action: { actionType: action.action }, + trigger: { + lifetimePercentage: action.lifetimePercentage, + daysBeforeExpiry: action.daysBeforeExpiry + } + })) + : undefined, + keyProperties: { + keyType: policy.keyType, + keySize: policy.keySize, + reuseKey: policy.reuseKey, + curve: policy.keyCurveName, + exportable: policy.exportable + }, + secretProperties: { + contentType: policy.contentType + }, + x509CertificateProperties: { + subject: policy.subject, + ekus: policy.enhancedKeyUsage, + subjectAlternativeNames, + keyUsage: policy.keyUsage, + validityInMonths: policy.validityInMonths + }, + issuerParameters: { + name: policy.issuerName, + certificateType: policy.certificateType, + certificateTransparency: policy.certificateTransparency + }, + attributes + }; +} + +export function toPublicPolicy(policy: CoreCertificatePolicy = {}): CertificatePolicy { + let subjectAlternativeNames: SubjectAlternativeNames | undefined; + const x509Properties: X509CertificateProperties = policy.x509CertificateProperties || {}; + + if (policy.x509CertificateProperties) { + if (x509Properties.subjectAlternativeNames) { + const names = x509Properties.subjectAlternativeNames; + if (names.emails && names.emails.length) { + subjectAlternativeNames = { + ...subjectAlternativeNames, + emails: names.emails as ArrayOneOrMore + }; + } + if (names.dnsNames && names.dnsNames.length) { + subjectAlternativeNames = { + ...subjectAlternativeNames, + dnsNames: names.dnsNames as ArrayOneOrMore + }; + } + if (names.upns && names.upns.length) { + subjectAlternativeNames = { + ...subjectAlternativeNames, + userPrincipalNames: names.upns as ArrayOneOrMore + }; + } + } + } + + const certificatePolicy: CertificatePolicy = { + lifetimeActions: policy.lifetimeActions + ? policy.lifetimeActions.map((action) => ({ + action: action.action ? action.action.actionType : undefined, + daysBeforeExpiry: action.trigger ? action.trigger.daysBeforeExpiry : undefined, + lifetimePercentage: action.trigger ? action.trigger.lifetimePercentage : undefined + })) + : undefined, + contentType: policy.secretProperties + ? (policy.secretProperties.contentType as CertificateContentType) + : undefined, + enhancedKeyUsage: x509Properties.ekus, + keyUsage: x509Properties.keyUsage, + validityInMonths: x509Properties.validityInMonths, + subject: x509Properties.subject, + subjectAlternativeNames: subjectAlternativeNames! + }; + + if (policy.attributes) { + certificatePolicy.enabled = policy.attributes.enabled; + } + + if (policy.keyProperties) { + certificatePolicy.keyType = policy.keyProperties.keyType as CertificateKeyType; + certificatePolicy.keySize = policy.keyProperties.keySize; + certificatePolicy.reuseKey = policy.keyProperties.reuseKey; + certificatePolicy.keyCurveName = policy.keyProperties.curve; + certificatePolicy.exportable = policy.keyProperties.exportable; + } + + if (policy.issuerParameters) { + certificatePolicy.issuerName = policy.issuerParameters && policy.issuerParameters.name; + certificatePolicy.certificateType = policy.issuerParameters + .certificateType as CertificateContentType; + certificatePolicy.certificateTransparency = policy.issuerParameters.certificateTransparency; + } + + return certificatePolicy; +} + +export function toPublicIssuer(issuer: IssuerBundle = {}): CertificateIssuer { + const parsedId = parseKeyVaultCertificateId(issuer.id!); + const attributes: IssuerAttributes = issuer.attributes || {}; + + const publicIssuer: CertificateIssuer = { + id: issuer.id, + name: parsedId.name, + provider: issuer.provider, + accountId: issuer.credentials && issuer.credentials.accountId, + password: issuer.credentials && issuer.credentials.password, + enabled: attributes.enabled, + createdOn: attributes.created, + updatedOn: attributes.updated + }; + + if (issuer.organizationDetails) { + publicIssuer.organizationId = issuer.organizationDetails.id; + publicIssuer.administratorContacts = issuer.organizationDetails.adminDetails + ? issuer.organizationDetails.adminDetails.map((x) => ({ + email: x.emailAddress, + phone: x.phone, + firstName: x.firstName, + lastName: x.lastName + })) + : undefined; + } + return publicIssuer; +} + +export function getCertificateFromCertificateBundle( + certificateBundle: CertificateBundle +): KeyVaultCertificate { + const parsedId = parseKeyVaultCertificateId(certificateBundle.id!); + + const attributes: CertificateAttributes = certificateBundle.attributes || {}; + + const abstractProperties: CertificateProperties = { + createdOn: attributes.created, + updatedOn: attributes.updated, + expiresOn: attributes.expires, + id: certificateBundle.id, + name: parsedId.name, + enabled: attributes.enabled, + notBefore: attributes.notBefore, + recoveryLevel: attributes.recoveryLevel, + vaultUrl: parsedId.vaultUrl, + version: parsedId.version, + tags: certificateBundle.tags, + x509Thumbprint: certificateBundle.x509Thumbprint, + recoverableDays: attributes.recoverableDays + }; + + return { + keyId: certificateBundle.kid, + secretId: certificateBundle.sid, + name: parsedId.name, + cer: certificateBundle.cer, + properties: abstractProperties + }; +} + +export function getCertificateWithPolicyFromCertificateBundle( + certificateBundle: CertificateBundle +): KeyVaultCertificateWithPolicy { + const parsedId = parseKeyVaultCertificateId(certificateBundle.id!); + + const attributes: CertificateAttributes = certificateBundle.attributes || {}; + const policy = toPublicPolicy(certificateBundle.policy || {}); + + const abstractProperties: CertificateProperties = { + createdOn: attributes.created, + updatedOn: attributes.updated, + expiresOn: attributes.expires, + id: certificateBundle.id, + name: parsedId.name, + enabled: attributes.enabled, + notBefore: attributes.notBefore, + recoveryLevel: attributes.recoveryLevel, + vaultUrl: parsedId.vaultUrl, + version: parsedId.version, + tags: certificateBundle.tags, + x509Thumbprint: certificateBundle.x509Thumbprint, + recoverableDays: attributes.recoverableDays + }; + + return { + keyId: certificateBundle.kid, + secretId: certificateBundle.sid, + name: parsedId.name, + cer: certificateBundle.cer, + policy, + properties: abstractProperties + }; +} + +export function getDeletedCertificateFromDeletedCertificateBundle( + certificateBundle: DeletedCertificateBundle +): DeletedCertificate { + const certificate: KeyVaultCertificateWithPolicy = getCertificateWithPolicyFromCertificateBundle( + certificateBundle + ); + + return { + ...certificate, + recoveryId: certificateBundle.recoveryId, + scheduledPurgeDate: certificateBundle.scheduledPurgeDate, + deletedOn: certificateBundle.deletedDate + }; +} + +export function getDeletedCertificateFromItem(item: DeletedCertificateItem): DeletedCertificate { + const parsedId = parseKeyVaultCertificateId(item.id!); + + const attributes: any = item.attributes || {}; + + const abstractProperties: any = { + createdOn: attributes.created, + updatedOn: attributes.updated, + expiresOn: attributes.expires, + ...parsedId, + ...item, + ...item.attributes + }; + + if (abstractProperties.deletedDate) { + delete abstractProperties.deletedDate; + } + + if (abstractProperties.expires) { + delete abstractProperties.expires; + } + if (abstractProperties.created) { + delete abstractProperties.created; + } + if (abstractProperties.updated) { + delete abstractProperties.updated; + } + + return { + name: parsedId.name, + properties: abstractProperties + }; +} + +export function getCertificateOperationFromCoreOperation( + certificateName: string, + vaultUrl: string, + operation: CoreCertificateOperation +): CertificateOperation { + return { + cancellationRequested: operation.cancellationRequested, + name: certificateName, + issuerName: operation.issuerParameters ? operation.issuerParameters.name : undefined, + certificateTransparency: operation.issuerParameters + ? operation.issuerParameters.certificateTransparency + : undefined, + certificateType: operation.issuerParameters + ? operation.issuerParameters.certificateType + : undefined, + csr: operation.csr, + error: operation.error, + id: operation.id, + requestId: operation.requestId, + status: operation.status, + statusDetails: operation.statusDetails, + target: operation.target, + vaultUrl: vaultUrl + }; +} + +export function coreContactsToCertificateContacts(contacts: CoreContacts): CertificateContact[] { + return contacts.contactList + ? contacts.contactList.map( + (x) => ({ email: x.emailAddress, phone: x.phone, name: x.name } as CertificateContact) + ) + : []; +} + +export function getPropertiesFromCertificateBundle( + certificateBundle: CertificateBundle +): CertificateProperties { + const parsedId = parseKeyVaultCertificateId(certificateBundle.id!); + const attributes: CertificateAttributes = certificateBundle.attributes || {}; + + const abstractProperties: CertificateProperties = { + createdOn: attributes.created, + updatedOn: attributes.updated, + expiresOn: attributes.expires, + id: certificateBundle.id, + name: parsedId.name, + enabled: attributes.enabled, + notBefore: attributes.notBefore, + recoveryLevel: attributes.recoveryLevel, + vaultUrl: parsedId.vaultUrl, + version: parsedId.version, + tags: certificateBundle.tags, + x509Thumbprint: certificateBundle.x509Thumbprint, + recoverableDays: attributes.recoverableDays + }; + + return abstractProperties; +} diff --git a/sdk/keyvault/keyvault-common/src/index.ts b/sdk/keyvault/keyvault-common/src/index.ts index 58bce83f161c..2178350900fb 100644 --- a/sdk/keyvault/keyvault-common/src/index.ts +++ b/sdk/keyvault/keyvault-common/src/index.ts @@ -3,3 +3,4 @@ export * from "./challengeBasedAuthenticationPolicy"; export * from "./parseKeyvaultIdentifier"; +export * from "./tracing"; diff --git a/sdk/keyvault/keyvault-keys/src/tracing.ts b/sdk/keyvault/keyvault-common/src/tracing.ts similarity index 89% rename from sdk/keyvault/keyvault-keys/src/tracing.ts rename to sdk/keyvault/keyvault-common/src/tracing.ts index 6bdad6f24c78..a97d4bd52abd 100644 --- a/sdk/keyvault/keyvault-keys/src/tracing.ts +++ b/sdk/keyvault/keyvault-common/src/tracing.ts @@ -6,11 +6,11 @@ import { getTracer } from "@azure/core-tracing"; import { Span } from "@opentelemetry/api"; /** - * Creates a span using the tracer that was set by the user. + * Creates a span using the tracer that was set by the user * @param {string} methodName The name of the method creating the span. * @param {RequestOptionsBase} [options] The options for the underlying HTTP request. */ -export function createSpan(methodName: string, requestOptions?: RequestOptionsBase): Span { +export function createSpan(methodName: string, requestOptions: RequestOptionsBase = {}): Span { const tracer = getTracer(); const span = tracer.startSpan(methodName, requestOptions && requestOptions.spanOptions); span.setAttribute("az.namespace", "Microsoft.KeyVault"); diff --git a/sdk/keyvault/keyvault-keys/CHANGELOG.md b/sdk/keyvault/keyvault-keys/CHANGELOG.md index 54f2f5b1947d..84b87e93866e 100644 --- a/sdk/keyvault/keyvault-keys/CHANGELOG.md +++ b/sdk/keyvault/keyvault-keys/CHANGELOG.md @@ -3,6 +3,7 @@ ## 4.2.0-beta.3 (Unreleased) +- Updated the Key Vault Keys Long Running Operation Pollers to follow a more compact and meaningful approach moving forward. ## 4.2.0-beta.2 (2020-10-06) diff --git a/sdk/keyvault/keyvault-keys/src/index.ts b/sdk/keyvault/keyvault-keys/src/index.ts index 59f0d6a082fa..470e842f399b 100644 --- a/sdk/keyvault/keyvault-keys/src/index.ts +++ b/sdk/keyvault/keyvault-keys/src/index.ts @@ -32,7 +32,11 @@ import { } from "./generated/models"; import { KeyVaultClient } from "./generated/keyVaultClient"; import { SDK_VERSION } from "./constants"; -import { challengeBasedAuthenticationPolicy } from "../../keyvault-common/src"; +import { + challengeBasedAuthenticationPolicy, + createSpan, + setParentSpan +} from "../../keyvault-common/src"; import { DeleteKeyPoller } from "./lro/delete/poller"; import { RecoverDeletedKeyPoller } from "./lro/recover/poller"; @@ -92,7 +96,6 @@ import { import { parseKeyVaultKeyId, KeyVaultKeyId } from "./identifier"; import { LocalSupportedAlgorithmName } from "./localCryptography/models"; -import { createSpan, setParentSpan } from "./tracing"; import { getKeyFromKeyBundle } from "./transformations"; export { diff --git a/sdk/keyvault/keyvault-keys/src/lro/delete/operation.ts b/sdk/keyvault/keyvault-keys/src/lro/delete/operation.ts index d227edf2c42a..aed263ffda0a 100644 --- a/sdk/keyvault/keyvault-keys/src/lro/delete/operation.ts +++ b/sdk/keyvault/keyvault-keys/src/lro/delete/operation.ts @@ -6,19 +6,19 @@ import { operationOptionsToRequestOptionsBase, RequestOptionsBase } from "@azure import { KeyVaultClient } from "../../generated/keyVaultClient"; import { DeleteKeyResponse, GetDeletedKeyResponse } from "../../generated/models"; import { DeletedKey, DeleteKeyOptions, GetDeletedKeyOptions } from "../../keysModels"; -import { createSpan, setParentSpan } from "../../tracing"; +import { createSpan, setParentSpan } from "../../../../keyvault-common/src"; import { getKeyFromKeyBundle } from "../../transformations"; import { KeyVaultKeyPollOperation, KeyVaultKeyPollOperationState } from "../keyVaultKeyPoller"; /** * An interface representing the state of a delete key's poll operation */ -export interface DeleteKeyPollOperationState extends KeyVaultKeyPollOperationState {} +export interface DeleteKeyPollOperationState extends KeyVaultKeyPollOperationState { } export class DeleteKeyPollOperation extends KeyVaultKeyPollOperation< DeleteKeyPollOperationState, DeletedKey -> { + > { constructor( public state: DeleteKeyPollOperationState, private vaultUrl: string, @@ -31,12 +31,10 @@ export class DeleteKeyPollOperation extends KeyVaultKeyPollOperation< /** * Sends a delete request for the given Key Vault Key's name to the Key Vault service. * Since the Key Vault Key won't be immediately deleted, we have {@link beginDeleteKey}. - * @param {string} name The name of the Key Vault Key. - * @param {DeleteKeyOptions} [options] Optional parameters for the underlying HTTP request. */ private async deleteKey(name: string, options: DeleteKeyOptions = {}): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = createSpan("deleteKey", requestOptions); + const span = createSpan("generatedClient.deleteKey", requestOptions); let response: DeleteKeyResponse; try { @@ -55,16 +53,13 @@ export class DeleteKeyPollOperation extends KeyVaultKeyPollOperation< /** * The getDeletedKey method returns the specified deleted key along with its properties. * This operation requires the keys/get permission. - * @summary Gets the specified deleted key. - * @param {string} name The name of the key. - * @param {GetDeletedKeyOptions} [options] The optional parameters. */ private async getDeletedKey( name: string, options: GetDeletedKeyOptions = {} ): Promise { const responseOptions = operationOptionsToRequestOptionsBase(options); - const span = createSpan("getDeletedKey", responseOptions); + const span = createSpan("generatedClient.getDeletedKey", responseOptions); let response: GetDeletedKeyResponse; try { @@ -81,8 +76,7 @@ export class DeleteKeyPollOperation extends KeyVaultKeyPollOperation< } /** - * @summary Reaches to the service and updates the delete key's poll operation. - * @param [options] The optional parameters, which are an abortSignal from @azure/abort-controller and a function that triggers the poller's onProgress function. + * Reaches to the service and updates the delete key's poll operation. */ public async update( options: { diff --git a/sdk/keyvault/keyvault-keys/src/lro/delete/poller.ts b/sdk/keyvault/keyvault-keys/src/lro/delete/poller.ts index d8db57f4b67f..9fa7863e0978 100644 --- a/sdk/keyvault/keyvault-keys/src/lro/delete/poller.ts +++ b/sdk/keyvault/keyvault-keys/src/lro/delete/poller.ts @@ -6,7 +6,7 @@ import { DeletedKey } from "../../keysModels"; import { KeyVaultKeyPoller, KeyVaultKeyPollerOptions } from "../keyVaultKeyPoller"; /** - * Class that deletes a poller that waits until a key finishes being deleted + * Class that creates a poller that waits until a key finishes being deleted. */ export class DeleteKeyPoller extends KeyVaultKeyPoller { constructor(options: KeyVaultKeyPollerOptions) { diff --git a/sdk/keyvault/keyvault-keys/src/lro/keyVaultKeyPoller.ts b/sdk/keyvault/keyvault-keys/src/lro/keyVaultKeyPoller.ts index 4ca0b82d7389..c3ec485b8ca4 100644 --- a/sdk/keyvault/keyvault-keys/src/lro/keyVaultKeyPoller.ts +++ b/sdk/keyvault/keyvault-keys/src/lro/keyVaultKeyPoller.ts @@ -30,16 +30,14 @@ export interface KeyVaultKeyPollOperationState extends PollOperationSta /** * Common properties and methods of the Key Vault Key Pollers. */ -export abstract class KeyVaultKeyPoller extends Poller { +export abstract class KeyVaultKeyPoller, TResult> extends Poller { /** * Defines how much time the poller is going to wait before making a new request to the service. - * @memberof DeleteKeyPoller */ public intervalInMs: number = 2000; /** * The method used by the poller to wait before attempting to update its operation. - * @memberof DeleteKeyPoller */ async delay(): Promise { return delay(this.intervalInMs); @@ -50,37 +48,37 @@ export abstract class KeyVaultKeyPoller extends Poller implements PollOperation { - private cancelMessage: string; + private cancelMessage: string = ""; - constructor(public state: TState, options: KeyVaultKeyPollOperationOptions) { - this.cancelMessage = options.cancelMessage; + constructor(public state: TState, options: KeyVaultKeyPollOperationOptions = {}) { + if (options.cancelMessage) { + this.cancelMessage = options.cancelMessage; + } } /** - * @summary Meant to reach to the service and update the Poller operation. - * @param [options] The optional parameters, which is only an abortSignal from @azure/abort-controller + * Meant to reach to the service and update the Poller operation. */ public async update(): Promise> { throw new Error("Operation not supported."); } /** - * @summary Meant to reach to the service and cancel the Poller operation. - * @param [options] The optional parameters, which is only an abortSignal from @azure/abort-controller + * Meant to reach to the service and cancel the Poller operation. */ public async cancel(): Promise> { throw new Error(this.cancelMessage); } /** - * @summary Serializes the Poller operation. + * Serializes the Poller operation. */ public toString(): string { return JSON.stringify({ diff --git a/sdk/keyvault/keyvault-keys/src/lro/recover/operation.ts b/sdk/keyvault/keyvault-keys/src/lro/recover/operation.ts index 7e4af34aead4..9371af5d5a70 100644 --- a/sdk/keyvault/keyvault-keys/src/lro/recover/operation.ts +++ b/sdk/keyvault/keyvault-keys/src/lro/recover/operation.ts @@ -6,7 +6,7 @@ import { operationOptionsToRequestOptionsBase, RequestOptionsBase } from "@azure import { KeyVaultClient } from "../../generated/keyVaultClient"; import { GetKeyResponse, RecoverDeletedKeyResponse } from "../../generated/models"; import { KeyVaultKey, GetKeyOptions, RecoverDeletedKeyOptions } from "../../keysModels"; -import { createSpan, setParentSpan } from "../../tracing"; +import { createSpan, setParentSpan } from "../../../../keyvault-common/src"; import { getKeyFromKeyBundle } from "../../transformations"; import { KeyVaultKeyPollOperation, KeyVaultKeyPollOperationState } from "../keyVaultKeyPoller"; @@ -14,12 +14,12 @@ import { KeyVaultKeyPollOperation, KeyVaultKeyPollOperationState } from "../keyV * An interface representing the state of a delete key's poll operation */ export interface RecoverDeletedKeyPollOperationState - extends KeyVaultKeyPollOperationState {} + extends KeyVaultKeyPollOperationState { } export class RecoverDeletedKeyPollOperation extends KeyVaultKeyPollOperation< RecoverDeletedKeyPollOperationState, KeyVaultKey -> { + > { constructor( public state: RecoverDeletedKeyPollOperationState, private vaultUrl: string, @@ -32,13 +32,10 @@ export class RecoverDeletedKeyPollOperation extends KeyVaultKeyPollOperation< /** * The getKey method gets a specified key and is applicable to any key stored in Azure Key Vault. * This operation requires the keys/get permission. - * @summary Get a specified key from a given key vault. - * @param {string} name The name of the key. - * @param {GetKeyOptions} [options] The optional parameters. */ private async getKey(name: string, options: GetKeyOptions = {}): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = createSpan("getKey", requestOptions); + const span = createSpan("generatedClient.getKey", requestOptions); let response: GetKeyResponse; try { @@ -58,15 +55,13 @@ export class RecoverDeletedKeyPollOperation extends KeyVaultKeyPollOperation< /** * Sends a request to recover a deleted Key Vault Key based on the given name. * Since the Key Vault Key won't be immediately recover the deleted key, we have {@link beginRecoverDeletedKey}. - * @param {string} name The name of the Key Vault Key. - * @param {RecoverDeletedKeyOptions} [options] Optional parameters for the underlying HTTP request. */ private async recoverDeletedKey( name: string, options: RecoverDeletedKeyOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = createSpan("recoverDeletedKey", requestOptions); + const span = createSpan("generatedClient.recoverDeletedKey", requestOptions); let response: RecoverDeletedKeyResponse; try { @@ -83,8 +78,7 @@ export class RecoverDeletedKeyPollOperation extends KeyVaultKeyPollOperation< } /** - * @summary Reaches to the service and updates the delete key's poll operation. - * @param [options] The optional parameters, which are an abortSignal from @azure/abort-controller and a function that triggers the poller's onProgress function. + * Reaches to the service and updates the delete key's poll operation. */ public async update( options: { diff --git a/sdk/keyvault/keyvault-secrets/CHANGELOG.md b/sdk/keyvault/keyvault-secrets/CHANGELOG.md index 707eae6aac0e..5427a0e4e5e3 100644 --- a/sdk/keyvault/keyvault-secrets/CHANGELOG.md +++ b/sdk/keyvault/keyvault-secrets/CHANGELOG.md @@ -2,6 +2,7 @@ ## 4.2.0-beta.2 (Unreleased) +- Updated the Key Vault Secrets Long Running Operation Pollers to follow a more compact and meaningful approach moving forward. ## 4.2.0-beta.1 (2020-09-11) diff --git a/sdk/keyvault/keyvault-secrets/src/index.ts b/sdk/keyvault/keyvault-secrets/src/index.ts index 36fe3ac5a932..bf8b46bb530a 100644 --- a/sdk/keyvault/keyvault-secrets/src/index.ts +++ b/sdk/keyvault/keyvault-secrets/src/index.ts @@ -7,26 +7,20 @@ import { TokenCredential, isTokenCredential, signingPolicy, - RequestOptionsBase, operationOptionsToRequestOptionsBase, PipelineOptions, createPipelineFromOptions } from "@azure/core-http"; -import { getTracer } from "@azure/core-tracing"; -import { Span } from "@opentelemetry/api"; import { logger } from "./log"; import "@azure/core-paging"; import { PageSettings, PagedAsyncIterableIterator } from "@azure/core-paging"; import { PollerLike, PollOperationState } from "@azure/core-lro"; import { - SecretBundle, - DeletedSecretBundle, DeletionRecoveryLevel, KeyVaultClientGetSecretsOptionalParams, SetSecretResponse, - DeleteSecretResponse, UpdateSecretResponse, GetSecretResponse, GetDeletedSecretResponse, @@ -35,7 +29,11 @@ import { } from "./generated/models"; import { KeyVaultClient } from "./generated/keyVaultClient"; import { SDK_VERSION } from "./constants"; -import { challengeBasedAuthenticationPolicy } from "../../keyvault-common/src"; +import { + challengeBasedAuthenticationPolicy, + createSpan, + setParentSpan +} from "../../keyvault-common/src"; import { DeleteSecretPoller } from "./lro/delete/poller"; import { RecoverDeletedSecretPoller } from "./lro/recover/poller"; @@ -43,8 +41,6 @@ import { RecoverDeletedSecretPoller } from "./lro/recover/poller"; import { KeyVaultSecret, DeletedSecret, - DeleteSecretOptions, - SecretClientInterface, SecretPollerOptions, BeginDeleteSecretOptions, BeginRecoverDeletedSecretOptions, @@ -55,7 +51,6 @@ import { PurgeDeletedSecretOptions, BackupSecretOptions, RestoreSecretBackupOptions, - RecoverDeletedSecretOptions, ListPropertiesOfSecretVersionsOptions, ListPropertiesOfSecretsOptions, ListDeletedSecretsOptions, @@ -64,6 +59,7 @@ import { LATEST_API_VERSION } from "./secretsModels"; import { parseKeyVaultSecretId, KeyVaultSecretId } from "./identifier"; +import { getSecretFromSecretBundle } from "./transformations"; export { SecretClientOptions, @@ -114,18 +110,6 @@ export class SecretClient { */ private readonly client: KeyVaultClient; - /** - * @internal - * @ignore - * A self reference that bypasses private methods, for the pollers. - */ - private readonly pollerClient: SecretClientInterface = { - recoverDeletedSecret: this.recoverDeletedSecret.bind(this), - getSecret: this.getSecret.bind(this), - deleteSecret: this.deleteSecret.bind(this), - getDeletedSecret: this.getDeletedSecret.bind(this) - }; - /** * Creates an instance of SecretClient. * @@ -224,7 +208,7 @@ export class SecretClient { } }; - const span = this.createSpan("setSecret", unflattenedOptions); + const span = createSpan("setSecret", unflattenedOptions); let response: SetSecretResponse; try { @@ -232,13 +216,13 @@ export class SecretClient { this.vaultUrl, secretName, value, - this.setParentSpan(span, unflattenedOptions) + setParentSpan(span, unflattenedOptions) ); } finally { span.end(); } - return this.getSecretFromSecretBundle(response); + return getSecretFromSecretBundle(response); } else { const response = await this.client.setSecret( this.vaultUrl, @@ -246,7 +230,7 @@ export class SecretClient { value, requestOptions ); - return this.getSecretFromSecretBundle(response); + return getSecretFromSecretBundle(response); } } @@ -284,7 +268,8 @@ export class SecretClient { const requestOptions = operationOptionsToRequestOptionsBase(options); const poller = new DeleteSecretPoller({ name, - client: this.pollerClient, + client: this.client, + vaultUrl: this.vaultUrl, ...options, requestOptions }); @@ -328,7 +313,7 @@ export class SecretClient { } }; - const span = this.createSpan("updateSecretProperties", unflattenedOptions); + const span = createSpan("updateSecretProperties", unflattenedOptions); let response: UpdateSecretResponse; @@ -337,13 +322,13 @@ export class SecretClient { this.vaultUrl, secretName, secretVersion, - this.setParentSpan(span, unflattenedOptions) + setParentSpan(span, unflattenedOptions) ); } finally { span.end(); } - return this.getSecretFromSecretBundle(response).properties; + return getSecretFromSecretBundle(response).properties; } else { const response = await this.client.updateSecret( this.vaultUrl, @@ -351,7 +336,7 @@ export class SecretClient { secretVersion, requestOptions ); - return this.getSecretFromSecretBundle(response).properties; + return getSecretFromSecretBundle(response).properties; } } @@ -373,7 +358,7 @@ export class SecretClient { options: GetSecretOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("getSecret", requestOptions); + const span = createSpan("getSecret", requestOptions); let response: GetSecretResponse; try { @@ -381,13 +366,13 @@ export class SecretClient { this.vaultUrl, secretName, options && options.version ? options.version : "", - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); } - return this.getSecretFromSecretBundle(response); + return getSecretFromSecretBundle(response); } /** @@ -408,7 +393,7 @@ export class SecretClient { options: GetDeletedSecretOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("getDeletedSecret", requestOptions); + const span = createSpan("getDeletedSecret", requestOptions); let response: GetDeletedSecretResponse; @@ -416,13 +401,13 @@ export class SecretClient { response = await this.client.getDeletedSecret( this.vaultUrl, secretName, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); } - return this.getSecretFromSecretBundle(response); + return getSecretFromSecretBundle(response); } /** @@ -446,13 +431,13 @@ export class SecretClient { options: PurgeDeletedSecretOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("purgeDeletedSecret", requestOptions); + const span = createSpan("purgeDeletedSecret", requestOptions); try { await this.client.purgeDeletedSecret( this.vaultUrl, secretName, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); @@ -497,7 +482,8 @@ export class SecretClient { const poller = new RecoverDeletedSecretPoller({ name, - client: this.pollerClient, + client: this.client, + vaultUrl: this.vaultUrl, ...options, requestOptions }); @@ -525,7 +511,7 @@ export class SecretClient { options: BackupSecretOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("backupSecret", requestOptions); + const span = createSpan("backupSecret", requestOptions); let response: BackupSecretResponse; @@ -533,7 +519,7 @@ export class SecretClient { response = await this.client.backupSecret( this.vaultUrl, secretName, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); } finally { span.end(); @@ -561,7 +547,7 @@ export class SecretClient { options: RestoreSecretBackupOptions = {} ): Promise { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("restoreSecretBackup", requestOptions); + const span = createSpan("restoreSecretBackup", requestOptions); let response: RestoreSecretResponse; @@ -569,73 +555,13 @@ export class SecretClient { response = await this.client.restoreSecret( this.vaultUrl, secretBundleBackup, - this.setParentSpan(span, requestOptions) - ); - } finally { - span.end(); - } - - return this.getSecretFromSecretBundle(response).properties; - } - - /** - * @internal - * @ignore - * Sends a delete request for the given KeyVault Secret's name to the KeyVault service. - * Since the KeyVault Secret won't be immediately deleted, we have {@link beginDeleteSecret}. - * @param {string} name The name of the KeyVault Secret. - * @param {RequestOptionsBase} [options] Optional parameters for the underlying HTTP request. - */ - private async deleteSecret( - secretName: string, - options: DeleteSecretOptions = {} - ): Promise { - const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("deleteSecret", requestOptions); - - let response: DeleteSecretResponse; - try { - response = await this.client.deleteSecret( - this.vaultUrl, - secretName, - this.setParentSpan(span, requestOptions) - ); - } finally { - span.end(); - } - - return this.getSecretFromSecretBundle(response); - } - - /** - * @internal - * @ignore - * Sends a request to recover a deleted KeyVault Secret based on the given name. - * Since the KeyVault Secret won't be immediately recover the deleted secret, we have {@link beginRecoverDeletedSecret}. - * @param {string} name The name of the KeyVault Secret. - * @param {RecoverDeletedKeyOptions} [options] Optional parameters for the underlying HTTP request. - */ - private async recoverDeletedSecret( - secretName: string, - options: RecoverDeletedSecretOptions = {} - ): Promise { - const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("recoverDeletedSecret", requestOptions); - - let properties: SecretProperties; - - try { - const response = await this.client.recoverDeletedSecret( - this.vaultUrl, - secretName, - this.setParentSpan(span, requestOptions) + setParentSpan(span, requestOptions) ); - properties = this.getSecretFromSecretBundle(response).properties; } finally { span.end(); } - return properties; + return getSecretFromSecretBundle(response).properties; } /** @@ -664,7 +590,7 @@ export class SecretClient { continuationState.continuationToken = currentSetResponse.nextLink; if (currentSetResponse.value) { yield currentSetResponse.value.map( - (bundle) => this.getSecretFromSecretBundle(bundle).properties + (bundle) => getSecretFromSecretBundle(bundle).properties ); } } @@ -677,7 +603,7 @@ export class SecretClient { continuationState.continuationToken = currentSetResponse.nextLink; if (currentSetResponse.value) { yield currentSetResponse.value.map( - (bundle) => this.getSecretFromSecretBundle(bundle).properties + (bundle) => getSecretFromSecretBundle(bundle).properties ); } else { break; @@ -725,10 +651,10 @@ export class SecretClient { options: ListPropertiesOfSecretVersionsOptions = {} ): PagedAsyncIterableIterator { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("listPropertiesOfSecretVersions", requestOptions); + const span = createSpan("listPropertiesOfSecretVersions", requestOptions); const updatedOptions: ListPropertiesOfSecretVersionsOptions = { ...requestOptions, - ...this.setParentSpan(span, requestOptions) + ...setParentSpan(span, requestOptions) }; const iter = this.listPropertiesOfSecretVersionsAll(secretName, updatedOptions); @@ -766,7 +692,7 @@ export class SecretClient { continuationState.continuationToken = currentSetResponse.nextLink; if (currentSetResponse.value) { yield currentSetResponse.value.map( - (bundle) => this.getSecretFromSecretBundle(bundle).properties + (bundle) => getSecretFromSecretBundle(bundle).properties ); } } @@ -778,7 +704,7 @@ export class SecretClient { continuationState.continuationToken = currentSetResponse.nextLink; if (currentSetResponse.value) { yield currentSetResponse.value.map( - (bundle) => this.getSecretFromSecretBundle(bundle).properties + (bundle) => getSecretFromSecretBundle(bundle).properties ); } else { break; @@ -823,10 +749,10 @@ export class SecretClient { options: ListPropertiesOfSecretsOptions = {} ): PagedAsyncIterableIterator { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("listPropertiesOfSecrets", requestOptions); + const span = createSpan("listPropertiesOfSecrets", requestOptions); const updatedOptions: ListPropertiesOfSecretsOptions = { ...requestOptions, - ...this.setParentSpan(span, requestOptions) + ...setParentSpan(span, requestOptions) }; const iter = this.listPropertiesOfSecretsAll(updatedOptions); @@ -866,7 +792,7 @@ export class SecretClient { ); continuationState.continuationToken = currentSetResponse.nextLink; if (currentSetResponse.value) { - yield currentSetResponse.value.map((bundle) => this.getSecretFromSecretBundle(bundle)); + yield currentSetResponse.value.map((bundle) => getSecretFromSecretBundle(bundle)); } } while (continuationState.continuationToken) { @@ -876,7 +802,7 @@ export class SecretClient { ); continuationState.continuationToken = currentSetResponse.nextLink; if (currentSetResponse.value) { - yield currentSetResponse.value.map((bundle) => this.getSecretFromSecretBundle(bundle)); + yield currentSetResponse.value.map((bundle) => getSecretFromSecretBundle(bundle)); } else { break; } @@ -919,10 +845,10 @@ export class SecretClient { options: ListDeletedSecretsOptions = {} ): PagedAsyncIterableIterator { const requestOptions = operationOptionsToRequestOptionsBase(options); - const span = this.createSpan("listDeletedSecrets", requestOptions); + const span = createSpan("listDeletedSecrets", requestOptions); const updatedOptions: ListDeletedSecretsOptions = { ...requestOptions, - ...this.setParentSpan(span, requestOptions) + ...setParentSpan(span, requestOptions) }; const iter = this.listDeletedSecretsAll(updatedOptions); @@ -938,97 +864,4 @@ export class SecretClient { byPage: (settings: PageSettings = {}) => this.listDeletedSecretsPage(settings, updatedOptions) }; } - - /** - * @internal - * @ignore - * Shapes the exposed {@link KeyVaultSecret} based on either a received secret bundle or deleted secret bundle. - */ - private getSecretFromSecretBundle(bundle: SecretBundle | DeletedSecretBundle): KeyVaultSecret { - const secretBundle = bundle as SecretBundle; - const deletedSecretBundle = bundle as DeletedSecretBundle; - const parsedId = parseKeyVaultSecretId(secretBundle.id!); - - const attributes = secretBundle.attributes; - delete secretBundle.attributes; - - const resultObject: KeyVaultSecret & DeletedSecret = { - value: secretBundle.value, - name: parsedId.name, - properties: { - expiresOn: (attributes as any).expires, - createdOn: (attributes as any).created, - updatedOn: (attributes as any).updated, - ...secretBundle, - ...parsedId, - ...attributes - } - }; - - if (deletedSecretBundle.deletedDate) { - resultObject.properties.deletedOn = deletedSecretBundle.deletedDate; - delete (resultObject.properties as any).deletedDate; - } - - if (attributes) { - if ((attributes as any).vaultUrl) { - delete (resultObject.properties as any).vaultUrl; - } - - if (attributes.expires) { - delete (resultObject.properties as any).expires; - } - - if (attributes.created) { - delete (resultObject.properties as any).created; - } - - if (attributes.updated) { - delete (resultObject.properties as any).updated; - } - } - - return resultObject; - } - - /** - * @internal - * @ignore - * Creates a span using the tracer that was set by the user - * @param {string} methodName The name of the method creating the span. - * @param {RequestOptionsBase} [options] The options for the underlying HTTP request. - */ - private createSpan(methodName: string, requestOptions: RequestOptionsBase = {}): Span { - const tracer = getTracer(); - const span = tracer.startSpan(methodName, requestOptions && requestOptions.spanOptions); - span.setAttribute("az.namespace", "Microsoft.KeyVault"); - return span; - } - - /** - * @internal - * @ignore - * Returns updated HTTP options with the given span as the parent of future spans, - * if applicable. - * @param {Span} span The span for the current operation. - * @param {RequestOptionsBase} [options] The options for the underlying HTTP request. - */ - private setParentSpan(span: Span, options: RequestOptionsBase = {}): RequestOptionsBase { - if (span.isRecording()) { - const spanOptions = options.spanOptions || {}; - return { - ...options, - spanOptions: { - ...spanOptions, - parent: span.context(), - attributes: { - ...spanOptions.attributes, - "az.namespace": "Microsoft.KeyVault" - } - } - }; - } else { - return options; - } - } } diff --git a/sdk/keyvault/keyvault-secrets/src/lro/delete/operation.ts b/sdk/keyvault/keyvault-secrets/src/lro/delete/operation.ts index c03577b8afed..7fd8396e7e8b 100644 --- a/sdk/keyvault/keyvault-secrets/src/lro/delete/operation.ts +++ b/sdk/keyvault/keyvault-secrets/src/lro/delete/operation.ts @@ -2,110 +2,129 @@ // Licensed under the MIT license. import { AbortSignalLike } from "@azure/abort-controller"; -import { PollOperationState, PollOperation } from "@azure/core-lro"; -import { RequestOptionsBase } from "@azure/core-http"; -import { DeletedSecret, SecretClientInterface } from "../../secretsModels"; +import { operationOptionsToRequestOptionsBase, RequestOptionsBase } from "@azure/core-http"; +import { DeletedSecret, DeleteSecretOptions, GetDeletedSecretOptions } from "../../secretsModels"; +import { + KeyVaultSecretPollOperation, + KeyVaultSecretPollOperationState +} from "../keyVaultSecretPoller"; +import { KeyVaultClient } from "../../generated/keyVaultClient"; +import { DeleteSecretResponse, GetDeletedSecretResponse } from "../../generated/models"; +import { createSpan, setParentSpan } from "../../../../keyvault-common/src"; +import { getSecretFromSecretBundle } from "../../transformations"; /** * An interface representing the state of a delete secret's poll operation */ -export interface DeleteSecretPollOperationState extends PollOperationState { - /** - * The name of the secret. - */ - name: string; - /** - * Options for the core-http requests. - */ - requestOptions?: RequestOptionsBase; - /** - * An interface representing a SecretClient. For internal use. - */ - client: SecretClientInterface; -} +export interface DeleteSecretPollOperationState + extends KeyVaultSecretPollOperationState {} /** * An interface representing a delete secret's poll operation */ -export interface DeleteSecretPollOperation - extends PollOperation {} - -/** - * @summary Reaches to the service and updates the delete secret's poll operation. - * @param [options] The optional parameters, which are an abortSignal from @azure/abort-controller and a function that triggers the poller's onProgress function. - */ -async function update( - this: DeleteSecretPollOperation, - options: { - abortSignal?: AbortSignalLike; - fireProgress?: (state: DeleteSecretPollOperationState) => void; - } = {} -): Promise { - const state = this.state; - const { name, client } = state; - - const requestOptions = state.requestOptions || {}; - if (options.abortSignal) { - requestOptions.abortSignal = options.abortSignal; +export class DeleteSecretPollOperation extends KeyVaultSecretPollOperation< + DeleteSecretPollOperationState, + DeletedSecret +> { + constructor( + public state: DeleteSecretPollOperationState, + private vaultUrl: string, + private client: KeyVaultClient, + private requestOptions: RequestOptionsBase = {} + ) { + super(state, { cancelMessage: "Canceling the deletion of a secret is not supported." }); } - if (!state.isStarted) { - const deletedSecret = await client.deleteSecret(name, requestOptions); - state.isStarted = true; - state.result = deletedSecret; - if (!deletedSecret.properties.recoveryId) { - state.isCompleted = true; + /** + * Sends a delete request for the given Key Vault Key's name to the Key Vault service. + * Since the Key Vault Key won't be immediately deleted, we have {@link beginDeleteKey}. + */ + private async deleteSecret( + name: string, + options: DeleteSecretOptions = {} + ): Promise { + const requestOptions = operationOptionsToRequestOptionsBase(options); + const span = createSpan("generatedClient.deleteKey", requestOptions); + + let response: DeleteSecretResponse; + try { + response = await this.client.deleteSecret( + this.vaultUrl, + name, + setParentSpan(span, requestOptions) + ); + } finally { + span.end(); } + + return getSecretFromSecretBundle(response); } - if (!state.isCompleted) { + /** + * The getDeletedSecret method returns the specified deleted secret along with its properties. + * This operation requires the secrets/get permission. + */ + private async getDeletedSecret( + name: string, + options: GetDeletedSecretOptions = {} + ): Promise { + const responseOptions = operationOptionsToRequestOptionsBase(options); + const span = createSpan("generatedClient.getDeletedSecret", responseOptions); + + let response: GetDeletedSecretResponse; try { - state.result = await client.getDeletedSecret(name, { requestOptions }); - state.isCompleted = true; - } catch (error) { - if (error.statusCode === 403) { - // At this point, the resource exists but the user doesn't have access to it. - state.isCompleted = true; - } else if (error.statusCode !== 404) { - state.error = error; - state.isCompleted = true; - } + response = await this.client.getDeletedSecret( + this.vaultUrl, + name, + setParentSpan(span, responseOptions) + ); + } finally { + span.end(); } + + return getSecretFromSecretBundle(response); } - return makeDeleteSecretPollOperation(state); -} + /** + * Reaches to the service and updates the delete secret's poll operation. + */ + public async update( + options: { + abortSignal?: AbortSignalLike; + fireProgress?: (state: DeleteSecretPollOperationState) => void; + } = {} + ): Promise { + const state = this.state; + const { name } = state; -/** - * @summary Reaches to the service and cancels the secret's operation, also updating the secret's poll operation - * @param [options] The optional parameters, which is only an abortSignal from @azure/abort-controller - */ -async function cancel(this: DeleteSecretPollOperation): Promise { - throw new Error("Canceling the deletion of a secret is not supported."); -} + if (options.abortSignal) { + this.requestOptions.abortSignal = options.abortSignal; + } -/** - * @summary Serializes the create secret's poll operation - */ -function toString(this: DeleteSecretPollOperation): string { - return JSON.stringify({ - state: this.state - }); -} + if (!state.isStarted) { + const deletedSecret = await this.deleteSecret(name, this.requestOptions); + state.isStarted = true; + state.result = deletedSecret; + if (!deletedSecret.properties.recoveryId) { + state.isCompleted = true; + } + } -/** - * @summary Builds a create secret's poll operation - * @param [state] A poll operation's state, in case the new one is intended to follow up where the previous one was left. - */ -export function makeDeleteSecretPollOperation( - state: DeleteSecretPollOperationState -): DeleteSecretPollOperation { - return { - state: { - ...state - }, - update, - cancel, - toString - }; + if (!state.isCompleted) { + try { + state.result = await this.getDeletedSecret(name, this.requestOptions); + state.isCompleted = true; + } catch (error) { + if (error.statusCode === 403) { + // At this point, the resource exists but the user doesn't have access to it. + state.isCompleted = true; + } else if (error.statusCode !== 404) { + state.error = error; + state.isCompleted = true; + } + } + } + + return this; + } } diff --git a/sdk/keyvault/keyvault-secrets/src/lro/delete/poller.ts b/sdk/keyvault/keyvault-secrets/src/lro/delete/poller.ts index 6a98d48560d0..8284ea57af31 100644 --- a/sdk/keyvault/keyvault-secrets/src/lro/delete/poller.ts +++ b/sdk/keyvault/keyvault-secrets/src/lro/delete/poller.ts @@ -1,31 +1,19 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { delay, RequestOptionsBase } from "@azure/core-http"; -import { Poller } from "@azure/core-lro"; -import { DeleteSecretPollOperationState, makeDeleteSecretPollOperation } from "./operation"; -import { DeletedSecret, SecretClientInterface } from "../../secretsModels"; - -export interface DeleteSecretPollerOptions { - client: SecretClientInterface; - name: string; - requestOptions?: RequestOptionsBase; - intervalInMs?: number; - resumeFrom?: string; -} +import { DeleteSecretPollOperation, DeleteSecretPollOperationState } from "./operation"; +import { DeletedSecret } from "../../secretsModels"; +import { KeyVaultSecretPoller, KeyVaultSecretPollerOptions } from "../keyVaultSecretPoller"; /** - * Class that deletes a poller that waits until a secret finishes being deleted + * Class that creates a poller that waits until a secret finishes being deleted. */ -export class DeleteSecretPoller extends Poller { - /** - * Defines how much time the poller is going to wait before making a new request to the service. - * @memberof DeleteSecretPoller - */ - public intervalInMs: number; - - constructor(options: DeleteSecretPollerOptions) { - const { client, name, requestOptions, intervalInMs = 2000, resumeFrom } = options; +export class DeleteSecretPoller extends KeyVaultSecretPoller< + DeleteSecretPollOperationState, + DeletedSecret +> { + constructor(options: KeyVaultSecretPollerOptions) { + const { vaultUrl, client, name, requestOptions, intervalInMs = 2000, resumeFrom } = options; let state: DeleteSecretPollOperationState | undefined; @@ -33,23 +21,18 @@ export class DeleteSecretPoller extends Poller { - return delay(this.intervalInMs); - } } diff --git a/sdk/keyvault/keyvault-secrets/src/lro/keyVaultSecretPoller.ts b/sdk/keyvault/keyvault-secrets/src/lro/keyVaultSecretPoller.ts new file mode 100644 index 000000000000..6f96edb71ebf --- /dev/null +++ b/sdk/keyvault/keyvault-secrets/src/lro/keyVaultSecretPoller.ts @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { delay, RequestOptionsBase } from "@azure/core-http"; +import { Poller, PollOperation, PollOperationState } from "@azure/core-lro"; +import { KeyVaultClient } from "../generated/keyVaultClient"; + +/** + * Common parameters to a Key Vault Secret Poller. + */ +export interface KeyVaultSecretPollerOptions { + vaultUrl: string; + client: KeyVaultClient; + name: string; + requestOptions?: RequestOptionsBase; + intervalInMs?: number; + resumeFrom?: string; +} + +/** + * An interface representing the state of a Key Vault Secret Poller's operation. + */ +export interface KeyVaultSecretPollOperationState extends PollOperationState { + /** + * The name of the secret. + */ + name: string; +} + +/** + * Common properties and methods of the Key Vault Secret Pollers. + */ +export abstract class KeyVaultSecretPoller< + TState extends KeyVaultSecretPollOperationState, + TResult +> extends Poller { + /** + * Defines how much time the poller is going to wait before making a new request to the service. + */ + public intervalInMs: number = 2000; + + /** + * The method used by the poller to wait before attempting to update its operation. + */ + async delay(): Promise { + return delay(this.intervalInMs); + } +} + +/** + * Optional parameters to the KeyVaultSecretPollOperation + */ +export interface KeyVaultSecretPollOperationOptions { + cancelMessage?: string; +} + +/** + * Common properties and methods of the Key Vault Secret Poller operations. + */ +export class KeyVaultSecretPollOperation< + TState extends KeyVaultSecretPollOperationState, + TResult +> implements PollOperation { + private cancelMessage: string = ""; + + constructor(public state: TState, options: KeyVaultSecretPollOperationOptions = {}) { + if (options.cancelMessage) { + this.cancelMessage = options.cancelMessage; + } + } + + /** + * @summary Meant to reach to the service and update the Poller operation. + * @param [options] The optional parameters, which is only an abortSignal from @azure/abort-controller + */ + public async update(): Promise> { + throw new Error("Operation not supported."); + } + + /** + * @summary Meant to reach to the service and cancel the Poller operation. + * @param [options] The optional parameters, which is only an abortSignal from @azure/abort-controller + */ + public async cancel(): Promise> { + throw new Error(this.cancelMessage); + } + + /** + * @summary Serializes the Poller operation. + */ + public toString(): string { + return JSON.stringify({ + state: this.state + }); + } +} diff --git a/sdk/keyvault/keyvault-secrets/src/lro/recover/operation.ts b/sdk/keyvault/keyvault-secrets/src/lro/recover/operation.ts index d630786969e6..1cfcaae5ff40 100644 --- a/sdk/keyvault/keyvault-secrets/src/lro/recover/operation.ts +++ b/sdk/keyvault/keyvault-secrets/src/lro/recover/operation.ts @@ -2,117 +2,137 @@ // Licensed under the MIT license. import { AbortSignalLike } from "@azure/abort-controller"; -import { PollOperationState, PollOperation } from "@azure/core-lro"; -import { RequestOptionsBase } from "@azure/core-http"; -import { SecretProperties, SecretClientInterface } from "../../secretsModels"; +import { operationOptionsToRequestOptionsBase, RequestOptionsBase } from "@azure/core-http"; +import { + DeletedSecret, + GetSecretOptions, + KeyVaultSecret, + SecretProperties +} from "../../secretsModels"; +import { + KeyVaultSecretPollOperation, + KeyVaultSecretPollOperationState +} from "../keyVaultSecretPoller"; +import { KeyVaultClient } from "../../generated/keyVaultClient"; +import { createSpan, setParentSpan } from "../../../../keyvault-common/src"; +import { GetSecretResponse } from "../../generated/models"; +import { getSecretFromSecretBundle } from "../../transformations"; /** * An interface representing the state of a delete secret's poll operation */ export interface RecoverDeletedSecretPollOperationState - extends PollOperationState { - /** - * The name of the secret. - */ - name: string; - /** - * Options for the core-http requests. - */ - requestOptions?: RequestOptionsBase; - /** - * An interface representing a SecretClient. For internal use. - */ - client: SecretClientInterface; -} + extends KeyVaultSecretPollOperationState {} /** * An interface representing a delete secret's poll operation */ -export interface RecoverDeletedSecretPollOperation - extends PollOperation {} - -/** - * @summary Reaches to the service and updates the delete secret's poll operation. - * @param [options] The optional parameters, which are an abortSignal from @azure/abort-controller and a function that triggers the poller's onProgress function. - */ -async function update( - this: RecoverDeletedSecretPollOperation, - options: { - abortSignal?: AbortSignalLike; - fireProgress?: (state: RecoverDeletedSecretPollOperationState) => void; - } = {} -): Promise { - const state = this.state; - const { name, client } = state; - - const requestOptions = state.requestOptions || {}; - if (options.abortSignal) { - requestOptions.abortSignal = options.abortSignal; +export class RecoverDeletedSecretPollOperation extends KeyVaultSecretPollOperation< + RecoverDeletedSecretPollOperationState, + SecretProperties +> { + constructor( + public state: RecoverDeletedSecretPollOperationState, + private vaultUrl: string, + private client: KeyVaultClient, + private requestOptions: RequestOptionsBase = {} + ) { + super(state, { cancelMessage: "Canceling the recovery of a deleted secret is not supported." }); } - if (!state.isStarted) { + /** + * The getSecret method returns the specified secret along with its properties. + * This operation requires the secrets/get permission. + */ + private async getSecret(name: string, options: GetSecretOptions = {}): Promise { + const responseOptions = operationOptionsToRequestOptionsBase(options); + const span = createSpan("generatedClient.getSecret", responseOptions); + + let response: GetSecretResponse; try { - state.result = (await client.getSecret(name, { requestOptions })).properties; - state.isCompleted = true; - } catch { - // Nothing to do here. - } - if (!state.isCompleted) { - state.result = await client.recoverDeletedSecret(name, { requestOptions }); - state.isStarted = true; + response = await this.client.getSecret( + this.vaultUrl, + name, + options && options.version ? options.version : "", + setParentSpan(span, responseOptions) + ); + } finally { + span.end(); } + + return getSecretFromSecretBundle(response); } - if (!state.isCompleted) { + /** + * The recoverDeletedSecret method recovers the specified deleted secret along with its properties. + * This operation requires the secrets/recover permission. + */ + private async recoverDeletedSecret( + name: string, + options: GetSecretOptions = {} + ): Promise { + const responseOptions = operationOptionsToRequestOptionsBase(options); + const span = createSpan("generatedClient.recoverDeletedSecret", responseOptions); + + let response: GetSecretResponse; try { - state.result = (await client.getSecret(name, { requestOptions })).properties; - state.isCompleted = true; - } catch (error) { - if (error.statusCode === 403) { - // At this point, the resource exists but the user doesn't have access to it. - state.isCompleted = true; - } else if (error.statusCode !== 404) { - state.error = error; - state.isCompleted = true; - } + response = await this.client.recoverDeletedSecret( + this.vaultUrl, + name, + setParentSpan(span, responseOptions) + ); + } finally { + span.end(); } + + return getSecretFromSecretBundle(response); } - return makeRecoverDeletedSecretPollOperation(state); -} + /** + * Reaches to the service and updates the delete secret's poll operation. + */ + async update( + this: RecoverDeletedSecretPollOperation, + options: { + abortSignal?: AbortSignalLike; + fireProgress?: (state: RecoverDeletedSecretPollOperationState) => void; + } = {} + ): Promise { + const state = this.state; + const { name } = state; -/** - * @summary Reaches to the service and cancels the secret's operation, also updating the secret's poll operation - * @param [options] The optional parameters, which is only an abortSignal from @azure/abort-controller - */ -async function cancel( - this: RecoverDeletedSecretPollOperation -): Promise { - throw new Error("Canceling the deletion of a secret is not supported."); -} + if (options.abortSignal) { + this.requestOptions.abortSignal = options.abortSignal; + } -/** - * @summary Serializes the create secret's poll operation - */ -function toString(this: RecoverDeletedSecretPollOperation): string { - return JSON.stringify({ - state: this.state - }); -} + if (!state.isStarted) { + try { + state.result = (await this.getSecret(name, this.requestOptions)).properties; + state.isCompleted = true; + } catch { + // Nothing to do here. + } + if (!state.isCompleted) { + state.result = (await this.recoverDeletedSecret(name, this.requestOptions)).properties; + state.isStarted = true; + } + } -/** - * @summary Builds a create secret's poll operation - * @param [state] A poll operation's state, in case the new one is intended to follow up where the previous one was left. - */ -export function makeRecoverDeletedSecretPollOperation( - state: RecoverDeletedSecretPollOperationState -): RecoverDeletedSecretPollOperation { - return { - state: { - ...state - }, - update, - cancel, - toString - }; + if (!state.isCompleted) { + try { + state.result = (await this.getSecret(name, this.requestOptions)).properties; + state.isCompleted = true; + } catch (error) { + if (error.statusCode === 403) { + // At this point, the resource exists but the user doesn't have access to it. + state.isCompleted = true; + } else if (error.statusCode !== 404) { + state.error = error; + state.isCompleted = true; + } + } + } + + return this; + } } diff --git a/sdk/keyvault/keyvault-secrets/src/lro/recover/poller.ts b/sdk/keyvault/keyvault-secrets/src/lro/recover/poller.ts index a821c9eb7b3b..2a2c87a1b189 100644 --- a/sdk/keyvault/keyvault-secrets/src/lro/recover/poller.ts +++ b/sdk/keyvault/keyvault-secrets/src/lro/recover/poller.ts @@ -1,37 +1,22 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { delay, RequestOptionsBase } from "@azure/core-http"; -import { Poller } from "@azure/core-lro"; import { - RecoverDeletedSecretPollOperationState, - makeRecoverDeletedSecretPollOperation + RecoverDeletedSecretPollOperation, + RecoverDeletedSecretPollOperationState } from "./operation"; -import { SecretProperties, SecretClientInterface } from "../../secretsModels"; - -export interface RecoverDeletedSecretPollerOptions { - client: SecretClientInterface; - name: string; - requestOptions?: RequestOptionsBase; - intervalInMs?: number; - resumeFrom?: string; -} +import { SecretProperties } from "../../secretsModels"; +import { KeyVaultSecretPoller, KeyVaultSecretPollerOptions } from "../keyVaultSecretPoller"; /** * Class that deletes a poller that waits until a secret finishes being deleted */ -export class RecoverDeletedSecretPoller extends Poller< +export class RecoverDeletedSecretPoller extends KeyVaultSecretPoller< RecoverDeletedSecretPollOperationState, SecretProperties > { - /** - * Defines how much time the poller is going to wait before making a new request to the service. - * @memberof RecoverDeletedSecretPoller - */ - public intervalInMs: number; - - constructor(options: RecoverDeletedSecretPollerOptions) { - const { client, name, requestOptions, intervalInMs = 2000, resumeFrom } = options; + constructor(options: KeyVaultSecretPollerOptions) { + const { vaultUrl, client, name, requestOptions, intervalInMs = 2000, resumeFrom } = options; let state: RecoverDeletedSecretPollOperationState | undefined; @@ -39,23 +24,18 @@ export class RecoverDeletedSecretPoller extends Poller< state = JSON.parse(resumeFrom).state; } - const operation = makeRecoverDeletedSecretPollOperation({ - ...state, - name, - requestOptions, - client - }); + const operation = new RecoverDeletedSecretPollOperation( + { + ...state, + name + }, + vaultUrl, + client, + requestOptions + ); super(operation); this.intervalInMs = intervalInMs; } - - /** - * The method used by the poller to wait before attempting to update its operation. - * @memberof RecoverDeletedSecretPoller - */ - async delay(): Promise { - return delay(this.intervalInMs); - } } diff --git a/sdk/keyvault/keyvault-secrets/src/secretsModels.ts b/sdk/keyvault/keyvault-secrets/src/secretsModels.ts index ba689263abd0..f86521f3617c 100644 --- a/sdk/keyvault/keyvault-secrets/src/secretsModels.ts +++ b/sdk/keyvault/keyvault-secrets/src/secretsModels.ts @@ -4,35 +4,6 @@ import * as coreHttp from "@azure/core-http"; import { DeletionRecoveryLevel } from "./generated/models"; -/** - * @internal - * @ignore - * An interface representing the SecretClient. For internal use. - */ -export interface SecretClientInterface { - /** - * Recovers the deleted secret in the specified vault. - */ - recoverDeletedSecret( - secretName: string, - options?: RecoverDeletedSecretOptions - ): Promise; - /** - * The getSecret method is applicable to any secret stored in Azure Key Vault. This operation requires - * the secrets/get permission. - */ - getSecret(secretName: string, options?: GetSecretOptions): Promise; - /** - * Deletes a secret stored in Azure Key Vault. - */ - deleteSecret(secretName: string, options?: coreHttp.OperationOptions): Promise; - /** - * The getDeletedSecret method returns the specified deleted secret along with its properties. - * This operation requires the secrets/get permission. - */ - getDeletedSecret(secretName: string, options?: DeleteSecretOptions): Promise; -} - /** * The latest supported KeyVault service API version */ diff --git a/sdk/keyvault/keyvault-secrets/src/transformations.ts b/sdk/keyvault/keyvault-secrets/src/transformations.ts new file mode 100644 index 000000000000..37a2f5b0b5ea --- /dev/null +++ b/sdk/keyvault/keyvault-secrets/src/transformations.ts @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { DeletedSecretBundle, SecretBundle } from "./generated/models"; +import { parseKeyVaultSecretId } from "./identifier"; +import { DeletedSecret, KeyVaultSecret } from "./secretsModels"; + +/** + * @internal + * @ignore + * Shapes the exposed {@link KeyVaultKey} based on either a received secret bundle or deleted secret bundle. + */ +export function getSecretFromSecretBundle( + bundle: SecretBundle | DeletedSecretBundle +): KeyVaultSecret { + const secretBundle = bundle as SecretBundle; + const deletedSecretBundle = bundle as DeletedSecretBundle; + const parsedId = parseKeyVaultSecretId(secretBundle.id!); + + const attributes = secretBundle.attributes; + delete secretBundle.attributes; + + const resultObject: KeyVaultSecret & DeletedSecret = { + value: secretBundle.value, + name: parsedId.name, + properties: { + expiresOn: (attributes as any).expires, + createdOn: (attributes as any).created, + updatedOn: (attributes as any).updated, + ...secretBundle, + ...parsedId, + ...attributes + } + }; + + if (deletedSecretBundle.deletedDate) { + resultObject.properties.deletedOn = deletedSecretBundle.deletedDate; + delete (resultObject.properties as any).deletedDate; + } + + if (attributes) { + if ((attributes as any).vaultUrl) { + delete (resultObject.properties as any).vaultUrl; + } + + if (attributes.expires) { + delete (resultObject.properties as any).expires; + } + + if (attributes.created) { + delete (resultObject.properties as any).created; + } + + if (attributes.updated) { + delete (resultObject.properties as any).updated; + } + } + + return resultObject; +}