mirror of
https://github.com/oven-sh/setup-bun.git
synced 2025-02-24 03:00:39 +08:00
752 lines
29 KiB
JavaScript
752 lines
29 KiB
JavaScript
'use strict';
|
|
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
|
var logger$1 = require('@azure/logger');
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
/**
|
|
* When a poller is cancelled through the `cancelOperation` method,
|
|
* the poller will be rejected with an instance of the PollerCancelledError.
|
|
*/
|
|
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) {
|
|
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.
|
|
*/
|
|
async startPolling() {
|
|
if (this.stopped) {
|
|
this.stopped = false;
|
|
}
|
|
while (!this.isStopped() && !this.isDone()) {
|
|
await this.poll();
|
|
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 = {}) {
|
|
try {
|
|
if (!this.isDone()) {
|
|
this.operation = await this.operation.update({
|
|
abortSignal: options.abortSignal,
|
|
fireProgress: this.fireProgress.bind(this),
|
|
});
|
|
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.operation.state.result);
|
|
}
|
|
}
|
|
}
|
|
catch (e) {
|
|
this.operation.state.error = e;
|
|
if (this.reject) {
|
|
this.reject(e);
|
|
}
|
|
throw e;
|
|
}
|
|
}
|
|
/**
|
|
* 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);
|
|
}
|
|
}
|
|
/**
|
|
* Invokes the underlying operation's cancel method, and rejects the
|
|
* pollUntilDone promise.
|
|
*/
|
|
async cancelOnce(options = {}) {
|
|
this.operation = await this.operation.cancel(options);
|
|
if (this.reject) {
|
|
this.reject(new PollerCancelledError("Poller cancelled"));
|
|
}
|
|
}
|
|
/**
|
|
* 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;
|
|
}
|
|
/**
|
|
* Returns a promise that will resolve once the underlying operation is completed.
|
|
*/
|
|
async pollUntilDone() {
|
|
if (this.stopped) {
|
|
this.startPolling().catch(this.reject);
|
|
}
|
|
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.stopped) {
|
|
this.stopped = true;
|
|
}
|
|
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.
|
|
// Licensed under the MIT license.
|
|
/**
|
|
* Detects where the continuation token is and returns it. Notice that azure-asyncoperation
|
|
* must be checked first before the other location headers because there are scenarios
|
|
* where both azure-asyncoperation and location could be present in the same response but
|
|
* azure-asyncoperation should be the one to use for polling.
|
|
*/
|
|
function getPollingUrl(rawResponse, defaultPath) {
|
|
var _a, _b, _c;
|
|
return ((_c = (_b = (_a = getAzureAsyncOperation(rawResponse)) !== null && _a !== void 0 ? _a : getOperationLocation(rawResponse)) !== null && _b !== void 0 ? _b : getLocation(rawResponse)) !== null && _c !== void 0 ? _c : defaultPath);
|
|
}
|
|
function getLocation(rawResponse) {
|
|
return rawResponse.headers["location"];
|
|
}
|
|
function getOperationLocation(rawResponse) {
|
|
return rawResponse.headers["operation-location"];
|
|
}
|
|
function getAzureAsyncOperation(rawResponse) {
|
|
return rawResponse.headers["azure-asyncoperation"];
|
|
}
|
|
function findResourceLocation(requestMethod, rawResponse, requestPath) {
|
|
switch (requestMethod) {
|
|
case "PUT": {
|
|
return requestPath;
|
|
}
|
|
case "POST":
|
|
case "PATCH": {
|
|
return getLocation(rawResponse);
|
|
}
|
|
default: {
|
|
return undefined;
|
|
}
|
|
}
|
|
}
|
|
function inferLroMode(requestPath, requestMethod, rawResponse) {
|
|
if (getAzureAsyncOperation(rawResponse) !== undefined ||
|
|
getOperationLocation(rawResponse) !== undefined) {
|
|
return {
|
|
mode: "Location",
|
|
resourceLocation: findResourceLocation(requestMethod, rawResponse, requestPath),
|
|
};
|
|
}
|
|
else if (getLocation(rawResponse) !== undefined) {
|
|
return {
|
|
mode: "Location",
|
|
};
|
|
}
|
|
else if (["PUT", "PATCH"].includes(requestMethod)) {
|
|
return {
|
|
mode: "Body",
|
|
};
|
|
}
|
|
return {};
|
|
}
|
|
class SimpleRestError extends Error {
|
|
constructor(message, statusCode) {
|
|
super(message);
|
|
this.name = "RestError";
|
|
this.statusCode = statusCode;
|
|
Object.setPrototypeOf(this, SimpleRestError.prototype);
|
|
}
|
|
}
|
|
function isUnexpectedInitialResponse(rawResponse) {
|
|
const code = rawResponse.statusCode;
|
|
if (![203, 204, 202, 201, 200, 500].includes(code)) {
|
|
throw new SimpleRestError(`Received unexpected HTTP status code ${code} in the initial response. This may indicate a server issue.`, code);
|
|
}
|
|
return false;
|
|
}
|
|
function isUnexpectedPollingResponse(rawResponse) {
|
|
const code = rawResponse.statusCode;
|
|
if (![202, 201, 200, 500].includes(code)) {
|
|
throw new SimpleRestError(`Received unexpected HTTP status code ${code} while polling. This may indicate a server issue.`, code);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
const successStates = ["succeeded"];
|
|
const failureStates = ["failed", "canceled", "cancelled"];
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
function getProvisioningState(rawResponse) {
|
|
var _a, _b;
|
|
const { properties, provisioningState } = (_a = rawResponse.body) !== null && _a !== void 0 ? _a : {};
|
|
const state = (_b = properties === null || properties === void 0 ? void 0 : properties.provisioningState) !== null && _b !== void 0 ? _b : provisioningState;
|
|
return typeof state === "string" ? state.toLowerCase() : "succeeded";
|
|
}
|
|
function isBodyPollingDone(rawResponse) {
|
|
const state = getProvisioningState(rawResponse);
|
|
if (isUnexpectedPollingResponse(rawResponse) || failureStates.includes(state)) {
|
|
throw new Error(`The long running operation has failed. The provisioning state: ${state}.`);
|
|
}
|
|
return successStates.includes(state);
|
|
}
|
|
/**
|
|
* Creates a polling strategy based on BodyPolling which uses the provisioning state
|
|
* from the result to determine the current operation state
|
|
*/
|
|
function processBodyPollingOperationResult(response) {
|
|
return Object.assign(Object.assign({}, response), { done: isBodyPollingDone(response.rawResponse) });
|
|
}
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
/**
|
|
* The `@azure/logger` configuration for this package.
|
|
* @internal
|
|
*/
|
|
const logger = logger$1.createClientLogger("core-lro");
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
function isPollingDone(rawResponse) {
|
|
var _a;
|
|
if (isUnexpectedPollingResponse(rawResponse) || rawResponse.statusCode === 202) {
|
|
return false;
|
|
}
|
|
const { status } = (_a = rawResponse.body) !== null && _a !== void 0 ? _a : {};
|
|
const state = typeof status === "string" ? status.toLowerCase() : "succeeded";
|
|
if (isUnexpectedPollingResponse(rawResponse) || failureStates.includes(state)) {
|
|
throw new Error(`The long running operation has failed. The provisioning state: ${state}.`);
|
|
}
|
|
return successStates.includes(state);
|
|
}
|
|
/**
|
|
* Sends a request to the URI of the provisioned resource if needed.
|
|
*/
|
|
async function sendFinalRequest(lro, resourceLocation, lroResourceLocationConfig) {
|
|
switch (lroResourceLocationConfig) {
|
|
case "original-uri":
|
|
return lro.sendPollRequest(lro.requestPath);
|
|
case "azure-async-operation":
|
|
return undefined;
|
|
case "location":
|
|
default:
|
|
return lro.sendPollRequest(resourceLocation !== null && resourceLocation !== void 0 ? resourceLocation : lro.requestPath);
|
|
}
|
|
}
|
|
function processLocationPollingOperationResult(lro, resourceLocation, lroResourceLocationConfig) {
|
|
return (response) => {
|
|
if (isPollingDone(response.rawResponse)) {
|
|
if (resourceLocation === undefined) {
|
|
return Object.assign(Object.assign({}, response), { done: true });
|
|
}
|
|
else {
|
|
return Object.assign(Object.assign({}, response), { done: false, next: async () => {
|
|
const finalResponse = await sendFinalRequest(lro, resourceLocation, lroResourceLocationConfig);
|
|
return Object.assign(Object.assign({}, (finalResponse !== null && finalResponse !== void 0 ? finalResponse : response)), { done: true });
|
|
} });
|
|
}
|
|
}
|
|
return Object.assign(Object.assign({}, response), { done: false });
|
|
};
|
|
}
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
function processPassthroughOperationResult(response) {
|
|
return Object.assign(Object.assign({}, response), { done: true });
|
|
}
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
/**
|
|
* creates a stepping function that maps an LRO state to another.
|
|
*/
|
|
function createGetLroStatusFromResponse(lroPrimitives, config, lroResourceLocationConfig) {
|
|
switch (config.mode) {
|
|
case "Location": {
|
|
return processLocationPollingOperationResult(lroPrimitives, config.resourceLocation, lroResourceLocationConfig);
|
|
}
|
|
case "Body": {
|
|
return processBodyPollingOperationResult;
|
|
}
|
|
default: {
|
|
return processPassthroughOperationResult;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Creates a polling operation.
|
|
*/
|
|
function createPoll(lroPrimitives) {
|
|
return async (path, pollerConfig, getLroStatusFromResponse) => {
|
|
const response = await lroPrimitives.sendPollRequest(path);
|
|
const retryAfter = response.rawResponse.headers["retry-after"];
|
|
if (retryAfter !== undefined) {
|
|
// Retry-After header value is either in HTTP date format, or in seconds
|
|
const retryAfterInSeconds = parseInt(retryAfter);
|
|
pollerConfig.intervalInMs = isNaN(retryAfterInSeconds)
|
|
? calculatePollingIntervalFromDate(new Date(retryAfter), pollerConfig.intervalInMs)
|
|
: retryAfterInSeconds * 1000;
|
|
}
|
|
return getLroStatusFromResponse(response);
|
|
};
|
|
}
|
|
function calculatePollingIntervalFromDate(retryAfterDate, defaultIntervalInMs) {
|
|
const timeNow = Math.floor(new Date().getTime());
|
|
const retryAfterTime = retryAfterDate.getTime();
|
|
if (timeNow < retryAfterTime) {
|
|
return retryAfterTime - timeNow;
|
|
}
|
|
return defaultIntervalInMs;
|
|
}
|
|
/**
|
|
* Creates a callback to be used to initialize the polling operation state.
|
|
* @param state - of the polling operation
|
|
* @param operationSpec - of the LRO
|
|
* @param callback - callback to be called when the operation is done
|
|
* @returns callback that initializes the state of the polling operation
|
|
*/
|
|
function createInitializeState(state, requestPath, requestMethod) {
|
|
return (response) => {
|
|
if (isUnexpectedInitialResponse(response.rawResponse))
|
|
;
|
|
state.initialRawResponse = response.rawResponse;
|
|
state.isStarted = true;
|
|
state.pollingURL = getPollingUrl(state.initialRawResponse, requestPath);
|
|
state.config = inferLroMode(requestPath, requestMethod, state.initialRawResponse);
|
|
/** short circuit polling if body polling is done in the initial request */
|
|
if (state.config.mode === undefined ||
|
|
(state.config.mode === "Body" && isBodyPollingDone(state.initialRawResponse))) {
|
|
state.result = response.flatResponse;
|
|
state.isCompleted = true;
|
|
}
|
|
logger.verbose(`LRO: initial state: ${JSON.stringify(state)}`);
|
|
return Boolean(state.isCompleted);
|
|
};
|
|
}
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
class GenericPollOperation {
|
|
constructor(state, lro, lroResourceLocationConfig, processResult, updateState, isDone) {
|
|
this.state = state;
|
|
this.lro = lro;
|
|
this.lroResourceLocationConfig = lroResourceLocationConfig;
|
|
this.processResult = processResult;
|
|
this.updateState = updateState;
|
|
this.isDone = isDone;
|
|
}
|
|
setPollerConfig(pollerConfig) {
|
|
this.pollerConfig = pollerConfig;
|
|
}
|
|
/**
|
|
* General update function for LROPoller, the general process is as follows
|
|
* 1. Check initial operation result to determine the strategy to use
|
|
* - Strategies: Location, Azure-AsyncOperation, Original Uri
|
|
* 2. Check if the operation result has a terminal state
|
|
* - Terminal state will be determined by each strategy
|
|
* 2.1 If it is terminal state Check if a final GET request is required, if so
|
|
* send final GET request and return result from operation. If no final GET
|
|
* is required, just return the result from operation.
|
|
* - Determining what to call for final request is responsibility of each strategy
|
|
* 2.2 If it is not terminal state, call the polling operation and go to step 1
|
|
* - Determining what to call for polling is responsibility of each strategy
|
|
* - Strategies will always use the latest URI for polling if provided otherwise
|
|
* the last known one
|
|
*/
|
|
async update(options) {
|
|
var _a, _b, _c;
|
|
const state = this.state;
|
|
let lastResponse = undefined;
|
|
if (!state.isStarted) {
|
|
const initializeState = createInitializeState(state, this.lro.requestPath, this.lro.requestMethod);
|
|
lastResponse = await this.lro.sendInitialRequest();
|
|
initializeState(lastResponse);
|
|
}
|
|
if (!state.isCompleted) {
|
|
if (!this.poll || !this.getLroStatusFromResponse) {
|
|
if (!state.config) {
|
|
throw new Error("Bad state: LRO mode is undefined. Please check if the serialized state is well-formed.");
|
|
}
|
|
const isDone = this.isDone;
|
|
this.getLroStatusFromResponse = isDone
|
|
? (response) => (Object.assign(Object.assign({}, response), { done: isDone(response.flatResponse, this.state) }))
|
|
: createGetLroStatusFromResponse(this.lro, state.config, this.lroResourceLocationConfig);
|
|
this.poll = createPoll(this.lro);
|
|
}
|
|
if (!state.pollingURL) {
|
|
throw new Error("Bad state: polling URL is undefined. Please check if the serialized state is well-formed.");
|
|
}
|
|
const currentState = await this.poll(state.pollingURL, this.pollerConfig, this.getLroStatusFromResponse);
|
|
logger.verbose(`LRO: polling response: ${JSON.stringify(currentState.rawResponse)}`);
|
|
if (currentState.done) {
|
|
state.result = this.processResult
|
|
? this.processResult(currentState.flatResponse, state)
|
|
: currentState.flatResponse;
|
|
state.isCompleted = true;
|
|
}
|
|
else {
|
|
this.poll = (_a = currentState.next) !== null && _a !== void 0 ? _a : this.poll;
|
|
state.pollingURL = getPollingUrl(currentState.rawResponse, state.pollingURL);
|
|
}
|
|
lastResponse = currentState;
|
|
}
|
|
logger.verbose(`LRO: current state: ${JSON.stringify(state)}`);
|
|
if (lastResponse) {
|
|
(_b = this.updateState) === null || _b === void 0 ? void 0 : _b.call(this, state, lastResponse === null || lastResponse === void 0 ? void 0 : lastResponse.rawResponse);
|
|
}
|
|
else {
|
|
logger.error(`LRO: no response was received`);
|
|
}
|
|
(_c = options === null || options === void 0 ? void 0 : options.fireProgress) === null || _c === void 0 ? void 0 : _c.call(options, state);
|
|
return this;
|
|
}
|
|
async cancel() {
|
|
this.state.isCancelled = true;
|
|
return this;
|
|
}
|
|
/**
|
|
* Serializes the Poller operation.
|
|
*/
|
|
toString() {
|
|
return JSON.stringify({
|
|
state: this.state,
|
|
});
|
|
}
|
|
}
|
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
function deserializeState(serializedState) {
|
|
try {
|
|
return JSON.parse(serializedState).state;
|
|
}
|
|
catch (e) {
|
|
throw new Error(`LroEngine: Unable to deserialize state: ${serializedState}`);
|
|
}
|
|
}
|
|
/**
|
|
* The LRO Engine, a class that performs polling.
|
|
*/
|
|
class LroEngine extends Poller {
|
|
constructor(lro, options) {
|
|
const { intervalInMs = 2000, resumeFrom } = options || {};
|
|
const state = resumeFrom
|
|
? deserializeState(resumeFrom)
|
|
: {};
|
|
const operation = new GenericPollOperation(state, lro, options === null || options === void 0 ? void 0 : options.lroResourceLocationConfig, options === null || options === void 0 ? void 0 : options.processResult, options === null || options === void 0 ? void 0 : options.updateState, options === null || options === void 0 ? void 0 : options.isDone);
|
|
super(operation);
|
|
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;
|
|
//# sourceMappingURL=index.js.map
|