@azure/core-lro 2.3.0-beta.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +720 -391
- package/dist/index.js.map +1 -1
- package/dist-esm/src/{lroEngine → http}/models.js +0 -0
- package/dist-esm/src/http/models.js.map +1 -0
- package/dist-esm/src/http/operation.js +228 -0
- package/dist-esm/src/http/operation.js.map +1 -0
- package/dist-esm/src/http/poller.js +47 -0
- package/dist-esm/src/http/poller.js.map +1 -0
- package/dist-esm/src/index.js +16 -3
- package/dist-esm/src/index.js.map +1 -1
- package/dist-esm/src/{lroEngine → legacy/lroEngine}/index.js +0 -0
- package/dist-esm/src/legacy/lroEngine/index.js.map +1 -0
- package/dist-esm/src/{lroEngine → legacy/lroEngine}/lroEngine.js +4 -10
- package/dist-esm/src/legacy/lroEngine/lroEngine.js.map +1 -0
- package/dist-esm/src/legacy/lroEngine/models.js +4 -0
- package/dist-esm/src/legacy/lroEngine/models.js.map +1 -0
- package/dist-esm/src/legacy/lroEngine/operation.js +81 -0
- package/dist-esm/src/legacy/lroEngine/operation.js.map +1 -0
- package/dist-esm/src/legacy/models.js +4 -0
- package/dist-esm/src/legacy/models.js.map +1 -0
- package/dist-esm/src/{pollOperation.js → legacy/pollOperation.js} +0 -0
- package/dist-esm/src/legacy/pollOperation.js.map +1 -0
- package/dist-esm/src/{poller.js → legacy/poller.js} +36 -30
- package/dist-esm/src/legacy/poller.js.map +1 -0
- package/dist-esm/src/{lroEngine/logger.js → logger.js} +0 -0
- package/dist-esm/src/logger.js.map +1 -0
- package/dist-esm/src/poller/constants.js +11 -0
- package/dist-esm/src/poller/constants.js.map +1 -0
- package/dist-esm/src/poller/models.js +4 -0
- package/dist-esm/src/poller/models.js.map +1 -0
- package/dist-esm/src/poller/operation.js +131 -0
- package/dist-esm/src/poller/operation.js.map +1 -0
- package/dist-esm/src/poller/poller.js +140 -0
- package/dist-esm/src/poller/poller.js.map +1 -0
- package/dist-esm/src/poller/util/delayMs.js +52 -0
- package/dist-esm/src/poller/util/delayMs.js.map +1 -0
- package/package.json +3 -8
- package/types/core-lro.d.ts +145 -21
- package/dist-esm/src/lroEngine/bodyPolling.js +0 -21
- package/dist-esm/src/lroEngine/bodyPolling.js.map +0 -1
- package/dist-esm/src/lroEngine/index.js.map +0 -1
- package/dist-esm/src/lroEngine/locationPolling.js +0 -50
- package/dist-esm/src/lroEngine/locationPolling.js.map +0 -1
- package/dist-esm/src/lroEngine/logger.js.map +0 -1
- package/dist-esm/src/lroEngine/lroEngine.js.map +0 -1
- package/dist-esm/src/lroEngine/models.js.map +0 -1
- package/dist-esm/src/lroEngine/operation.js +0 -101
- package/dist-esm/src/lroEngine/operation.js.map +0 -1
- package/dist-esm/src/lroEngine/passthrough.js +0 -6
- package/dist-esm/src/lroEngine/passthrough.js.map +0 -1
- package/dist-esm/src/lroEngine/requestUtils.js +0 -102
- package/dist-esm/src/lroEngine/requestUtils.js.map +0 -1
- package/dist-esm/src/lroEngine/stateMachine.js +0 -78
- package/dist-esm/src/lroEngine/stateMachine.js.map +0 -1
- package/dist-esm/src/pollOperation.js.map +0 -1
- package/dist-esm/src/poller.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -3,6 +3,687 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var logger$1 = require('@azure/logger');
|
|
6
|
+
var abortController = require('@azure/abort-controller');
|
|
7
|
+
|
|
8
|
+
// Copyright (c) Microsoft Corporation.
|
|
9
|
+
/**
|
|
10
|
+
* The `@azure/logger` configuration for this package.
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
const logger = logger$1.createClientLogger("core-lro");
|
|
14
|
+
|
|
15
|
+
// Copyright (c) Microsoft Corporation.
|
|
16
|
+
// Licensed under the MIT license.
|
|
17
|
+
/**
|
|
18
|
+
* The default time interval to wait before sending the next polling request.
|
|
19
|
+
*/
|
|
20
|
+
const POLL_INTERVAL_IN_MS = 2000;
|
|
21
|
+
/**
|
|
22
|
+
* The closed set of terminal states.
|
|
23
|
+
*/
|
|
24
|
+
const terminalStates = ["succeeded", "canceled", "failed"];
|
|
25
|
+
|
|
26
|
+
// Copyright (c) Microsoft Corporation.
|
|
27
|
+
/**
|
|
28
|
+
* Deserializes the state
|
|
29
|
+
*/
|
|
30
|
+
function deserializeState(serializedState) {
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(serializedState).state;
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
throw new Error(`Unable to deserialize input state: ${serializedState}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function setStateError(inputs) {
|
|
39
|
+
const { state, stateProxy } = inputs;
|
|
40
|
+
return (error) => {
|
|
41
|
+
stateProxy.setError(state, error);
|
|
42
|
+
stateProxy.setFailed(state);
|
|
43
|
+
throw error;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function processOperationStatus(result) {
|
|
47
|
+
const { state, stateProxy, status } = result;
|
|
48
|
+
logger.verbose(`LRO: Status:\n\tPolling from: ${state.config.operationLocation}\n\tOperation status: ${status}\n\tPolling status: ${terminalStates.includes(status) ? "Stopped" : "Running"}`);
|
|
49
|
+
switch (status) {
|
|
50
|
+
case "succeeded": {
|
|
51
|
+
stateProxy.setSucceeded(state);
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
case "failed": {
|
|
55
|
+
stateProxy.setError(state, new Error(`The long-running operation has failed`));
|
|
56
|
+
stateProxy.setFailed(state);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
case "canceled": {
|
|
60
|
+
stateProxy.setCanceled(state);
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function buildResult(inputs) {
|
|
66
|
+
const { processResult, response, state } = inputs;
|
|
67
|
+
return processResult ? processResult(response, state) : response;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Initiates the long-running operation.
|
|
71
|
+
*/
|
|
72
|
+
async function initOperation(inputs) {
|
|
73
|
+
const { init, stateProxy, processResult, getOperationStatus, withOperationLocation } = inputs;
|
|
74
|
+
const { operationLocation, resourceLocation, metadata, response } = await init();
|
|
75
|
+
if (operationLocation)
|
|
76
|
+
withOperationLocation === null || withOperationLocation === void 0 ? void 0 : withOperationLocation(operationLocation, false);
|
|
77
|
+
const config = {
|
|
78
|
+
metadata,
|
|
79
|
+
operationLocation,
|
|
80
|
+
resourceLocation,
|
|
81
|
+
};
|
|
82
|
+
logger.verbose(`LRO: Operation description:`, config);
|
|
83
|
+
const state = stateProxy.initState(config);
|
|
84
|
+
const status = getOperationStatus(response, state);
|
|
85
|
+
if (status === "succeeded" || operationLocation === undefined) {
|
|
86
|
+
stateProxy.setSucceeded(state);
|
|
87
|
+
stateProxy.setResult(state, buildResult({
|
|
88
|
+
response,
|
|
89
|
+
state,
|
|
90
|
+
processResult,
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
return state;
|
|
94
|
+
}
|
|
95
|
+
async function pollOperationHelper(inputs) {
|
|
96
|
+
const { poll, state, stateProxy, operationLocation, resourceLocation, getOperationStatus, options, } = inputs;
|
|
97
|
+
const response = await poll(operationLocation, options).catch(setStateError({
|
|
98
|
+
state,
|
|
99
|
+
stateProxy,
|
|
100
|
+
}));
|
|
101
|
+
const status = getOperationStatus(response, state);
|
|
102
|
+
processOperationStatus({
|
|
103
|
+
status,
|
|
104
|
+
state,
|
|
105
|
+
stateProxy,
|
|
106
|
+
});
|
|
107
|
+
if (status === "succeeded" && resourceLocation !== undefined) {
|
|
108
|
+
return {
|
|
109
|
+
response: await poll(resourceLocation).catch(setStateError({ state, stateProxy })),
|
|
110
|
+
status,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return { response, status };
|
|
114
|
+
}
|
|
115
|
+
/** Polls the long-running operation. */
|
|
116
|
+
async function pollOperation(inputs) {
|
|
117
|
+
const { poll, state, stateProxy, options, getOperationStatus, getOperationLocation, withOperationLocation, getPollingInterval, processResult, updateState, setDelay, isDone, } = inputs;
|
|
118
|
+
const { operationLocation, resourceLocation } = state.config;
|
|
119
|
+
if (operationLocation !== undefined) {
|
|
120
|
+
const { response, status } = await pollOperationHelper({
|
|
121
|
+
poll,
|
|
122
|
+
getOperationStatus,
|
|
123
|
+
state,
|
|
124
|
+
stateProxy,
|
|
125
|
+
operationLocation,
|
|
126
|
+
resourceLocation,
|
|
127
|
+
options,
|
|
128
|
+
});
|
|
129
|
+
if ((isDone === null || isDone === void 0 ? void 0 : isDone(response, state)) ||
|
|
130
|
+
(isDone === undefined && ["succeeded", "canceled"].includes(status))) {
|
|
131
|
+
stateProxy.setResult(state, buildResult({
|
|
132
|
+
response,
|
|
133
|
+
state,
|
|
134
|
+
processResult,
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
const intervalInMs = getPollingInterval === null || getPollingInterval === void 0 ? void 0 : getPollingInterval(response);
|
|
139
|
+
if (intervalInMs)
|
|
140
|
+
setDelay(intervalInMs);
|
|
141
|
+
const location = getOperationLocation === null || getOperationLocation === void 0 ? void 0 : getOperationLocation(response, state);
|
|
142
|
+
if (location !== undefined) {
|
|
143
|
+
const isUpdated = operationLocation !== location;
|
|
144
|
+
state.config.operationLocation = location;
|
|
145
|
+
withOperationLocation === null || withOperationLocation === void 0 ? void 0 : withOperationLocation(location, isUpdated);
|
|
146
|
+
}
|
|
147
|
+
else
|
|
148
|
+
withOperationLocation === null || withOperationLocation === void 0 ? void 0 : withOperationLocation(operationLocation, false);
|
|
149
|
+
}
|
|
150
|
+
updateState === null || updateState === void 0 ? void 0 : updateState(state, response);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Copyright (c) Microsoft Corporation.
|
|
155
|
+
function getOperationLocationPollingUrl(inputs) {
|
|
156
|
+
const { azureAsyncOperation, operationLocation } = inputs;
|
|
157
|
+
return operationLocation !== null && operationLocation !== void 0 ? operationLocation : azureAsyncOperation;
|
|
158
|
+
}
|
|
159
|
+
function getLocationHeader(rawResponse) {
|
|
160
|
+
return rawResponse.headers["location"];
|
|
161
|
+
}
|
|
162
|
+
function getOperationLocationHeader(rawResponse) {
|
|
163
|
+
return rawResponse.headers["operation-location"];
|
|
164
|
+
}
|
|
165
|
+
function getAzureAsyncOperationHeader(rawResponse) {
|
|
166
|
+
return rawResponse.headers["azure-asyncoperation"];
|
|
167
|
+
}
|
|
168
|
+
function findResourceLocation(inputs) {
|
|
169
|
+
const { location, requestMethod, requestPath, resourceLocationConfig } = inputs;
|
|
170
|
+
switch (requestMethod) {
|
|
171
|
+
case "PUT": {
|
|
172
|
+
return requestPath;
|
|
173
|
+
}
|
|
174
|
+
case "DELETE": {
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
default: {
|
|
178
|
+
switch (resourceLocationConfig) {
|
|
179
|
+
case "azure-async-operation": {
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
case "original-uri": {
|
|
183
|
+
return requestPath;
|
|
184
|
+
}
|
|
185
|
+
case "location":
|
|
186
|
+
default: {
|
|
187
|
+
return location;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function inferLroMode(inputs) {
|
|
194
|
+
const { rawResponse, requestMethod, requestPath, resourceLocationConfig } = inputs;
|
|
195
|
+
const operationLocation = getOperationLocationHeader(rawResponse);
|
|
196
|
+
const azureAsyncOperation = getAzureAsyncOperationHeader(rawResponse);
|
|
197
|
+
const pollingUrl = getOperationLocationPollingUrl({ operationLocation, azureAsyncOperation });
|
|
198
|
+
const location = getLocationHeader(rawResponse);
|
|
199
|
+
const normalizedRequestMethod = requestMethod === null || requestMethod === void 0 ? void 0 : requestMethod.toLocaleUpperCase();
|
|
200
|
+
if (pollingUrl !== undefined) {
|
|
201
|
+
return {
|
|
202
|
+
mode: "OperationLocation",
|
|
203
|
+
operationLocation: pollingUrl,
|
|
204
|
+
resourceLocation: findResourceLocation({
|
|
205
|
+
requestMethod: normalizedRequestMethod,
|
|
206
|
+
location,
|
|
207
|
+
requestPath,
|
|
208
|
+
resourceLocationConfig,
|
|
209
|
+
}),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
else if (location !== undefined) {
|
|
213
|
+
return {
|
|
214
|
+
mode: "ResourceLocation",
|
|
215
|
+
operationLocation: location,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
else if (normalizedRequestMethod === "PUT" && requestPath) {
|
|
219
|
+
return {
|
|
220
|
+
mode: "Body",
|
|
221
|
+
operationLocation: requestPath,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function transformStatus(status) {
|
|
229
|
+
switch (status === null || status === void 0 ? void 0 : status.toLowerCase()) {
|
|
230
|
+
case undefined:
|
|
231
|
+
case "succeeded":
|
|
232
|
+
return "succeeded";
|
|
233
|
+
case "failed":
|
|
234
|
+
return "failed";
|
|
235
|
+
case "running":
|
|
236
|
+
case "accepted":
|
|
237
|
+
case "canceling":
|
|
238
|
+
case "cancelling":
|
|
239
|
+
return "running";
|
|
240
|
+
case "canceled":
|
|
241
|
+
case "cancelled":
|
|
242
|
+
return "canceled";
|
|
243
|
+
default: {
|
|
244
|
+
logger.warning(`LRO: unrecognized operation status: ${status}`);
|
|
245
|
+
return status;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function getStatus(rawResponse) {
|
|
250
|
+
var _a;
|
|
251
|
+
const { status } = (_a = rawResponse.body) !== null && _a !== void 0 ? _a : {};
|
|
252
|
+
return transformStatus(status);
|
|
253
|
+
}
|
|
254
|
+
function getProvisioningState(rawResponse) {
|
|
255
|
+
var _a, _b;
|
|
256
|
+
const { properties, provisioningState } = (_a = rawResponse.body) !== null && _a !== void 0 ? _a : {};
|
|
257
|
+
const state = (_b = properties === null || properties === void 0 ? void 0 : properties.provisioningState) !== null && _b !== void 0 ? _b : provisioningState;
|
|
258
|
+
return transformStatus(state);
|
|
259
|
+
}
|
|
260
|
+
function toOperationStatus(statusCode) {
|
|
261
|
+
if (statusCode === 202) {
|
|
262
|
+
return "running";
|
|
263
|
+
}
|
|
264
|
+
else if (statusCode < 300) {
|
|
265
|
+
return "succeeded";
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
return "failed";
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function parseRetryAfter({ rawResponse }) {
|
|
272
|
+
const retryAfter = rawResponse.headers["retry-after"];
|
|
273
|
+
if (retryAfter !== undefined) {
|
|
274
|
+
// Retry-After header value is either in HTTP date format, or in seconds
|
|
275
|
+
const retryAfterInSeconds = parseInt(retryAfter);
|
|
276
|
+
return isNaN(retryAfterInSeconds)
|
|
277
|
+
? calculatePollingIntervalFromDate(new Date(retryAfter))
|
|
278
|
+
: retryAfterInSeconds * 1000;
|
|
279
|
+
}
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
function calculatePollingIntervalFromDate(retryAfterDate) {
|
|
283
|
+
const timeNow = Math.floor(new Date().getTime());
|
|
284
|
+
const retryAfterTime = retryAfterDate.getTime();
|
|
285
|
+
if (timeNow < retryAfterTime) {
|
|
286
|
+
return retryAfterTime - timeNow;
|
|
287
|
+
}
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Initiates the long-running operation.
|
|
292
|
+
*/
|
|
293
|
+
async function initHttpOperation(inputs) {
|
|
294
|
+
const { stateProxy, resourceLocationConfig, processResult, lro } = inputs;
|
|
295
|
+
return initOperation({
|
|
296
|
+
init: async () => {
|
|
297
|
+
const response = await lro.sendInitialRequest();
|
|
298
|
+
const config = inferLroMode({
|
|
299
|
+
rawResponse: response.rawResponse,
|
|
300
|
+
requestPath: lro.requestPath,
|
|
301
|
+
requestMethod: lro.requestMethod,
|
|
302
|
+
resourceLocationConfig,
|
|
303
|
+
});
|
|
304
|
+
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 } } : {}));
|
|
305
|
+
},
|
|
306
|
+
stateProxy,
|
|
307
|
+
processResult: processResult
|
|
308
|
+
? ({ flatResponse }, state) => processResult(flatResponse, state)
|
|
309
|
+
: ({ flatResponse }) => flatResponse,
|
|
310
|
+
getOperationStatus: (response, state) => {
|
|
311
|
+
var _a;
|
|
312
|
+
const mode = (_a = state.config.metadata) === null || _a === void 0 ? void 0 : _a["mode"];
|
|
313
|
+
return mode === undefined ||
|
|
314
|
+
(mode === "Body" && getOperationStatus(response, state) === "succeeded")
|
|
315
|
+
? "succeeded"
|
|
316
|
+
: "running";
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
function getOperationLocation({ rawResponse }, state) {
|
|
321
|
+
var _a;
|
|
322
|
+
const mode = (_a = state.config.metadata) === null || _a === void 0 ? void 0 : _a["mode"];
|
|
323
|
+
switch (mode) {
|
|
324
|
+
case "OperationLocation": {
|
|
325
|
+
return getOperationLocationPollingUrl({
|
|
326
|
+
operationLocation: getOperationLocationHeader(rawResponse),
|
|
327
|
+
azureAsyncOperation: getAzureAsyncOperationHeader(rawResponse),
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
case "ResourceLocation": {
|
|
331
|
+
return getLocationHeader(rawResponse);
|
|
332
|
+
}
|
|
333
|
+
case "Body":
|
|
334
|
+
default: {
|
|
335
|
+
return undefined;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function getOperationStatus({ rawResponse }, state) {
|
|
340
|
+
var _a;
|
|
341
|
+
const mode = (_a = state.config.metadata) === null || _a === void 0 ? void 0 : _a["mode"];
|
|
342
|
+
switch (mode) {
|
|
343
|
+
case "OperationLocation": {
|
|
344
|
+
return getStatus(rawResponse);
|
|
345
|
+
}
|
|
346
|
+
case "ResourceLocation": {
|
|
347
|
+
return toOperationStatus(rawResponse.statusCode);
|
|
348
|
+
}
|
|
349
|
+
case "Body": {
|
|
350
|
+
return getProvisioningState(rawResponse);
|
|
351
|
+
}
|
|
352
|
+
default:
|
|
353
|
+
throw new Error(`Unexpected operation mode: ${mode}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/** Polls the long-running operation. */
|
|
357
|
+
async function pollHttpOperation(inputs) {
|
|
358
|
+
const { lro, stateProxy, options, processResult, updateState, setDelay, state } = inputs;
|
|
359
|
+
return pollOperation({
|
|
360
|
+
state,
|
|
361
|
+
stateProxy,
|
|
362
|
+
setDelay,
|
|
363
|
+
processResult: processResult
|
|
364
|
+
? ({ flatResponse }, inputState) => processResult(flatResponse, inputState)
|
|
365
|
+
: ({ flatResponse }) => flatResponse,
|
|
366
|
+
updateState,
|
|
367
|
+
getPollingInterval: parseRetryAfter,
|
|
368
|
+
getOperationLocation,
|
|
369
|
+
getOperationStatus,
|
|
370
|
+
options,
|
|
371
|
+
/**
|
|
372
|
+
* The expansion here is intentional because `lro` could be an object that
|
|
373
|
+
* references an inner this, so we need to preserve a reference to it.
|
|
374
|
+
*/
|
|
375
|
+
poll: async (location, inputOptions) => lro.sendPollRequest(location, inputOptions),
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Copyright (c) Microsoft Corporation.
|
|
380
|
+
// Licensed under the MIT license.
|
|
381
|
+
/**
|
|
382
|
+
* Map an optional value through a function
|
|
383
|
+
* @internal
|
|
384
|
+
*/
|
|
385
|
+
const maybemap = (value, f) => value === undefined ? undefined : f(value);
|
|
386
|
+
const INTERRUPTED = new Error("The poller is already stopped");
|
|
387
|
+
/**
|
|
388
|
+
* A promise that delays resolution until a certain amount of time (in milliseconds) has passed, with facilities for
|
|
389
|
+
* robust cancellation.
|
|
390
|
+
*
|
|
391
|
+
* ### Example:
|
|
392
|
+
*
|
|
393
|
+
* ```javascript
|
|
394
|
+
* let toCancel;
|
|
395
|
+
*
|
|
396
|
+
* // Wait 20 seconds, and optionally allow the function to be cancelled.
|
|
397
|
+
* await delayMs(20000, (cancel) => { toCancel = cancel });
|
|
398
|
+
*
|
|
399
|
+
* // ... if `toCancel` is called before the 20 second timer expires, then the delayMs promise will reject.
|
|
400
|
+
* ```
|
|
401
|
+
*
|
|
402
|
+
* @internal
|
|
403
|
+
* @param ms - the number of milliseconds to wait before resolving
|
|
404
|
+
* @param cb - a callback that can provide the caller with a cancellation function
|
|
405
|
+
*/
|
|
406
|
+
function delayMs(ms) {
|
|
407
|
+
let aborted = false;
|
|
408
|
+
let toReject;
|
|
409
|
+
return Object.assign(new Promise((resolve, reject) => {
|
|
410
|
+
let token;
|
|
411
|
+
toReject = () => {
|
|
412
|
+
maybemap(token, clearTimeout);
|
|
413
|
+
reject(INTERRUPTED);
|
|
414
|
+
};
|
|
415
|
+
// In the rare case that the operation is _already_ aborted, we will reject instantly. This could happen, for
|
|
416
|
+
// example, if the user calls the cancellation function immediately without yielding execution.
|
|
417
|
+
if (aborted) {
|
|
418
|
+
toReject();
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
token = setTimeout(resolve, ms);
|
|
422
|
+
}
|
|
423
|
+
}), {
|
|
424
|
+
cancel: () => {
|
|
425
|
+
aborted = true;
|
|
426
|
+
toReject === null || toReject === void 0 ? void 0 : toReject();
|
|
427
|
+
},
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Copyright (c) Microsoft Corporation.
|
|
432
|
+
const createStateProxy$1 = () => ({
|
|
433
|
+
/**
|
|
434
|
+
* The state at this point is created to be of type OperationState<TResult>.
|
|
435
|
+
* It will be updated later to be of type TState when the
|
|
436
|
+
* customer-provided callback, `updateState`, is called during polling.
|
|
437
|
+
*/
|
|
438
|
+
initState: (config) => ({ status: "running", config }),
|
|
439
|
+
setCanceled: (state) => (state.status = "canceled"),
|
|
440
|
+
setError: (state, error) => (state.error = error),
|
|
441
|
+
setResult: (state, result) => (state.result = result),
|
|
442
|
+
setRunning: (state) => (state.status = "running"),
|
|
443
|
+
setSucceeded: (state) => (state.status = "succeeded"),
|
|
444
|
+
setFailed: (state) => (state.status = "failed"),
|
|
445
|
+
getError: (state) => state.error,
|
|
446
|
+
getResult: (state) => state.result,
|
|
447
|
+
isCanceled: (state) => state.status === "canceled",
|
|
448
|
+
isFailed: (state) => state.status === "failed",
|
|
449
|
+
isRunning: (state) => state.status === "running",
|
|
450
|
+
isSucceeded: (state) => state.status === "succeeded",
|
|
451
|
+
});
|
|
452
|
+
/**
|
|
453
|
+
* Returns a poller factory.
|
|
454
|
+
*/
|
|
455
|
+
function buildCreatePoller(inputs) {
|
|
456
|
+
const { getOperationLocation, getStatusFromInitialResponse, getStatusFromPollResponse, getPollingInterval, } = inputs;
|
|
457
|
+
return async ({ init, poll }, options) => {
|
|
458
|
+
const { processResult, updateState, withOperationLocation: withOperationLocationCallback, intervalInMs = POLL_INTERVAL_IN_MS, restoreFrom, } = options || {};
|
|
459
|
+
const stateProxy = createStateProxy$1();
|
|
460
|
+
const withOperationLocation = withOperationLocationCallback
|
|
461
|
+
? (() => {
|
|
462
|
+
let called = false;
|
|
463
|
+
return (operationLocation, isUpdated) => {
|
|
464
|
+
if (isUpdated)
|
|
465
|
+
withOperationLocationCallback(operationLocation);
|
|
466
|
+
else if (!called)
|
|
467
|
+
withOperationLocationCallback(operationLocation);
|
|
468
|
+
called = true;
|
|
469
|
+
};
|
|
470
|
+
})()
|
|
471
|
+
: undefined;
|
|
472
|
+
const state = restoreFrom
|
|
473
|
+
? deserializeState(restoreFrom)
|
|
474
|
+
: await initOperation({
|
|
475
|
+
init,
|
|
476
|
+
stateProxy,
|
|
477
|
+
processResult,
|
|
478
|
+
getOperationStatus: getStatusFromInitialResponse,
|
|
479
|
+
withOperationLocation,
|
|
480
|
+
});
|
|
481
|
+
let resultPromise;
|
|
482
|
+
let cancelJob;
|
|
483
|
+
const abortController$1 = new abortController.AbortController();
|
|
484
|
+
const handlers = new Map();
|
|
485
|
+
const handleProgressEvents = async () => handlers.forEach((h) => h(state));
|
|
486
|
+
let currentPollIntervalInMs = intervalInMs;
|
|
487
|
+
const poller = {
|
|
488
|
+
getOperationState: () => state,
|
|
489
|
+
getResult: () => state.result,
|
|
490
|
+
isDone: () => ["succeeded", "failed", "canceled"].includes(state.status),
|
|
491
|
+
isStopped: () => resultPromise === undefined,
|
|
492
|
+
stopPolling: () => {
|
|
493
|
+
abortController$1.abort();
|
|
494
|
+
cancelJob === null || cancelJob === void 0 ? void 0 : cancelJob();
|
|
495
|
+
},
|
|
496
|
+
toString: () => JSON.stringify({
|
|
497
|
+
state,
|
|
498
|
+
}),
|
|
499
|
+
onProgress: (callback) => {
|
|
500
|
+
const s = Symbol();
|
|
501
|
+
handlers.set(s, callback);
|
|
502
|
+
return () => handlers.delete(s);
|
|
503
|
+
},
|
|
504
|
+
pollUntilDone: (pollOptions) => (resultPromise !== null && resultPromise !== void 0 ? resultPromise : (resultPromise = (async () => {
|
|
505
|
+
const { abortSignal: inputAbortSignal } = pollOptions || {};
|
|
506
|
+
const { signal: abortSignal } = inputAbortSignal
|
|
507
|
+
? new abortController.AbortController([inputAbortSignal, abortController$1.signal])
|
|
508
|
+
: abortController$1;
|
|
509
|
+
if (!poller.isDone()) {
|
|
510
|
+
await poller.poll({ abortSignal });
|
|
511
|
+
while (!poller.isDone()) {
|
|
512
|
+
const delay = delayMs(currentPollIntervalInMs);
|
|
513
|
+
cancelJob = delay.cancel;
|
|
514
|
+
await delay;
|
|
515
|
+
await poller.poll({ abortSignal });
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
switch (state.status) {
|
|
519
|
+
case "succeeded": {
|
|
520
|
+
return poller.getResult();
|
|
521
|
+
}
|
|
522
|
+
case "canceled": {
|
|
523
|
+
throw new Error("Operation was canceled");
|
|
524
|
+
}
|
|
525
|
+
case "failed": {
|
|
526
|
+
throw state.error;
|
|
527
|
+
}
|
|
528
|
+
case "notStarted":
|
|
529
|
+
case "running": {
|
|
530
|
+
// Unreachable
|
|
531
|
+
throw new Error(`polling completed without succeeding or failing`);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
})().finally(() => {
|
|
535
|
+
resultPromise = undefined;
|
|
536
|
+
}))),
|
|
537
|
+
async poll(pollOptions) {
|
|
538
|
+
await pollOperation({
|
|
539
|
+
poll,
|
|
540
|
+
state,
|
|
541
|
+
stateProxy,
|
|
542
|
+
getOperationLocation,
|
|
543
|
+
withOperationLocation,
|
|
544
|
+
getPollingInterval,
|
|
545
|
+
getOperationStatus: getStatusFromPollResponse,
|
|
546
|
+
processResult,
|
|
547
|
+
updateState,
|
|
548
|
+
options: pollOptions,
|
|
549
|
+
setDelay: (pollIntervalInMs) => {
|
|
550
|
+
currentPollIntervalInMs = pollIntervalInMs;
|
|
551
|
+
},
|
|
552
|
+
});
|
|
553
|
+
await handleProgressEvents();
|
|
554
|
+
if (state.status === "canceled") {
|
|
555
|
+
throw new Error("Operation was canceled");
|
|
556
|
+
}
|
|
557
|
+
if (state.status === "failed") {
|
|
558
|
+
throw state.error;
|
|
559
|
+
}
|
|
560
|
+
},
|
|
561
|
+
};
|
|
562
|
+
return poller;
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Copyright (c) Microsoft Corporation.
|
|
567
|
+
/**
|
|
568
|
+
* Creates a poller that can be used to poll a long-running operation.
|
|
569
|
+
* @param lro - Description of the long-running operation
|
|
570
|
+
* @param options - options to configure the poller
|
|
571
|
+
* @returns an initialized poller
|
|
572
|
+
*/
|
|
573
|
+
async function createHttpPoller(lro, options) {
|
|
574
|
+
const { resourceLocationConfig, intervalInMs, processResult, restoreFrom, updateState, withOperationLocation, } = options || {};
|
|
575
|
+
return buildCreatePoller({
|
|
576
|
+
getStatusFromInitialResponse: (response, state) => {
|
|
577
|
+
var _a;
|
|
578
|
+
const mode = (_a = state.config.metadata) === null || _a === void 0 ? void 0 : _a["mode"];
|
|
579
|
+
return mode === undefined ||
|
|
580
|
+
(mode === "Body" && getOperationStatus(response, state) === "succeeded")
|
|
581
|
+
? "succeeded"
|
|
582
|
+
: "running";
|
|
583
|
+
},
|
|
584
|
+
getStatusFromPollResponse: getOperationStatus,
|
|
585
|
+
getOperationLocation,
|
|
586
|
+
getPollingInterval: parseRetryAfter,
|
|
587
|
+
})({
|
|
588
|
+
init: async () => {
|
|
589
|
+
const response = await lro.sendInitialRequest();
|
|
590
|
+
const config = inferLroMode({
|
|
591
|
+
rawResponse: response.rawResponse,
|
|
592
|
+
requestPath: lro.requestPath,
|
|
593
|
+
requestMethod: lro.requestMethod,
|
|
594
|
+
resourceLocationConfig,
|
|
595
|
+
});
|
|
596
|
+
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 } } : {}));
|
|
597
|
+
},
|
|
598
|
+
poll: lro.sendPollRequest,
|
|
599
|
+
}, {
|
|
600
|
+
intervalInMs,
|
|
601
|
+
withOperationLocation,
|
|
602
|
+
restoreFrom,
|
|
603
|
+
updateState,
|
|
604
|
+
processResult: processResult
|
|
605
|
+
? ({ flatResponse }, state) => processResult(flatResponse, state)
|
|
606
|
+
: ({ flatResponse }) => flatResponse,
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Copyright (c) Microsoft Corporation.
|
|
611
|
+
const createStateProxy = () => ({
|
|
612
|
+
initState: (config) => ({ config, isStarted: true }),
|
|
613
|
+
setCanceled: (state) => (state.isCancelled = true),
|
|
614
|
+
setError: (state, error) => (state.error = error),
|
|
615
|
+
setResult: (state, result) => (state.result = result),
|
|
616
|
+
setRunning: (state) => (state.isStarted = true),
|
|
617
|
+
setSucceeded: (state) => (state.isCompleted = true),
|
|
618
|
+
setFailed: () => {
|
|
619
|
+
/** empty body */
|
|
620
|
+
},
|
|
621
|
+
getError: (state) => state.error,
|
|
622
|
+
getResult: (state) => state.result,
|
|
623
|
+
isCanceled: (state) => !!state.isCancelled,
|
|
624
|
+
isFailed: (state) => !!state.error,
|
|
625
|
+
isRunning: (state) => !!state.isStarted,
|
|
626
|
+
isSucceeded: (state) => Boolean(state.isCompleted && !state.isCancelled && !state.error),
|
|
627
|
+
});
|
|
628
|
+
class GenericPollOperation {
|
|
629
|
+
constructor(state, lro, lroResourceLocationConfig, processResult, updateState, isDone) {
|
|
630
|
+
this.state = state;
|
|
631
|
+
this.lro = lro;
|
|
632
|
+
this.lroResourceLocationConfig = lroResourceLocationConfig;
|
|
633
|
+
this.processResult = processResult;
|
|
634
|
+
this.updateState = updateState;
|
|
635
|
+
this.isDone = isDone;
|
|
636
|
+
}
|
|
637
|
+
setPollerConfig(pollerConfig) {
|
|
638
|
+
this.pollerConfig = pollerConfig;
|
|
639
|
+
}
|
|
640
|
+
async update(options) {
|
|
641
|
+
var _a;
|
|
642
|
+
const stateProxy = createStateProxy();
|
|
643
|
+
if (!this.state.isStarted) {
|
|
644
|
+
this.state = Object.assign(Object.assign({}, this.state), (await initHttpOperation({
|
|
645
|
+
lro: this.lro,
|
|
646
|
+
stateProxy,
|
|
647
|
+
resourceLocationConfig: this.lroResourceLocationConfig,
|
|
648
|
+
processResult: this.processResult,
|
|
649
|
+
})));
|
|
650
|
+
}
|
|
651
|
+
const updateState = this.updateState;
|
|
652
|
+
const isDone = this.isDone;
|
|
653
|
+
if (!this.state.isCompleted) {
|
|
654
|
+
await pollHttpOperation({
|
|
655
|
+
lro: this.lro,
|
|
656
|
+
state: this.state,
|
|
657
|
+
stateProxy,
|
|
658
|
+
processResult: this.processResult,
|
|
659
|
+
updateState: updateState
|
|
660
|
+
? (state, { rawResponse }) => updateState(state, rawResponse)
|
|
661
|
+
: undefined,
|
|
662
|
+
isDone: isDone
|
|
663
|
+
? ({ flatResponse }, state) => isDone(flatResponse, state)
|
|
664
|
+
: undefined,
|
|
665
|
+
options,
|
|
666
|
+
setDelay: (intervalInMs) => {
|
|
667
|
+
this.pollerConfig.intervalInMs = intervalInMs;
|
|
668
|
+
},
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
(_a = options === null || options === void 0 ? void 0 : options.fireProgress) === null || _a === void 0 ? void 0 : _a.call(options, this.state);
|
|
672
|
+
return this;
|
|
673
|
+
}
|
|
674
|
+
async cancel() {
|
|
675
|
+
logger.error("`cancelOperation` is deprecated because it wasn't implemented");
|
|
676
|
+
return this;
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Serializes the Poller operation.
|
|
680
|
+
*/
|
|
681
|
+
toString() {
|
|
682
|
+
return JSON.stringify({
|
|
683
|
+
state: this.state,
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
}
|
|
6
687
|
|
|
7
688
|
// Copyright (c) Microsoft Corporation.
|
|
8
689
|
// Licensed under the MIT license.
|
|
@@ -18,8 +699,8 @@ class PollerStoppedError extends Error {
|
|
|
18
699
|
}
|
|
19
700
|
}
|
|
20
701
|
/**
|
|
21
|
-
* When
|
|
22
|
-
*
|
|
702
|
+
* When the operation is cancelled, the poller will be rejected with an instance
|
|
703
|
+
* of the PollerCancelledError.
|
|
23
704
|
*/
|
|
24
705
|
class PollerCancelledError extends Error {
|
|
25
706
|
constructor(message) {
|
|
@@ -175,12 +856,12 @@ class Poller {
|
|
|
175
856
|
* Starts a loop that will break only if the poller is done
|
|
176
857
|
* or if the poller is stopped.
|
|
177
858
|
*/
|
|
178
|
-
async startPolling() {
|
|
859
|
+
async startPolling(pollOptions = {}) {
|
|
179
860
|
if (this.stopped) {
|
|
180
861
|
this.stopped = false;
|
|
181
862
|
}
|
|
182
863
|
while (!this.isStopped() && !this.isDone()) {
|
|
183
|
-
await this.poll();
|
|
864
|
+
await this.poll(pollOptions);
|
|
184
865
|
await this.delay();
|
|
185
866
|
}
|
|
186
867
|
}
|
|
@@ -193,29 +874,18 @@ class Poller {
|
|
|
193
874
|
* @param options - Optional properties passed to the operation's update method.
|
|
194
875
|
*/
|
|
195
876
|
async pollOnce(options = {}) {
|
|
196
|
-
|
|
197
|
-
|
|
877
|
+
if (!this.isDone()) {
|
|
878
|
+
try {
|
|
198
879
|
this.operation = await this.operation.update({
|
|
199
880
|
abortSignal: options.abortSignal,
|
|
200
881
|
fireProgress: this.fireProgress.bind(this),
|
|
201
882
|
});
|
|
202
|
-
if (this.isDone() && this.resolve) {
|
|
203
|
-
// If the poller has finished polling, this means we now have a result.
|
|
204
|
-
// However, it can be the case that TResult is instantiated to void, so
|
|
205
|
-
// we are not expecting a result anyway. To assert that we might not
|
|
206
|
-
// have a result eventually after finishing polling, we cast the result
|
|
207
|
-
// to TResult.
|
|
208
|
-
this.resolve(this.operation.state.result);
|
|
209
|
-
}
|
|
210
883
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
this.operation.state.error = e;
|
|
214
|
-
if (this.reject) {
|
|
215
|
-
this.reject(e);
|
|
884
|
+
catch (e) {
|
|
885
|
+
this.operation.state.error = e;
|
|
216
886
|
}
|
|
217
|
-
throw e;
|
|
218
887
|
}
|
|
888
|
+
this.processUpdatedState();
|
|
219
889
|
}
|
|
220
890
|
/**
|
|
221
891
|
* fireProgress calls the functions passed in via onProgress the method of the poller.
|
|
@@ -231,14 +901,10 @@ class Poller {
|
|
|
231
901
|
}
|
|
232
902
|
}
|
|
233
903
|
/**
|
|
234
|
-
* Invokes the underlying operation's cancel method
|
|
235
|
-
* pollUntilDone promise.
|
|
904
|
+
* Invokes the underlying operation's cancel method.
|
|
236
905
|
*/
|
|
237
906
|
async cancelOnce(options = {}) {
|
|
238
907
|
this.operation = await this.operation.cancel(options);
|
|
239
|
-
if (this.reject) {
|
|
240
|
-
this.reject(new PollerCancelledError("Poller cancelled"));
|
|
241
|
-
}
|
|
242
908
|
}
|
|
243
909
|
/**
|
|
244
910
|
* Returns a promise that will resolve once a single polling request finishes.
|
|
@@ -258,13 +924,37 @@ class Poller {
|
|
|
258
924
|
}
|
|
259
925
|
return this.pollOncePromise;
|
|
260
926
|
}
|
|
927
|
+
processUpdatedState() {
|
|
928
|
+
if (this.operation.state.error) {
|
|
929
|
+
this.stopped = true;
|
|
930
|
+
this.reject(this.operation.state.error);
|
|
931
|
+
throw this.operation.state.error;
|
|
932
|
+
}
|
|
933
|
+
if (this.operation.state.isCancelled) {
|
|
934
|
+
this.stopped = true;
|
|
935
|
+
const error = new PollerCancelledError("Operation was canceled");
|
|
936
|
+
this.reject(error);
|
|
937
|
+
throw error;
|
|
938
|
+
}
|
|
939
|
+
else if (this.isDone() && this.resolve) {
|
|
940
|
+
// If the poller has finished polling, this means we now have a result.
|
|
941
|
+
// However, it can be the case that TResult is instantiated to void, so
|
|
942
|
+
// we are not expecting a result anyway. To assert that we might not
|
|
943
|
+
// have a result eventually after finishing polling, we cast the result
|
|
944
|
+
// to TResult.
|
|
945
|
+
this.resolve(this.operation.state.result);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
261
948
|
/**
|
|
262
949
|
* Returns a promise that will resolve once the underlying operation is completed.
|
|
263
950
|
*/
|
|
264
|
-
async pollUntilDone() {
|
|
951
|
+
async pollUntilDone(pollOptions = {}) {
|
|
265
952
|
if (this.stopped) {
|
|
266
|
-
this.startPolling().catch(this.reject);
|
|
953
|
+
this.startPolling(pollOptions).catch(this.reject);
|
|
267
954
|
}
|
|
955
|
+
// This is needed because the state could have been updated by
|
|
956
|
+
// `cancelOperation`, e.g. the operation is canceled or an error occurred.
|
|
957
|
+
this.processUpdatedState();
|
|
268
958
|
return this.promise;
|
|
269
959
|
}
|
|
270
960
|
/**
|
|
@@ -313,9 +1003,6 @@ class Poller {
|
|
|
313
1003
|
* @param options - Optional properties passed to the operation's update method.
|
|
314
1004
|
*/
|
|
315
1005
|
cancelOperation(options = {}) {
|
|
316
|
-
if (!this.stopped) {
|
|
317
|
-
this.stopped = true;
|
|
318
|
-
}
|
|
319
1006
|
if (!this.cancelPromise) {
|
|
320
1007
|
this.cancelPromise = this.cancelOnce(options);
|
|
321
1008
|
}
|
|
@@ -395,375 +1082,16 @@ class Poller {
|
|
|
395
1082
|
}
|
|
396
1083
|
|
|
397
1084
|
// Copyright (c) Microsoft Corporation.
|
|
398
|
-
// Licensed under the MIT license.
|
|
399
|
-
/**
|
|
400
|
-
* Detects where the continuation token is and returns it. Notice that azure-asyncoperation
|
|
401
|
-
* must be checked first before the other location headers because there are scenarios
|
|
402
|
-
* where both azure-asyncoperation and location could be present in the same response but
|
|
403
|
-
* azure-asyncoperation should be the one to use for polling.
|
|
404
|
-
*/
|
|
405
|
-
function getPollingUrl(rawResponse, defaultPath) {
|
|
406
|
-
var _a, _b, _c;
|
|
407
|
-
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);
|
|
408
|
-
}
|
|
409
|
-
function getLocation(rawResponse) {
|
|
410
|
-
return rawResponse.headers["location"];
|
|
411
|
-
}
|
|
412
|
-
function getOperationLocation(rawResponse) {
|
|
413
|
-
return rawResponse.headers["operation-location"];
|
|
414
|
-
}
|
|
415
|
-
function getAzureAsyncOperation(rawResponse) {
|
|
416
|
-
return rawResponse.headers["azure-asyncoperation"];
|
|
417
|
-
}
|
|
418
|
-
function findResourceLocation(requestMethod, rawResponse, requestPath) {
|
|
419
|
-
switch (requestMethod) {
|
|
420
|
-
case "PUT": {
|
|
421
|
-
return requestPath;
|
|
422
|
-
}
|
|
423
|
-
case "POST":
|
|
424
|
-
case "PATCH": {
|
|
425
|
-
return getLocation(rawResponse);
|
|
426
|
-
}
|
|
427
|
-
default: {
|
|
428
|
-
return undefined;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
function inferLroMode(requestPath, requestMethod, rawResponse) {
|
|
433
|
-
if (getAzureAsyncOperation(rawResponse) !== undefined ||
|
|
434
|
-
getOperationLocation(rawResponse) !== undefined) {
|
|
435
|
-
return {
|
|
436
|
-
mode: "Location",
|
|
437
|
-
resourceLocation: findResourceLocation(requestMethod, rawResponse, requestPath),
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
else if (getLocation(rawResponse) !== undefined) {
|
|
441
|
-
return {
|
|
442
|
-
mode: "Location",
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
else if (["PUT", "PATCH"].includes(requestMethod)) {
|
|
446
|
-
return {
|
|
447
|
-
mode: "Body",
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
return {};
|
|
451
|
-
}
|
|
452
|
-
class SimpleRestError extends Error {
|
|
453
|
-
constructor(message, statusCode) {
|
|
454
|
-
super(message);
|
|
455
|
-
this.name = "RestError";
|
|
456
|
-
this.statusCode = statusCode;
|
|
457
|
-
Object.setPrototypeOf(this, SimpleRestError.prototype);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
function isUnexpectedInitialResponse(rawResponse) {
|
|
461
|
-
const code = rawResponse.statusCode;
|
|
462
|
-
if (![203, 204, 202, 201, 200, 500].includes(code)) {
|
|
463
|
-
throw new SimpleRestError(`Received unexpected HTTP status code ${code} in the initial response. This may indicate a server issue.`, code);
|
|
464
|
-
}
|
|
465
|
-
return false;
|
|
466
|
-
}
|
|
467
|
-
function isUnexpectedPollingResponse(rawResponse) {
|
|
468
|
-
const code = rawResponse.statusCode;
|
|
469
|
-
if (![202, 201, 200, 500].includes(code)) {
|
|
470
|
-
throw new SimpleRestError(`Received unexpected HTTP status code ${code} while polling. This may indicate a server issue.`, code);
|
|
471
|
-
}
|
|
472
|
-
return false;
|
|
473
|
-
}
|
|
474
|
-
function isCanceled(operation) {
|
|
475
|
-
const { state, status } = operation;
|
|
476
|
-
if (["canceled", "cancelled"].includes(status)) {
|
|
477
|
-
state.isCancelled = true;
|
|
478
|
-
throw new Error(`The long-running operation has been canceled.`);
|
|
479
|
-
}
|
|
480
|
-
return false;
|
|
481
|
-
}
|
|
482
|
-
function isSucceededStatus(status) {
|
|
483
|
-
return status === "succeeded";
|
|
484
|
-
}
|
|
485
|
-
function isPollingDone(result) {
|
|
486
|
-
const { rawResponse, status } = result;
|
|
487
|
-
if (isUnexpectedPollingResponse(rawResponse) || status === "failed") {
|
|
488
|
-
throw new Error(`The long-running operation has failed.`);
|
|
489
|
-
}
|
|
490
|
-
return isSucceededStatus(status);
|
|
491
|
-
}
|
|
492
|
-
function getProvisioningState(rawResponse) {
|
|
493
|
-
var _a, _b;
|
|
494
|
-
const { properties, provisioningState } = (_a = rawResponse.body) !== null && _a !== void 0 ? _a : {};
|
|
495
|
-
const state = (_b = properties === null || properties === void 0 ? void 0 : properties.provisioningState) !== null && _b !== void 0 ? _b : provisioningState;
|
|
496
|
-
return typeof state === "string" ? state.toLowerCase() : "succeeded";
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Copyright (c) Microsoft Corporation.
|
|
500
|
-
/**
|
|
501
|
-
* The `@azure/logger` configuration for this package.
|
|
502
|
-
* @internal
|
|
503
|
-
*/
|
|
504
|
-
const logger = logger$1.createClientLogger("core-lro");
|
|
505
|
-
|
|
506
|
-
// Copyright (c) Microsoft Corporation.
|
|
507
|
-
/**
|
|
508
|
-
* Creates a polling strategy based on BodyPolling which uses the provisioning state
|
|
509
|
-
* from the result to determine the current operation state
|
|
510
|
-
*/
|
|
511
|
-
function processBodyPollingOperationResult(state) {
|
|
512
|
-
return (response) => {
|
|
513
|
-
const status = getProvisioningState(response.rawResponse);
|
|
514
|
-
return Object.assign(Object.assign({}, response), { done: isCanceled({
|
|
515
|
-
state,
|
|
516
|
-
status,
|
|
517
|
-
}) ||
|
|
518
|
-
isPollingDone({
|
|
519
|
-
rawResponse: response.rawResponse,
|
|
520
|
-
status,
|
|
521
|
-
}) });
|
|
522
|
-
};
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// Copyright (c) Microsoft Corporation.
|
|
526
|
-
function getStatus(rawResponse) {
|
|
527
|
-
var _a;
|
|
528
|
-
const { status } = (_a = rawResponse.body) !== null && _a !== void 0 ? _a : {};
|
|
529
|
-
return typeof status === "string" ? status.toLowerCase() : "succeeded";
|
|
530
|
-
}
|
|
531
|
-
function isLocationPollingDone(rawResponse, status) {
|
|
532
|
-
if (rawResponse.statusCode === 202) {
|
|
533
|
-
return false;
|
|
534
|
-
}
|
|
535
|
-
return isPollingDone({ rawResponse, status });
|
|
536
|
-
}
|
|
537
|
-
/**
|
|
538
|
-
* Sends a request to the URI of the provisioned resource if needed.
|
|
539
|
-
*/
|
|
540
|
-
async function sendFinalRequest(lro, resourceLocation, lroResourceLocationConfig) {
|
|
541
|
-
switch (lroResourceLocationConfig) {
|
|
542
|
-
case "original-uri":
|
|
543
|
-
return lro.sendPollRequest(lro.requestPath);
|
|
544
|
-
case "azure-async-operation":
|
|
545
|
-
return undefined;
|
|
546
|
-
case "location":
|
|
547
|
-
default:
|
|
548
|
-
return lro.sendPollRequest(resourceLocation !== null && resourceLocation !== void 0 ? resourceLocation : lro.requestPath);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
function processLocationPollingOperationResult(lro, state, resourceLocation, lroResourceLocationConfig) {
|
|
552
|
-
return (response) => {
|
|
553
|
-
const status = getStatus(response.rawResponse);
|
|
554
|
-
if (isCanceled({
|
|
555
|
-
state,
|
|
556
|
-
status,
|
|
557
|
-
}) ||
|
|
558
|
-
isLocationPollingDone(response.rawResponse, status)) {
|
|
559
|
-
if (resourceLocation === undefined) {
|
|
560
|
-
return Object.assign(Object.assign({}, response), { done: true });
|
|
561
|
-
}
|
|
562
|
-
else {
|
|
563
|
-
return Object.assign(Object.assign({}, response), { done: false, next: async () => {
|
|
564
|
-
const finalResponse = await sendFinalRequest(lro, resourceLocation, lroResourceLocationConfig);
|
|
565
|
-
return Object.assign(Object.assign({}, (finalResponse !== null && finalResponse !== void 0 ? finalResponse : response)), { done: true });
|
|
566
|
-
} });
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
return Object.assign(Object.assign({}, response), { done: false });
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// Copyright (c) Microsoft Corporation.
|
|
574
|
-
// Licensed under the MIT license.
|
|
575
|
-
function processPassthroughOperationResult(response) {
|
|
576
|
-
return Object.assign(Object.assign({}, response), { done: true });
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// Copyright (c) Microsoft Corporation.
|
|
580
|
-
/**
|
|
581
|
-
* creates a stepping function that maps an LRO state to another.
|
|
582
|
-
*/
|
|
583
|
-
function createGetLroStatusFromResponse(lroPrimitives, config, state, lroResourceLocationConfig) {
|
|
584
|
-
switch (config.mode) {
|
|
585
|
-
case "Location": {
|
|
586
|
-
return processLocationPollingOperationResult(lroPrimitives, state, config.resourceLocation, lroResourceLocationConfig);
|
|
587
|
-
}
|
|
588
|
-
case "Body": {
|
|
589
|
-
return processBodyPollingOperationResult(state);
|
|
590
|
-
}
|
|
591
|
-
default: {
|
|
592
|
-
return processPassthroughOperationResult;
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
/**
|
|
597
|
-
* Creates a polling operation.
|
|
598
|
-
*/
|
|
599
|
-
function createPoll(lroPrimitives) {
|
|
600
|
-
return async (path, pollerConfig, getLroStatusFromResponse) => {
|
|
601
|
-
const response = await lroPrimitives.sendPollRequest(path);
|
|
602
|
-
const retryAfter = response.rawResponse.headers["retry-after"];
|
|
603
|
-
if (retryAfter !== undefined) {
|
|
604
|
-
// Retry-After header value is either in HTTP date format, or in seconds
|
|
605
|
-
const retryAfterInSeconds = parseInt(retryAfter);
|
|
606
|
-
pollerConfig.intervalInMs = isNaN(retryAfterInSeconds)
|
|
607
|
-
? calculatePollingIntervalFromDate(new Date(retryAfter), pollerConfig.intervalInMs)
|
|
608
|
-
: retryAfterInSeconds * 1000;
|
|
609
|
-
}
|
|
610
|
-
return getLroStatusFromResponse(response);
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
function calculatePollingIntervalFromDate(retryAfterDate, defaultIntervalInMs) {
|
|
614
|
-
const timeNow = Math.floor(new Date().getTime());
|
|
615
|
-
const retryAfterTime = retryAfterDate.getTime();
|
|
616
|
-
if (timeNow < retryAfterTime) {
|
|
617
|
-
return retryAfterTime - timeNow;
|
|
618
|
-
}
|
|
619
|
-
return defaultIntervalInMs;
|
|
620
|
-
}
|
|
621
|
-
/**
|
|
622
|
-
* Creates a callback to be used to initialize the polling operation state.
|
|
623
|
-
* @param state - of the polling operation
|
|
624
|
-
* @param operationSpec - of the LRO
|
|
625
|
-
* @param callback - callback to be called when the operation is done
|
|
626
|
-
* @returns callback that initializes the state of the polling operation
|
|
627
|
-
*/
|
|
628
|
-
function createInitializeState(state, requestPath, requestMethod) {
|
|
629
|
-
return (response) => {
|
|
630
|
-
if (isUnexpectedInitialResponse(response.rawResponse))
|
|
631
|
-
;
|
|
632
|
-
state.initialRawResponse = response.rawResponse;
|
|
633
|
-
state.isStarted = true;
|
|
634
|
-
state.pollingURL = getPollingUrl(state.initialRawResponse, requestPath);
|
|
635
|
-
state.config = inferLroMode(requestPath, requestMethod, state.initialRawResponse);
|
|
636
|
-
/** short circuit polling if body polling is done in the initial request */
|
|
637
|
-
if (state.config.mode === undefined ||
|
|
638
|
-
(state.config.mode === "Body" &&
|
|
639
|
-
isPollingDone({
|
|
640
|
-
rawResponse: state.initialRawResponse,
|
|
641
|
-
status: getProvisioningState(state.initialRawResponse),
|
|
642
|
-
}))) {
|
|
643
|
-
state.result = response.flatResponse;
|
|
644
|
-
state.isCompleted = true;
|
|
645
|
-
}
|
|
646
|
-
logger.verbose(`LRO: initial state: ${JSON.stringify(state)}`);
|
|
647
|
-
return Boolean(state.isCompleted);
|
|
648
|
-
};
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// Copyright (c) Microsoft Corporation.
|
|
652
|
-
class GenericPollOperation {
|
|
653
|
-
constructor(state, lro, lroResourceLocationConfig, processResult, updateState, isDone, cancelOp) {
|
|
654
|
-
this.state = state;
|
|
655
|
-
this.lro = lro;
|
|
656
|
-
this.lroResourceLocationConfig = lroResourceLocationConfig;
|
|
657
|
-
this.processResult = processResult;
|
|
658
|
-
this.updateState = updateState;
|
|
659
|
-
this.isDone = isDone;
|
|
660
|
-
this.cancelOp = cancelOp;
|
|
661
|
-
}
|
|
662
|
-
setPollerConfig(pollerConfig) {
|
|
663
|
-
this.pollerConfig = pollerConfig;
|
|
664
|
-
}
|
|
665
|
-
/**
|
|
666
|
-
* General update function for LROPoller, the general process is as follows
|
|
667
|
-
* 1. Check initial operation result to determine the strategy to use
|
|
668
|
-
* - Strategies: Location, Azure-AsyncOperation, Original Uri
|
|
669
|
-
* 2. Check if the operation result has a terminal state
|
|
670
|
-
* - Terminal state will be determined by each strategy
|
|
671
|
-
* 2.1 If it is terminal state Check if a final GET request is required, if so
|
|
672
|
-
* send final GET request and return result from operation. If no final GET
|
|
673
|
-
* is required, just return the result from operation.
|
|
674
|
-
* - Determining what to call for final request is responsibility of each strategy
|
|
675
|
-
* 2.2 If it is not terminal state, call the polling operation and go to step 1
|
|
676
|
-
* - Determining what to call for polling is responsibility of each strategy
|
|
677
|
-
* - Strategies will always use the latest URI for polling if provided otherwise
|
|
678
|
-
* the last known one
|
|
679
|
-
*/
|
|
680
|
-
async update(options) {
|
|
681
|
-
var _a, _b, _c;
|
|
682
|
-
const state = this.state;
|
|
683
|
-
let lastResponse = undefined;
|
|
684
|
-
if (!state.isStarted) {
|
|
685
|
-
const initializeState = createInitializeState(state, this.lro.requestPath, this.lro.requestMethod);
|
|
686
|
-
lastResponse = await this.lro.sendInitialRequest();
|
|
687
|
-
initializeState(lastResponse);
|
|
688
|
-
}
|
|
689
|
-
if (!state.isCompleted) {
|
|
690
|
-
if (!this.poll || !this.getLroStatusFromResponse) {
|
|
691
|
-
if (!state.config) {
|
|
692
|
-
throw new Error("Bad state: LRO mode is undefined. Please check if the serialized state is well-formed.");
|
|
693
|
-
}
|
|
694
|
-
const isDone = this.isDone;
|
|
695
|
-
this.getLroStatusFromResponse = isDone
|
|
696
|
-
? (response) => (Object.assign(Object.assign({}, response), { done: isDone(response.flatResponse, this.state) }))
|
|
697
|
-
: createGetLroStatusFromResponse(this.lro, state.config, this.state, this.lroResourceLocationConfig);
|
|
698
|
-
this.poll = createPoll(this.lro);
|
|
699
|
-
}
|
|
700
|
-
if (!state.pollingURL) {
|
|
701
|
-
throw new Error("Bad state: polling URL is undefined. Please check if the serialized state is well-formed.");
|
|
702
|
-
}
|
|
703
|
-
const currentState = await this.poll(state.pollingURL, this.pollerConfig, this.getLroStatusFromResponse);
|
|
704
|
-
logger.verbose(`LRO: polling response: ${JSON.stringify(currentState.rawResponse)}`);
|
|
705
|
-
if (currentState.done) {
|
|
706
|
-
state.result = this.processResult
|
|
707
|
-
? this.processResult(currentState.flatResponse, state)
|
|
708
|
-
: currentState.flatResponse;
|
|
709
|
-
state.isCompleted = true;
|
|
710
|
-
}
|
|
711
|
-
else {
|
|
712
|
-
this.poll = (_a = currentState.next) !== null && _a !== void 0 ? _a : this.poll;
|
|
713
|
-
state.pollingURL = getPollingUrl(currentState.rawResponse, state.pollingURL);
|
|
714
|
-
}
|
|
715
|
-
lastResponse = currentState;
|
|
716
|
-
}
|
|
717
|
-
logger.verbose(`LRO: current state: ${JSON.stringify(state)}`);
|
|
718
|
-
if (lastResponse) {
|
|
719
|
-
(_b = this.updateState) === null || _b === void 0 ? void 0 : _b.call(this, state, lastResponse === null || lastResponse === void 0 ? void 0 : lastResponse.rawResponse);
|
|
720
|
-
}
|
|
721
|
-
else {
|
|
722
|
-
logger.error(`LRO: no response was received`);
|
|
723
|
-
}
|
|
724
|
-
(_c = options === null || options === void 0 ? void 0 : options.fireProgress) === null || _c === void 0 ? void 0 : _c.call(options, state);
|
|
725
|
-
return this;
|
|
726
|
-
}
|
|
727
|
-
async cancel() {
|
|
728
|
-
var _a;
|
|
729
|
-
await ((_a = this.cancelOp) === null || _a === void 0 ? void 0 : _a.call(this, this.state));
|
|
730
|
-
/**
|
|
731
|
-
* When `cancelOperation` is called, polling stops so it is important that
|
|
732
|
-
* `isCancelled` is set now because the polling logic will not be able to
|
|
733
|
-
* set it itself because it will not fire.
|
|
734
|
-
*/
|
|
735
|
-
this.state.isCancelled = true;
|
|
736
|
-
return this;
|
|
737
|
-
}
|
|
738
|
-
/**
|
|
739
|
-
* Serializes the Poller operation.
|
|
740
|
-
*/
|
|
741
|
-
toString() {
|
|
742
|
-
return JSON.stringify({
|
|
743
|
-
state: this.state,
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
// Copyright (c) Microsoft Corporation.
|
|
749
|
-
function deserializeState(serializedState) {
|
|
750
|
-
try {
|
|
751
|
-
return JSON.parse(serializedState).state;
|
|
752
|
-
}
|
|
753
|
-
catch (e) {
|
|
754
|
-
throw new Error(`LroEngine: Unable to deserialize state: ${serializedState}`);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
1085
|
/**
|
|
758
1086
|
* The LRO Engine, a class that performs polling.
|
|
759
1087
|
*/
|
|
760
1088
|
class LroEngine extends Poller {
|
|
761
1089
|
constructor(lro, options) {
|
|
762
|
-
const { intervalInMs =
|
|
1090
|
+
const { intervalInMs = POLL_INTERVAL_IN_MS, resumeFrom } = options || {};
|
|
763
1091
|
const state = resumeFrom
|
|
764
1092
|
? deserializeState(resumeFrom)
|
|
765
1093
|
: {};
|
|
766
|
-
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
|
|
1094
|
+
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);
|
|
767
1095
|
super(operation);
|
|
768
1096
|
this.config = { intervalInMs: intervalInMs };
|
|
769
1097
|
operation.setPollerConfig(this.config);
|
|
@@ -780,4 +1108,5 @@ exports.LroEngine = LroEngine;
|
|
|
780
1108
|
exports.Poller = Poller;
|
|
781
1109
|
exports.PollerCancelledError = PollerCancelledError;
|
|
782
1110
|
exports.PollerStoppedError = PollerStoppedError;
|
|
1111
|
+
exports.createHttpPoller = createHttpPoller;
|
|
783
1112
|
//# sourceMappingURL=index.js.map
|