2022-07-12 09:00:22 +02:00
'use strict' ;
Object . defineProperty ( exports , '__esModule' , { value : true } ) ;
var logger$1 = require ( '@azure/logger' ) ;
2022-10-29 10:03:51 +02:00
var abortController = require ( '@azure/abort-controller' ) ;
// Copyright (c) Microsoft Corporation.
/ * *
* The ` @azure/logger ` configuration for this package .
* @ internal
* /
const logger = logger$1 . createClientLogger ( "core-lro" ) ;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
/ * *
* The default time interval to wait before sending the next polling request .
* /
const POLL _INTERVAL _IN _MS = 2000 ;
/ * *
* The closed set of terminal states .
* /
const terminalStates = [ "succeeded" , "canceled" , "failed" ] ;
// Copyright (c) Microsoft Corporation.
/ * *
* Deserializes the state
* /
function deserializeState ( serializedState ) {
try {
return JSON . parse ( serializedState ) . state ;
}
catch ( e ) {
throw new Error ( ` Unable to deserialize input state: ${ serializedState } ` ) ;
}
}
function setStateError ( inputs ) {
2023-02-22 17:47:24 -08:00
const { state , stateProxy , isOperationError } = inputs ;
2022-10-29 10:03:51 +02:00
return ( error ) => {
2023-02-22 17:47:24 -08:00
if ( isOperationError ( error ) ) {
stateProxy . setError ( state , error ) ;
stateProxy . setFailed ( state ) ;
}
2022-10-29 10:03:51 +02:00
throw error ;
} ;
}
function processOperationStatus ( result ) {
const { state , stateProxy , status , isDone , processResult , response , setErrorAsResult } = result ;
switch ( status ) {
case "succeeded" : {
stateProxy . setSucceeded ( state ) ;
break ;
}
case "failed" : {
stateProxy . setError ( state , new Error ( ` The long-running operation has failed ` ) ) ;
stateProxy . setFailed ( state ) ;
break ;
}
case "canceled" : {
stateProxy . setCanceled ( state ) ;
break ;
}
}
if ( ( isDone === null || isDone === void 0 ? void 0 : isDone ( response , state ) ) ||
( isDone === undefined &&
[ "succeeded" , "canceled" ] . concat ( setErrorAsResult ? [ ] : [ "failed" ] ) . includes ( status ) ) ) {
stateProxy . setResult ( state , buildResult ( {
response ,
state ,
processResult ,
} ) ) ;
}
}
function buildResult ( inputs ) {
const { processResult , response , state } = inputs ;
return processResult ? processResult ( response , state ) : response ;
}
/ * *
* Initiates the long - running operation .
* /
async function initOperation ( inputs ) {
const { init , stateProxy , processResult , getOperationStatus , withOperationLocation , setErrorAsResult , } = inputs ;
const { operationLocation , resourceLocation , metadata , response } = await init ( ) ;
if ( operationLocation )
withOperationLocation === null || withOperationLocation === void 0 ? void 0 : withOperationLocation ( operationLocation , false ) ;
const config = {
metadata ,
operationLocation ,
resourceLocation ,
} ;
logger . verbose ( ` LRO: Operation description: ` , config ) ;
const state = stateProxy . initState ( config ) ;
const status = getOperationStatus ( { response , state , operationLocation } ) ;
processOperationStatus ( { state , status , stateProxy , response , setErrorAsResult , processResult } ) ;
return state ;
}
async function pollOperationHelper ( inputs ) {
2023-02-22 17:47:24 -08:00
const { poll , state , stateProxy , operationLocation , getOperationStatus , getResourceLocation , isOperationError , options , } = inputs ;
2022-10-29 10:03:51 +02:00
const response = await poll ( operationLocation , options ) . catch ( setStateError ( {
state ,
stateProxy ,
2023-02-22 17:47:24 -08:00
isOperationError ,
2022-10-29 10:03:51 +02:00
} ) ) ;
const status = getOperationStatus ( response , state ) ;
logger . verbose ( ` LRO: Status: \n \t Polling from: ${ state . config . operationLocation } \n \t Operation status: ${ status } \n \t Polling status: ${ terminalStates . includes ( status ) ? "Stopped" : "Running" } ` ) ;
if ( status === "succeeded" ) {
const resourceLocation = getResourceLocation ( response , state ) ;
if ( resourceLocation !== undefined ) {
return {
2023-02-22 17:47:24 -08:00
response : await poll ( resourceLocation ) . catch ( setStateError ( { state , stateProxy , isOperationError } ) ) ,
2022-10-29 10:03:51 +02:00
status ,
} ;
}
}
return { response , status } ;
}
/** Polls the long-running operation. */
async function pollOperation ( inputs ) {
2023-02-22 17:47:24 -08:00
const { poll , state , stateProxy , options , getOperationStatus , getResourceLocation , getOperationLocation , isOperationError , withOperationLocation , getPollingInterval , processResult , updateState , setDelay , isDone , setErrorAsResult , } = inputs ;
2022-10-29 10:03:51 +02:00
const { operationLocation } = state . config ;
if ( operationLocation !== undefined ) {
const { response , status } = await pollOperationHelper ( {
poll ,
getOperationStatus ,
state ,
stateProxy ,
operationLocation ,
getResourceLocation ,
2023-02-22 17:47:24 -08:00
isOperationError ,
2022-10-29 10:03:51 +02:00
options ,
} ) ;
processOperationStatus ( {
status ,
response ,
state ,
stateProxy ,
isDone ,
processResult ,
setErrorAsResult ,
} ) ;
if ( ! terminalStates . includes ( status ) ) {
const intervalInMs = getPollingInterval === null || getPollingInterval === void 0 ? void 0 : getPollingInterval ( response ) ;
if ( intervalInMs )
setDelay ( intervalInMs ) ;
const location = getOperationLocation === null || getOperationLocation === void 0 ? void 0 : getOperationLocation ( response , state ) ;
if ( location !== undefined ) {
const isUpdated = operationLocation !== location ;
state . config . operationLocation = location ;
withOperationLocation === null || withOperationLocation === void 0 ? void 0 : withOperationLocation ( location , isUpdated ) ;
}
else
withOperationLocation === null || withOperationLocation === void 0 ? void 0 : withOperationLocation ( operationLocation , false ) ;
}
updateState === null || updateState === void 0 ? void 0 : updateState ( state , response ) ;
}
}
// Copyright (c) Microsoft Corporation.
function getOperationLocationPollingUrl ( inputs ) {
const { azureAsyncOperation , operationLocation } = inputs ;
return operationLocation !== null && operationLocation !== void 0 ? operationLocation : azureAsyncOperation ;
}
function getLocationHeader ( rawResponse ) {
return rawResponse . headers [ "location" ] ;
}
function getOperationLocationHeader ( rawResponse ) {
return rawResponse . headers [ "operation-location" ] ;
}
function getAzureAsyncOperationHeader ( rawResponse ) {
return rawResponse . headers [ "azure-asyncoperation" ] ;
}
function findResourceLocation ( inputs ) {
const { location , requestMethod , requestPath , resourceLocationConfig } = inputs ;
switch ( requestMethod ) {
case "PUT" : {
return requestPath ;
}
case "DELETE" : {
return undefined ;
}
default : {
switch ( resourceLocationConfig ) {
case "azure-async-operation" : {
return undefined ;
}
case "original-uri" : {
return requestPath ;
}
case "location" :
default : {
return location ;
}
}
}
}
}
function inferLroMode ( inputs ) {
const { rawResponse , requestMethod , requestPath , resourceLocationConfig } = inputs ;
const operationLocation = getOperationLocationHeader ( rawResponse ) ;
const azureAsyncOperation = getAzureAsyncOperationHeader ( rawResponse ) ;
const pollingUrl = getOperationLocationPollingUrl ( { operationLocation , azureAsyncOperation } ) ;
const location = getLocationHeader ( rawResponse ) ;
const normalizedRequestMethod = requestMethod === null || requestMethod === void 0 ? void 0 : requestMethod . toLocaleUpperCase ( ) ;
if ( pollingUrl !== undefined ) {
return {
mode : "OperationLocation" ,
operationLocation : pollingUrl ,
resourceLocation : findResourceLocation ( {
requestMethod : normalizedRequestMethod ,
location ,
requestPath ,
resourceLocationConfig ,
} ) ,
} ;
}
else if ( location !== undefined ) {
return {
mode : "ResourceLocation" ,
operationLocation : location ,
} ;
}
else if ( normalizedRequestMethod === "PUT" && requestPath ) {
return {
mode : "Body" ,
operationLocation : requestPath ,
} ;
}
else {
return undefined ;
}
}
function transformStatus ( inputs ) {
const { status , statusCode } = inputs ;
if ( typeof status !== "string" && status !== undefined ) {
throw new Error ( ` Polling was unsuccessful. Expected status to have a string value or no value but it has instead: ${ status } . This doesn't necessarily indicate the operation has failed. Check your Azure subscription or resource status for more information. ` ) ;
}
switch ( status === null || status === void 0 ? void 0 : status . toLocaleLowerCase ( ) ) {
case undefined :
return toOperationStatus ( statusCode ) ;
case "succeeded" :
return "succeeded" ;
case "failed" :
return "failed" ;
case "running" :
case "accepted" :
case "started" :
case "canceling" :
case "cancelling" :
return "running" ;
case "canceled" :
case "cancelled" :
return "canceled" ;
default : {
logger . warning ( ` LRO: unrecognized operation status: ${ status } ` ) ;
return status ;
}
}
}
function getStatus ( rawResponse ) {
var _a ;
const { status } = ( _a = rawResponse . body ) !== null && _a !== void 0 ? _a : { } ;
return transformStatus ( { status , statusCode : rawResponse . statusCode } ) ;
}
function getProvisioningState ( rawResponse ) {
var _a , _b ;
const { properties , provisioningState } = ( _a = rawResponse . body ) !== null && _a !== void 0 ? _a : { } ;
const status = ( _b = properties === null || properties === void 0 ? void 0 : properties . provisioningState ) !== null && _b !== void 0 ? _b : provisioningState ;
return transformStatus ( { status , statusCode : rawResponse . statusCode } ) ;
}
function toOperationStatus ( statusCode ) {
if ( statusCode === 202 ) {
return "running" ;
}
else if ( statusCode < 300 ) {
return "succeeded" ;
}
else {
return "failed" ;
}
}
function parseRetryAfter ( { rawResponse } ) {
const retryAfter = rawResponse . headers [ "retry-after" ] ;
if ( retryAfter !== undefined ) {
// Retry-After header value is either in HTTP date format, or in seconds
const retryAfterInSeconds = parseInt ( retryAfter ) ;
return isNaN ( retryAfterInSeconds )
? calculatePollingIntervalFromDate ( new Date ( retryAfter ) )
: retryAfterInSeconds * 1000 ;
}
return undefined ;
}
function calculatePollingIntervalFromDate ( retryAfterDate ) {
const timeNow = Math . floor ( new Date ( ) . getTime ( ) ) ;
const retryAfterTime = retryAfterDate . getTime ( ) ;
if ( timeNow < retryAfterTime ) {
return retryAfterTime - timeNow ;
}
return undefined ;
}
function getStatusFromInitialResponse ( inputs ) {
const { response , state , operationLocation } = inputs ;
function helper ( ) {
var _a ;
const mode = ( _a = state . config . metadata ) === null || _a === void 0 ? void 0 : _a [ "mode" ] ;
switch ( mode ) {
case undefined :
return toOperationStatus ( response . rawResponse . statusCode ) ;
case "Body" :
return getOperationStatus ( response , state ) ;
default :
return "running" ;
}
}
const status = helper ( ) ;
return status === "running" && operationLocation === undefined ? "succeeded" : status ;
}
/ * *
* Initiates the long - running operation .
* /
async function initHttpOperation ( inputs ) {
const { stateProxy , resourceLocationConfig , processResult , lro , setErrorAsResult } = inputs ;
return initOperation ( {
init : async ( ) => {
const response = await lro . sendInitialRequest ( ) ;
const config = inferLroMode ( {
rawResponse : response . rawResponse ,
requestPath : lro . requestPath ,
requestMethod : lro . requestMethod ,
resourceLocationConfig ,
} ) ;
return Object . assign ( { response , operationLocation : config === null || config === void 0 ? void 0 : config . operationLocation , resourceLocation : config === null || config === void 0 ? void 0 : config . resourceLocation } , ( ( config === null || config === void 0 ? void 0 : config . mode ) ? { metadata : { mode : config . mode } } : { } ) ) ;
} ,
stateProxy ,
processResult : processResult
? ( { flatResponse } , state ) => processResult ( flatResponse , state )
: ( { flatResponse } ) => flatResponse ,
getOperationStatus : getStatusFromInitialResponse ,
setErrorAsResult ,
} ) ;
}
function getOperationLocation ( { rawResponse } , state ) {
var _a ;
const mode = ( _a = state . config . metadata ) === null || _a === void 0 ? void 0 : _a [ "mode" ] ;
switch ( mode ) {
case "OperationLocation" : {
return getOperationLocationPollingUrl ( {
operationLocation : getOperationLocationHeader ( rawResponse ) ,
azureAsyncOperation : getAzureAsyncOperationHeader ( rawResponse ) ,
} ) ;
}
case "ResourceLocation" : {
return getLocationHeader ( rawResponse ) ;
}
case "Body" :
default : {
return undefined ;
}
}
}
function getOperationStatus ( { rawResponse } , state ) {
var _a ;
const mode = ( _a = state . config . metadata ) === null || _a === void 0 ? void 0 : _a [ "mode" ] ;
switch ( mode ) {
case "OperationLocation" : {
return getStatus ( rawResponse ) ;
}
case "ResourceLocation" : {
return toOperationStatus ( rawResponse . statusCode ) ;
}
case "Body" : {
return getProvisioningState ( rawResponse ) ;
}
default :
throw new Error ( ` Internal error: Unexpected operation mode: ${ mode } ` ) ;
}
}
function getResourceLocation ( { flatResponse } , state ) {
if ( typeof flatResponse === "object" ) {
const resourceLocation = flatResponse . resourceLocation ;
if ( resourceLocation !== undefined ) {
state . config . resourceLocation = resourceLocation ;
}
}
return state . config . resourceLocation ;
}
2023-02-22 17:47:24 -08:00
function isOperationError ( e ) {
return e . name === "RestError" ;
}
2022-10-29 10:03:51 +02:00
/** Polls the long-running operation. */
async function pollHttpOperation ( inputs ) {
const { lro , stateProxy , options , processResult , updateState , setDelay , state , setErrorAsResult , } = inputs ;
return pollOperation ( {
state ,
stateProxy ,
setDelay ,
processResult : processResult
? ( { flatResponse } , inputState ) => processResult ( flatResponse , inputState )
: ( { flatResponse } ) => flatResponse ,
updateState ,
getPollingInterval : parseRetryAfter ,
getOperationLocation ,
getOperationStatus ,
2023-02-22 17:47:24 -08:00
isOperationError ,
2022-10-29 10:03:51 +02:00
getResourceLocation ,
options ,
/ * *
* The expansion here is intentional because ` lro ` could be an object that
* references an inner this , so we need to preserve a reference to it .
* /
poll : async ( location , inputOptions ) => lro . sendPollRequest ( location , inputOptions ) ,
setErrorAsResult ,
} ) ;
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
/ * *
* Map an optional value through a function
* @ internal
* /
const maybemap = ( value , f ) => value === undefined ? undefined : f ( value ) ;
const INTERRUPTED = new Error ( "The poller is already stopped" ) ;
/ * *
* A promise that delays resolution until a certain amount of time ( in milliseconds ) has passed , with facilities for
* robust cancellation .
*
* # # # Example :
*
* ` ` ` javascript
* let toCancel ;
*
* // Wait 20 seconds, and optionally allow the function to be cancelled.
* await delayMs ( 20000 , ( cancel ) => { toCancel = cancel } ) ;
*
* // ... if `toCancel` is called before the 20 second timer expires, then the delayMs promise will reject.
* ` ` `
*
* @ internal
* @ param ms - the number of milliseconds to wait before resolving
* @ param cb - a callback that can provide the caller with a cancellation function
* /
function delayMs ( ms ) {
let aborted = false ;
let toReject ;
return Object . assign ( new Promise ( ( resolve , reject ) => {
let token ;
toReject = ( ) => {
maybemap ( token , clearTimeout ) ;
reject ( INTERRUPTED ) ;
} ;
// In the rare case that the operation is _already_ aborted, we will reject instantly. This could happen, for
// example, if the user calls the cancellation function immediately without yielding execution.
if ( aborted ) {
toReject ( ) ;
}
else {
token = setTimeout ( resolve , ms ) ;
}
} ) , {
cancel : ( ) => {
aborted = true ;
toReject === null || toReject === void 0 ? void 0 : toReject ( ) ;
} ,
} ) ;
}
// Copyright (c) Microsoft Corporation.
const createStateProxy$1 = ( ) => ( {
/ * *
* The state at this point is created to be of type OperationState < TResult > .
* It will be updated later to be of type TState when the
* customer - provided callback , ` updateState ` , is called during polling .
* /
initState : ( config ) => ( { status : "running" , config } ) ,
setCanceled : ( state ) => ( state . status = "canceled" ) ,
setError : ( state , error ) => ( state . error = error ) ,
setResult : ( state , result ) => ( state . result = result ) ,
setRunning : ( state ) => ( state . status = "running" ) ,
setSucceeded : ( state ) => ( state . status = "succeeded" ) ,
setFailed : ( state ) => ( state . status = "failed" ) ,
getError : ( state ) => state . error ,
getResult : ( state ) => state . result ,
isCanceled : ( state ) => state . status === "canceled" ,
isFailed : ( state ) => state . status === "failed" ,
isRunning : ( state ) => state . status === "running" ,
isSucceeded : ( state ) => state . status === "succeeded" ,
} ) ;
/ * *
* Returns a poller factory .
* /
function buildCreatePoller ( inputs ) {
2023-02-22 17:47:24 -08:00
const { getOperationLocation , getStatusFromInitialResponse , getStatusFromPollResponse , isOperationError , getResourceLocation , getPollingInterval , resolveOnUnsuccessful , } = inputs ;
2022-10-29 10:03:51 +02:00
return async ( { init , poll } , options ) => {
const { processResult , updateState , withOperationLocation : withOperationLocationCallback , intervalInMs = POLL _INTERVAL _IN _MS , restoreFrom , } = options || { } ;
const stateProxy = createStateProxy$1 ( ) ;
const withOperationLocation = withOperationLocationCallback
? ( ( ) => {
let called = false ;
return ( operationLocation , isUpdated ) => {
if ( isUpdated )
withOperationLocationCallback ( operationLocation ) ;
else if ( ! called )
withOperationLocationCallback ( operationLocation ) ;
called = true ;
} ;
} ) ( )
: undefined ;
const state = restoreFrom
? deserializeState ( restoreFrom )
: await initOperation ( {
init ,
stateProxy ,
processResult ,
getOperationStatus : getStatusFromInitialResponse ,
withOperationLocation ,
setErrorAsResult : ! resolveOnUnsuccessful ,
} ) ;
let resultPromise ;
let cancelJob ;
const abortController$1 = new abortController . AbortController ( ) ;
const handlers = new Map ( ) ;
const handleProgressEvents = async ( ) => handlers . forEach ( ( h ) => h ( state ) ) ;
2023-02-22 17:47:24 -08:00
const cancelErrMsg = "Operation was canceled" ;
2022-10-29 10:03:51 +02:00
let currentPollIntervalInMs = intervalInMs ;
const poller = {
getOperationState : ( ) => state ,
getResult : ( ) => state . result ,
isDone : ( ) => [ "succeeded" , "failed" , "canceled" ] . includes ( state . status ) ,
isStopped : ( ) => resultPromise === undefined ,
stopPolling : ( ) => {
abortController$1 . abort ( ) ;
cancelJob === null || cancelJob === void 0 ? void 0 : cancelJob ( ) ;
} ,
toString : ( ) => JSON . stringify ( {
state ,
} ) ,
onProgress : ( callback ) => {
const s = Symbol ( ) ;
handlers . set ( s , callback ) ;
return ( ) => handlers . delete ( s ) ;
} ,
pollUntilDone : ( pollOptions ) => ( resultPromise !== null && resultPromise !== void 0 ? resultPromise : ( resultPromise = ( async ( ) => {
const { abortSignal : inputAbortSignal } = pollOptions || { } ;
const { signal : abortSignal } = inputAbortSignal
? new abortController . AbortController ( [ inputAbortSignal , abortController$1 . signal ] )
: abortController$1 ;
if ( ! poller . isDone ( ) ) {
await poller . poll ( { abortSignal } ) ;
while ( ! poller . isDone ( ) ) {
const delay = delayMs ( currentPollIntervalInMs ) ;
cancelJob = delay . cancel ;
await delay ;
await poller . poll ( { abortSignal } ) ;
}
}
2023-02-22 17:47:24 -08:00
if ( resolveOnUnsuccessful ) {
return poller . getResult ( ) ;
}
else {
switch ( state . status ) {
case "succeeded" :
return poller . getResult ( ) ;
case "canceled" :
throw new Error ( cancelErrMsg ) ;
case "failed" :
2022-10-29 10:03:51 +02:00
throw state . error ;
2023-02-22 17:47:24 -08:00
case "notStarted" :
case "running" :
throw new Error ( ` Polling completed without succeeding or failing ` ) ;
2022-10-29 10:03:51 +02:00
}
}
} ) ( ) . finally ( ( ) => {
resultPromise = undefined ;
} ) ) ) ,
async poll ( pollOptions ) {
2023-02-22 17:47:24 -08:00
if ( resolveOnUnsuccessful ) {
if ( poller . isDone ( ) )
return ;
}
else {
switch ( state . status ) {
case "succeeded" :
return ;
case "canceled" :
throw new Error ( cancelErrMsg ) ;
case "failed" :
throw state . error ;
}
}
2022-10-29 10:03:51 +02:00
await pollOperation ( {
poll ,
state ,
stateProxy ,
getOperationLocation ,
2023-02-22 17:47:24 -08:00
isOperationError ,
2022-10-29 10:03:51 +02:00
withOperationLocation ,
getPollingInterval ,
getOperationStatus : getStatusFromPollResponse ,
getResourceLocation ,
processResult ,
updateState ,
options : pollOptions ,
setDelay : ( pollIntervalInMs ) => {
currentPollIntervalInMs = pollIntervalInMs ;
} ,
setErrorAsResult : ! resolveOnUnsuccessful ,
} ) ;
await handleProgressEvents ( ) ;
2023-02-22 17:47:24 -08:00
if ( ! resolveOnUnsuccessful ) {
switch ( state . status ) {
case "canceled" :
throw new Error ( cancelErrMsg ) ;
case "failed" :
throw state . error ;
}
2022-10-29 10:03:51 +02:00
}
} ,
} ;
return poller ;
} ;
}
// Copyright (c) Microsoft Corporation.
/ * *
* Creates a poller that can be used to poll a long - running operation .
* @ param lro - Description of the long - running operation
* @ param options - options to configure the poller
* @ returns an initialized poller
* /
async function createHttpPoller ( lro , options ) {
const { resourceLocationConfig , intervalInMs , processResult , restoreFrom , updateState , withOperationLocation , resolveOnUnsuccessful = false , } = options || { } ;
return buildCreatePoller ( {
getStatusFromInitialResponse ,
getStatusFromPollResponse : getOperationStatus ,
2023-02-22 17:47:24 -08:00
isOperationError ,
2022-10-29 10:03:51 +02:00
getOperationLocation ,
getResourceLocation ,
getPollingInterval : parseRetryAfter ,
resolveOnUnsuccessful ,
} ) ( {
init : async ( ) => {
const response = await lro . sendInitialRequest ( ) ;
const config = inferLroMode ( {
rawResponse : response . rawResponse ,
requestPath : lro . requestPath ,
requestMethod : lro . requestMethod ,
resourceLocationConfig ,
} ) ;
return Object . assign ( { response , operationLocation : config === null || config === void 0 ? void 0 : config . operationLocation , resourceLocation : config === null || config === void 0 ? void 0 : config . resourceLocation } , ( ( config === null || config === void 0 ? void 0 : config . mode ) ? { metadata : { mode : config . mode } } : { } ) ) ;
} ,
poll : lro . sendPollRequest ,
} , {
intervalInMs ,
withOperationLocation ,
restoreFrom ,
updateState ,
processResult : processResult
? ( { flatResponse } , state ) => processResult ( flatResponse , state )
: ( { flatResponse } ) => flatResponse ,
} ) ;
}
// Copyright (c) Microsoft Corporation.
const createStateProxy = ( ) => ( {
initState : ( config ) => ( { config , isStarted : true } ) ,
setCanceled : ( state ) => ( state . isCancelled = true ) ,
setError : ( state , error ) => ( state . error = error ) ,
setResult : ( state , result ) => ( state . result = result ) ,
setRunning : ( state ) => ( state . isStarted = true ) ,
setSucceeded : ( state ) => ( state . isCompleted = true ) ,
setFailed : ( ) => {
/** empty body */
} ,
getError : ( state ) => state . error ,
getResult : ( state ) => state . result ,
isCanceled : ( state ) => ! ! state . isCancelled ,
isFailed : ( state ) => ! ! state . error ,
isRunning : ( state ) => ! ! state . isStarted ,
isSucceeded : ( state ) => Boolean ( state . isCompleted && ! state . isCancelled && ! state . error ) ,
} ) ;
class GenericPollOperation {
constructor ( state , lro , setErrorAsResult , lroResourceLocationConfig , processResult , updateState , isDone ) {
this . state = state ;
this . lro = lro ;
this . setErrorAsResult = setErrorAsResult ;
this . lroResourceLocationConfig = lroResourceLocationConfig ;
this . processResult = processResult ;
this . updateState = updateState ;
this . isDone = isDone ;
}
setPollerConfig ( pollerConfig ) {
this . pollerConfig = pollerConfig ;
}
async update ( options ) {
var _a ;
const stateProxy = createStateProxy ( ) ;
if ( ! this . state . isStarted ) {
this . state = Object . assign ( Object . assign ( { } , this . state ) , ( await initHttpOperation ( {
lro : this . lro ,
stateProxy ,
resourceLocationConfig : this . lroResourceLocationConfig ,
processResult : this . processResult ,
setErrorAsResult : this . setErrorAsResult ,
} ) ) ) ;
}
const updateState = this . updateState ;
const isDone = this . isDone ;
if ( ! this . state . isCompleted && this . state . error === undefined ) {
await pollHttpOperation ( {
lro : this . lro ,
state : this . state ,
stateProxy ,
processResult : this . processResult ,
updateState : updateState
? ( state , { rawResponse } ) => updateState ( state , rawResponse )
: undefined ,
isDone : isDone
? ( { flatResponse } , state ) => isDone ( flatResponse , state )
: undefined ,
options ,
setDelay : ( intervalInMs ) => {
this . pollerConfig . intervalInMs = intervalInMs ;
} ,
setErrorAsResult : this . setErrorAsResult ,
} ) ;
}
( _a = options === null || options === void 0 ? void 0 : options . fireProgress ) === null || _a === void 0 ? void 0 : _a . call ( options , this . state ) ;
return this ;
}
async cancel ( ) {
logger . error ( "`cancelOperation` is deprecated because it wasn't implemented" ) ;
return this ;
}
/ * *
* Serializes the Poller operation .
* /
toString ( ) {
return JSON . stringify ( {
state : this . state ,
} ) ;
}
}
2022-07-12 09:00:22 +02:00
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
/ * *
* When a poller is manually stopped through the ` stopPolling ` method ,
* the poller will be rejected with an instance of the PollerStoppedError .
* /
class PollerStoppedError extends Error {
constructor ( message ) {
super ( message ) ;
this . name = "PollerStoppedError" ;
Object . setPrototypeOf ( this , PollerStoppedError . prototype ) ;
}
}
/ * *
2022-10-29 10:03:51 +02:00
* When the operation is cancelled , the poller will be rejected with an instance
* of the PollerCancelledError .
2022-07-12 09:00:22 +02:00
* /
class PollerCancelledError extends Error {
constructor ( message ) {
super ( message ) ;
this . name = "PollerCancelledError" ;
Object . setPrototypeOf ( this , PollerCancelledError . prototype ) ;
}
}
/ * *
* A class that represents the definition of a program that polls through consecutive requests
* until it reaches a state of completion .
*
* A poller can be executed manually , by polling request by request by calling to the ` poll() ` method repeatedly , until its operation is completed .
* It also provides a way to wait until the operation completes , by calling ` pollUntilDone() ` and waiting until the operation finishes .
* Pollers can also request the cancellation of the ongoing process to whom is providing the underlying long running operation .
*
* ` ` ` ts
* const poller = new MyPoller ( ) ;
*
* // Polling just once:
* await poller . poll ( ) ;
*
* // We can try to cancel the request here, by calling:
* //
* // await poller.cancelOperation();
* //
*
* // Getting the final result:
* const result = await poller . pollUntilDone ( ) ;
* ` ` `
*
* The Poller is defined by two types , a type representing the state of the poller , which
* must include a basic set of properties from ` PollOperationState<TResult> ` ,
* and a return type defined by ` TResult ` , which can be anything .
*
* The Poller class implements the ` PollerLike ` interface , which allows poller implementations to avoid having
* to export the Poller ' s class directly , and instead only export the already instantiated poller with the PollerLike type .
*
* ` ` ` ts
* class Client {
* public async makePoller : PollerLike < MyOperationState , MyResult > {
* const poller = new MyPoller ( { } ) ;
* // It might be preferred to return the poller after the first request is made,
* // so that some information can be obtained right away.
* await poller . poll ( ) ;
* return poller ;
* }
* }
*
* const poller : PollerLike < MyOperationState , MyResult > = myClient . makePoller ( ) ;
* ` ` `
*
* A poller can be created through its constructor , then it can be polled until it ' s completed .
* At any point in time , the state of the poller can be obtained without delay through the getOperationState method .
* At any point in time , the intermediate forms of the result type can be requested without delay .
* Once the underlying operation is marked as completed , the poller will stop and the final value will be returned .
*
* ` ` ` ts
* const poller = myClient . makePoller ( ) ;
* const state : MyOperationState = poller . getOperationState ( ) ;
*
* // The intermediate result can be obtained at any time.
* const result : MyResult | undefined = poller . getResult ( ) ;
*
* // The final result can only be obtained after the poller finishes.
* const result : MyResult = await poller . pollUntilDone ( ) ;
* ` ` `
*
* /
// eslint-disable-next-line no-use-before-define
class Poller {
/ * *
* A poller needs to be initialized by passing in at least the basic properties of the ` PollOperation<TState, TResult> ` .
*
* When writing an implementation of a Poller , this implementation needs to deal with the initialization
* of any custom state beyond the basic definition of the poller . The basic poller assumes that the poller ' s
* operation has already been defined , at least its basic properties . The code below shows how to approach
* the definition of the constructor of a new custom poller .
*
* ` ` ` ts
* export class MyPoller extends Poller < MyOperationState , string > {
* constructor ( {
* // Anything you might need outside of the basics
* } ) {
* let state : MyOperationState = {
* privateProperty : private ,
* publicProperty : public ,
* } ;
*
* const operation = {
* state ,
* update ,
* cancel ,
* toString
* }
*
* // Sending the operation to the parent's constructor.
* super ( operation ) ;
*
* // You can assign more local properties here.
* }
* }
* ` ` `
*
* Inside of this constructor , a new promise is created . This will be used to
* tell the user when the poller finishes ( see ` pollUntilDone() ` ) . The promise ' s
* resolve and reject methods are also used internally to control when to resolve
* or reject anyone waiting for the poller to finish .
*
* The constructor of a custom implementation of a poller is where any serialized version of
* a previous poller ' s operation should be deserialized into the operation sent to the
* base constructor . For example :
*
* ` ` ` ts
* export class MyPoller extends Poller < MyOperationState , string > {
* constructor (
* baseOperation : string | undefined
* ) {
* let state : MyOperationState = { } ;
* if ( baseOperation ) {
* state = {
* ... JSON . parse ( baseOperation ) . state ,
* ... state
* } ;
* }
* const operation = {
* state ,
* // ...
* }
* super ( operation ) ;
* }
* }
* ` ` `
*
* @ param operation - Must contain the basic properties of ` PollOperation<State, TResult> ` .
* /
constructor ( operation ) {
2022-10-29 10:03:51 +02:00
/** controls whether to throw an error if the operation failed or was canceled. */
this . resolveOnUnsuccessful = false ;
2022-07-12 09:00:22 +02:00
this . stopped = true ;
this . pollProgressCallbacks = [ ] ;
this . operation = operation ;
this . promise = new Promise ( ( resolve , reject ) => {
this . resolve = resolve ;
this . reject = reject ;
} ) ;
// This prevents the UnhandledPromiseRejectionWarning in node.js from being thrown.
// The above warning would get thrown if `poller.poll` is called, it returns an error,
// and pullUntilDone did not have a .catch or await try/catch on it's return value.
this . promise . catch ( ( ) => {
/* intentionally blank */
} ) ;
}
/ * *
* Starts a loop that will break only if the poller is done
* or if the poller is stopped .
* /
2022-10-29 10:03:51 +02:00
async startPolling ( pollOptions = { } ) {
2022-07-12 09:00:22 +02:00
if ( this . stopped ) {
this . stopped = false ;
}
while ( ! this . isStopped ( ) && ! this . isDone ( ) ) {
2022-10-29 10:03:51 +02:00
await this . poll ( pollOptions ) ;
2022-07-12 09:00:22 +02:00
await this . delay ( ) ;
}
}
/ * *
* pollOnce does one polling , by calling to the update method of the underlying
* poll operation to make any relevant change effective .
*
* It only optionally receives an object with an abortSignal property , from \ @ azure / abort - controller ' s AbortSignalLike .
*
* @ param options - Optional properties passed to the operation ' s update method .
* /
async pollOnce ( options = { } ) {
2022-10-29 10:03:51 +02:00
if ( ! this . isDone ( ) ) {
this . operation = await this . operation . update ( {
abortSignal : options . abortSignal ,
fireProgress : this . fireProgress . bind ( this ) ,
} ) ;
2022-07-12 09:00:22 +02:00
}
2022-10-29 10:03:51 +02:00
this . processUpdatedState ( ) ;
2022-07-12 09:00:22 +02:00
}
/ * *
* fireProgress calls the functions passed in via onProgress the method of the poller .
*
* It loops over all of the callbacks received from onProgress , and executes them , sending them
* the current operation state .
*
* @ param state - The current operation state .
* /
fireProgress ( state ) {
for ( const callback of this . pollProgressCallbacks ) {
callback ( state ) ;
}
}
/ * *
2022-10-29 10:03:51 +02:00
* Invokes the underlying operation ' s cancel method .
2022-07-12 09:00:22 +02:00
* /
async cancelOnce ( options = { } ) {
this . operation = await this . operation . cancel ( options ) ;
}
/ * *
* Returns a promise that will resolve once a single polling request finishes .
* It does this by calling the update method of the Poller ' s operation .
*
* It only optionally receives an object with an abortSignal property , from \ @ azure / abort - controller ' s AbortSignalLike .
*
* @ param options - Optional properties passed to the operation ' s update method .
* /
poll ( options = { } ) {
if ( ! this . pollOncePromise ) {
this . pollOncePromise = this . pollOnce ( options ) ;
const clearPollOncePromise = ( ) => {
this . pollOncePromise = undefined ;
} ;
this . pollOncePromise . then ( clearPollOncePromise , clearPollOncePromise ) . catch ( this . reject ) ;
}
return this . pollOncePromise ;
}
2022-10-29 10:03:51 +02:00
processUpdatedState ( ) {
if ( this . operation . state . error ) {
this . stopped = true ;
if ( ! this . resolveOnUnsuccessful ) {
this . reject ( this . operation . state . error ) ;
throw this . operation . state . error ;
}
}
if ( this . operation . state . isCancelled ) {
this . stopped = true ;
if ( ! this . resolveOnUnsuccessful ) {
const error = new PollerCancelledError ( "Operation was canceled" ) ;
this . reject ( error ) ;
throw error ;
}
}
if ( this . isDone ( ) && this . resolve ) {
// If the poller has finished polling, this means we now have a result.
// However, it can be the case that TResult is instantiated to void, so
// we are not expecting a result anyway. To assert that we might not
// have a result eventually after finishing polling, we cast the result
// to TResult.
this . resolve ( this . getResult ( ) ) ;
}
}
2022-07-12 09:00:22 +02:00
/ * *
* Returns a promise that will resolve once the underlying operation is completed .
* /
2022-10-29 10:03:51 +02:00
async pollUntilDone ( pollOptions = { } ) {
2022-07-12 09:00:22 +02:00
if ( this . stopped ) {
2022-10-29 10:03:51 +02:00
this . startPolling ( pollOptions ) . catch ( this . reject ) ;
2022-07-12 09:00:22 +02:00
}
2022-10-29 10:03:51 +02:00
// This is needed because the state could have been updated by
// `cancelOperation`, e.g. the operation is canceled or an error occurred.
this . processUpdatedState ( ) ;
2022-07-12 09:00:22 +02:00
return this . promise ;
}
/ * *
* Invokes the provided callback after each polling is completed ,
* sending the current state of the poller ' s operation .
*
* It returns a method that can be used to stop receiving updates on the given callback function .
* /
onProgress ( callback ) {
this . pollProgressCallbacks . push ( callback ) ;
return ( ) => {
this . pollProgressCallbacks = this . pollProgressCallbacks . filter ( ( c ) => c !== callback ) ;
} ;
}
/ * *
* Returns true if the poller has finished polling .
* /
isDone ( ) {
const state = this . operation . state ;
return Boolean ( state . isCompleted || state . isCancelled || state . error ) ;
}
/ * *
* Stops the poller from continuing to poll .
* /
stopPolling ( ) {
if ( ! this . stopped ) {
this . stopped = true ;
if ( this . reject ) {
this . reject ( new PollerStoppedError ( "This poller is already stopped" ) ) ;
}
}
}
/ * *
* Returns true if the poller is stopped .
* /
isStopped ( ) {
return this . stopped ;
}
/ * *
* Attempts to cancel the underlying operation .
*
* It only optionally receives an object with an abortSignal property , from \ @ azure / abort - controller ' s AbortSignalLike .
*
* If it ' s called again before it finishes , it will throw an error .
*
* @ param options - Optional properties passed to the operation ' s update method .
* /
cancelOperation ( options = { } ) {
if ( ! this . cancelPromise ) {
this . cancelPromise = this . cancelOnce ( options ) ;
}
else if ( options . abortSignal ) {
throw new Error ( "A cancel request is currently pending" ) ;
}
return this . cancelPromise ;
}
/ * *
* Returns the state of the operation .
*
* Even though TState will be the same type inside any of the methods of any extension of the Poller class ,
* implementations of the pollers can customize what ' s shared with the public by writing their own
* version of the ` getOperationState ` method , and by defining two types , one representing the internal state of the poller
* and a public type representing a safe to share subset of the properties of the internal state .
* Their definition of getOperationState can then return their public type .
*
* Example :
*
* ` ` ` ts
* // Let's say we have our poller's operation state defined as:
* interface MyOperationState extends PollOperationState < ResultType > {
* privateProperty ? : string ;
* publicProperty ? : string ;
* }
*
* // To allow us to have a true separation of public and private state, we have to define another interface:
* interface PublicState extends PollOperationState < ResultType > {
* publicProperty ? : string ;
* }
*
* // Then, we define our Poller as follows:
* export class MyPoller extends Poller < MyOperationState , ResultType > {
* // ... More content is needed here ...
*
* public getOperationState ( ) : PublicState {
* const state : PublicState = this . operation . state ;
* return {
* // Properties from PollOperationState<TResult>
* isStarted : state . isStarted ,
* isCompleted : state . isCompleted ,
* isCancelled : state . isCancelled ,
* error : state . error ,
* result : state . result ,
*
* // The only other property needed by PublicState.
* publicProperty : state . publicProperty
* }
* }
* }
* ` ` `
*
* You can see this in the tests of this repository , go to the file :
* ` ../test/utils/testPoller.ts `
* and look for the getOperationState implementation .
* /
getOperationState ( ) {
return this . operation . state ;
}
/ * *
* Returns the result value of the operation ,
* regardless of the state of the poller .
* It can return undefined or an incomplete form of the final TResult value
* depending on the implementation .
* /
getResult ( ) {
const state = this . operation . state ;
return state . result ;
}
/ * *
* Returns a serialized version of the poller ' s operation
* by invoking the operation ' s toString method .
* /
toString ( ) {
return this . operation . toString ( ) ;
}
}
// Copyright (c) Microsoft Corporation.
/ * *
* The LRO Engine , a class that performs polling .
* /
class LroEngine extends Poller {
constructor ( lro , options ) {
2022-10-29 10:03:51 +02:00
const { intervalInMs = POLL _INTERVAL _IN _MS , resumeFrom , resolveOnUnsuccessful = false , isDone , lroResourceLocationConfig , processResult , updateState , } = options || { } ;
2022-07-12 09:00:22 +02:00
const state = resumeFrom
? deserializeState ( resumeFrom )
: { } ;
2022-10-29 10:03:51 +02:00
const operation = new GenericPollOperation ( state , lro , ! resolveOnUnsuccessful , lroResourceLocationConfig , processResult , updateState , isDone ) ;
2022-07-12 09:00:22 +02:00
super ( operation ) ;
2022-10-29 10:03:51 +02:00
this . resolveOnUnsuccessful = resolveOnUnsuccessful ;
2022-07-12 09:00:22 +02:00
this . config = { intervalInMs : intervalInMs } ;
operation . setPollerConfig ( this . config ) ;
}
/ * *
* The method used by the poller to wait before attempting to update its operation .
* /
delay ( ) {
return new Promise ( ( resolve ) => setTimeout ( ( ) => resolve ( ) , this . config . intervalInMs ) ) ;
}
}
exports . LroEngine = LroEngine ;
exports . Poller = Poller ;
exports . PollerCancelledError = PollerCancelledError ;
exports . PollerStoppedError = PollerStoppedError ;
2022-10-29 10:03:51 +02:00
exports . createHttpPoller = createHttpPoller ;
2022-07-12 09:00:22 +02:00
//# sourceMappingURL=index.js.map