@azure/core-lro
Version:
Isomorphic client library for supporting long-running operations in node.js and browser.
264 lines • 9.86 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { pollOperation } from "../poller/operation.js";
import { logger } from "../logger.js";
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) {
var _a;
const { location, requestMethod, requestPath, resourceLocationConfig, skipFinalGet } = inputs;
// If skipFinalGet is true, return undefined to skip the final GET request
if (skipFinalGet) {
return undefined;
}
switch (requestMethod) {
case "PUT": {
return requestPath;
}
case "DELETE": {
return undefined;
}
case "PATCH": {
return (_a = getDefault()) !== null && _a !== void 0 ? _a : requestPath;
}
default: {
return getDefault();
}
}
function getDefault() {
switch (resourceLocationConfig) {
case "operation-location":
case "azure-async-operation": {
return undefined;
}
case "original-uri": {
return requestPath;
}
case "location":
default: {
return location;
}
}
}
}
export function inferLroMode(rawResponse, resourceLocationConfig, skipFinalGet) {
const requestPath = rawResponse.request.url;
const requestMethod = rawResponse.request.method;
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,
skipFinalGet,
}),
initialRequestUrl: requestPath,
requestMethod,
};
}
else if (location !== undefined) {
return {
mode: "ResourceLocation",
operationLocation: location,
initialRequestUrl: requestPath,
requestMethod,
};
}
else if (normalizedRequestMethod === "PUT" && requestPath) {
return {
mode: "Body",
operationLocation: requestPath,
initialRequestUrl: requestPath,
requestMethod,
};
}
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.`);
}
logger.verbose(`LRO: Transforming status: ${status} with status code: ${statusCode}.`);
const lowerCaseStatus = status === null || status === void 0 ? void 0 : status.toLocaleLowerCase();
if (!lowerCaseStatus) {
return toOperationStatus(statusCode);
}
if (lowerCaseStatus.includes("succeeded")) {
return "succeeded";
}
if (lowerCaseStatus.includes("fail")) {
return "failed";
}
if (["canceled", "cancelled"].includes(lowerCaseStatus)) {
return "canceled";
}
return "running";
}
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";
}
}
export 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;
}
export function getErrorFromResponse(response) {
const error = accessBodyProperty(response, "error");
if (!error) {
logger.warning(`The long-running operation failed but there is no error property in the response's body`);
return;
}
if (!error.code || !error.message) {
logger.warning(`The long-running operation failed but the error property in the response's body doesn't contain code or message`);
return;
}
return error;
}
function calculatePollingIntervalFromDate(retryAfterDate) {
const timeNow = Math.floor(new Date().getTime());
const retryAfterTime = retryAfterDate.getTime();
if (timeNow < retryAfterTime) {
return retryAfterTime - timeNow;
}
return undefined;
}
export 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;
}
export 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;
}
}
}
export 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 accessBodyProperty({ flatResponse, rawResponse }, prop) {
var _a, _b;
return (_a = flatResponse === null || flatResponse === void 0 ? void 0 : flatResponse[prop]) !== null && _a !== void 0 ? _a : (_b = rawResponse.body) === null || _b === void 0 ? void 0 : _b[prop];
}
export function getResourceLocation(res, state) {
const loc = accessBodyProperty(res, "resourceLocation");
if (loc && typeof loc === "string") {
state.config.resourceLocation = loc;
}
return state.config.resourceLocation;
}
export function isOperationError(e) {
return e.name === "RestError";
}
/** Polls the long-running operation. */
export async function pollHttpOperation(inputs) {
const { lro, options, processResult, updateState, setDelay, state, setErrorAsResult } = inputs;
return pollOperation({
state,
setDelay,
processResult: processResult
? ({ flatResponse }, inputState) => processResult(flatResponse, inputState)
: ({ flatResponse }) => flatResponse,
getError: getErrorFromResponse,
updateState,
getPollingInterval: parseRetryAfter,
getOperationLocation,
getOperationStatus,
isOperationError,
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,
});
}
//# sourceMappingURL=operation.js.map