@azure/core-lro 2.3.0-alpha.20220701.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.
Files changed (48) hide show
  1. package/dist/index.js +717 -382
  2. package/dist/index.js.map +1 -1
  3. package/dist-esm/src/{lroEngine → http}/models.js +0 -0
  4. package/dist-esm/src/http/models.js.map +1 -0
  5. package/dist-esm/src/http/operation.js +228 -0
  6. package/dist-esm/src/http/operation.js.map +1 -0
  7. package/dist-esm/src/http/poller.js +47 -0
  8. package/dist-esm/src/http/poller.js.map +1 -0
  9. package/dist-esm/src/index.js +16 -3
  10. package/dist-esm/src/index.js.map +1 -1
  11. package/dist-esm/src/{lroEngine → legacy/lroEngine}/index.js +0 -0
  12. package/dist-esm/src/legacy/lroEngine/index.js.map +1 -0
  13. package/dist-esm/src/{lroEngine → legacy/lroEngine}/lroEngine.js +4 -10
  14. package/dist-esm/src/legacy/lroEngine/lroEngine.js.map +1 -0
  15. package/dist-esm/src/legacy/lroEngine/models.js +4 -0
  16. package/dist-esm/src/legacy/lroEngine/models.js.map +1 -0
  17. package/dist-esm/src/legacy/lroEngine/operation.js +81 -0
  18. package/dist-esm/src/legacy/lroEngine/operation.js.map +1 -0
  19. package/dist-esm/src/legacy/models.js +4 -0
  20. package/dist-esm/src/legacy/models.js.map +1 -0
  21. package/dist-esm/src/{pollOperation.js → legacy/pollOperation.js} +0 -0
  22. package/dist-esm/src/legacy/pollOperation.js.map +1 -0
  23. package/dist-esm/src/{poller.js → legacy/poller.js} +33 -27
  24. package/dist-esm/src/legacy/poller.js.map +1 -0
  25. package/dist-esm/src/{lroEngine/logger.js → logger.js} +0 -0
  26. package/dist-esm/src/logger.js.map +1 -0
  27. package/dist-esm/src/poller/constants.js +11 -0
  28. package/dist-esm/src/poller/constants.js.map +1 -0
  29. package/dist-esm/src/poller/models.js +4 -0
  30. package/dist-esm/src/poller/models.js.map +1 -0
  31. package/dist-esm/src/poller/operation.js +131 -0
  32. package/dist-esm/src/poller/operation.js.map +1 -0
  33. package/dist-esm/src/poller/poller.js +140 -0
  34. package/dist-esm/src/poller/poller.js.map +1 -0
  35. package/dist-esm/src/poller/util/delayMs.js +52 -0
  36. package/dist-esm/src/poller/util/delayMs.js.map +1 -0
  37. package/package.json +5 -10
  38. package/types/core-lro.d.ts +142 -17
  39. package/dist-esm/src/lroEngine/impl.js +0 -233
  40. package/dist-esm/src/lroEngine/impl.js.map +0 -1
  41. package/dist-esm/src/lroEngine/index.js.map +0 -1
  42. package/dist-esm/src/lroEngine/logger.js.map +0 -1
  43. package/dist-esm/src/lroEngine/lroEngine.js.map +0 -1
  44. package/dist-esm/src/lroEngine/models.js.map +0 -1
  45. package/dist-esm/src/lroEngine/operation.js +0 -110
  46. package/dist-esm/src/lroEngine/operation.js.map +0 -1
  47. package/dist-esm/src/pollOperation.js.map +0 -1
  48. 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.
@@ -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,36 +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
- try {
197
- if (!this.isDone()) {
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.operation.state.isCancelled) {
203
- this.stopped = true;
204
- if (this.reject) {
205
- this.reject(new PollerCancelledError("Poller cancelled"));
206
- }
207
- throw new Error(`The long-running operation has been canceled.`);
208
- }
209
- else if (this.isDone() && this.resolve) {
210
- // If the poller has finished polling, this means we now have a result.
211
- // However, it can be the case that TResult is instantiated to void, so
212
- // we are not expecting a result anyway. To assert that we might not
213
- // have a result eventually after finishing polling, we cast the result
214
- // to TResult.
215
- this.resolve(this.operation.state.result);
216
- }
217
883
  }
218
- }
219
- catch (e) {
220
- this.operation.state.error = e;
221
- if (this.reject) {
222
- this.reject(e);
884
+ catch (e) {
885
+ this.operation.state.error = e;
223
886
  }
224
- throw e;
225
887
  }
888
+ this.processUpdatedState();
226
889
  }
227
890
  /**
228
891
  * fireProgress calls the functions passed in via onProgress the method of the poller.
@@ -261,13 +924,37 @@ class Poller {
261
924
  }
262
925
  return this.pollOncePromise;
263
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
+ }
264
948
  /**
265
949
  * Returns a promise that will resolve once the underlying operation is completed.
266
950
  */
267
- async pollUntilDone() {
951
+ async pollUntilDone(pollOptions = {}) {
268
952
  if (this.stopped) {
269
- this.startPolling().catch(this.reject);
953
+ this.startPolling(pollOptions).catch(this.reject);
270
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();
271
958
  return this.promise;
272
959
  }
273
960
  /**
@@ -395,369 +1082,16 @@ class Poller {
395
1082
  }
396
1083
 
397
1084
  // Copyright (c) Microsoft Corporation.
398
- /**
399
- * The `@azure/logger` configuration for this package.
400
- * @internal
401
- */
402
- const logger = logger$1.createClientLogger("core-lro");
403
-
404
- // Copyright (c) Microsoft Corporation.
405
- function throwIfUndefined(input, options = {}) {
406
- var _a;
407
- if (input === undefined) {
408
- throw new Error((_a = options.errorMessage) !== null && _a !== void 0 ? _a : "undefined variable");
409
- }
410
- return input;
411
- }
412
- function updatePollingUrl(inputs) {
413
- var _a, _b;
414
- const { info, rawResponse } = inputs;
415
- switch (info.mode) {
416
- case "OperationLocation": {
417
- const operationLocation = getOperationLocation(rawResponse);
418
- const azureAsyncOperation = getAzureAsyncOperation(rawResponse);
419
- info.pollingUrl =
420
- (_a = getOperationLocationPollingUrl({ operationLocation, azureAsyncOperation })) !== null && _a !== void 0 ? _a : throwIfUndefined(info.pollingUrl);
421
- break;
422
- }
423
- case "ResourceLocation": {
424
- info.pollingUrl = (_b = getLocation(rawResponse)) !== null && _b !== void 0 ? _b : throwIfUndefined(info.pollingUrl);
425
- break;
426
- }
427
- }
428
- }
429
- function getOperationLocationPollingUrl(inputs) {
430
- const { azureAsyncOperation, operationLocation } = inputs;
431
- return operationLocation !== null && operationLocation !== void 0 ? operationLocation : azureAsyncOperation;
432
- }
433
- function getLocation(rawResponse) {
434
- return rawResponse.headers["location"];
435
- }
436
- function getOperationLocation(rawResponse) {
437
- return rawResponse.headers["operation-location"];
438
- }
439
- function getAzureAsyncOperation(rawResponse) {
440
- return rawResponse.headers["azure-asyncoperation"];
441
- }
442
- function findResourceLocation(inputs) {
443
- const { location, requestMethod, requestPath, lroResourceLocationConfig } = inputs;
444
- switch (requestMethod) {
445
- case "PUT": {
446
- return requestPath;
447
- }
448
- case "DELETE": {
449
- return undefined;
450
- }
451
- default: {
452
- switch (lroResourceLocationConfig) {
453
- case "azure-async-operation": {
454
- return undefined;
455
- }
456
- case "original-uri": {
457
- return requestPath;
458
- }
459
- case "location":
460
- default: {
461
- return location;
462
- }
463
- }
464
- }
465
- }
466
- }
467
- function inferLroMode(inputs) {
468
- const { rawResponse, requestMethod, requestPath, lroResourceLocationConfig } = inputs;
469
- const operationLocation = getOperationLocation(rawResponse);
470
- const azureAsyncOperation = getAzureAsyncOperation(rawResponse);
471
- const location = getLocation(rawResponse);
472
- if (operationLocation !== undefined || azureAsyncOperation !== undefined) {
473
- return {
474
- mode: "OperationLocation",
475
- pollingUrl: operationLocation !== null && operationLocation !== void 0 ? operationLocation : azureAsyncOperation,
476
- resourceLocation: findResourceLocation({
477
- requestMethod,
478
- location,
479
- requestPath,
480
- lroResourceLocationConfig,
481
- }),
482
- };
483
- }
484
- else if (location !== undefined) {
485
- return {
486
- mode: "ResourceLocation",
487
- pollingUrl: location,
488
- };
489
- }
490
- else if (requestMethod === "PUT") {
491
- return {
492
- mode: "Body",
493
- pollingUrl: requestPath,
494
- };
495
- }
496
- else {
497
- return {
498
- mode: "None",
499
- };
500
- }
501
- }
502
- class SimpleRestError extends Error {
503
- constructor(message, statusCode) {
504
- super(message);
505
- this.name = "RestError";
506
- this.statusCode = statusCode;
507
- Object.setPrototypeOf(this, SimpleRestError.prototype);
508
- }
509
- }
510
- function throwIfError(rawResponse) {
511
- const code = rawResponse.statusCode;
512
- if (code >= 400) {
513
- throw new SimpleRestError(`Received unexpected HTTP status code ${code} while polling. This may indicate a server issue.`, code);
514
- }
515
- }
516
- function getStatus(rawResponse) {
517
- var _a;
518
- const { status } = (_a = rawResponse.body) !== null && _a !== void 0 ? _a : {};
519
- return typeof status === "string" ? status.toLowerCase() : "succeeded";
520
- }
521
- function getProvisioningState(rawResponse) {
522
- var _a, _b;
523
- const { properties, provisioningState } = (_a = rawResponse.body) !== null && _a !== void 0 ? _a : {};
524
- const state = (_b = properties === null || properties === void 0 ? void 0 : properties.provisioningState) !== null && _b !== void 0 ? _b : provisioningState;
525
- return typeof state === "string" ? state.toLowerCase() : "succeeded";
526
- }
527
- function isCanceled(operation) {
528
- const { state, status } = operation;
529
- if (["canceled", "cancelled"].includes(status)) {
530
- state.isCancelled = true;
531
- return true;
532
- }
533
- return false;
534
- }
535
- function isTerminal(operation) {
536
- const { state, status } = operation;
537
- if (status === "failed") {
538
- throw new Error(`The long-running operation has failed.`);
539
- }
540
- return status === "succeeded" || isCanceled({ state, status });
541
- }
542
- function isDone(result) {
543
- const { rawResponse, state, info, responseKind = "Polling" } = result;
544
- throwIfError(rawResponse);
545
- switch (info.mode) {
546
- case "OperationLocation": {
547
- return responseKind === "Polling" && isTerminal({ state, status: getStatus(rawResponse) });
548
- }
549
- case "Body": {
550
- return isTerminal({ state, status: getProvisioningState(rawResponse) });
551
- }
552
- case "ResourceLocation": {
553
- return responseKind === "Polling" && rawResponse.statusCode !== 202;
554
- }
555
- case "None": {
556
- return true;
557
- }
558
- }
559
- }
560
- /**
561
- * Creates a polling operation.
562
- */
563
- function createPoll(lroPrimitives) {
564
- return async (path, pollerConfig, getLroStatusFromResponse) => {
565
- const response = await lroPrimitives.sendPollRequest(path);
566
- const retryAfter = response.rawResponse.headers["retry-after"];
567
- if (retryAfter !== undefined) {
568
- // Retry-After header value is either in HTTP date format, or in seconds
569
- const retryAfterInSeconds = parseInt(retryAfter);
570
- pollerConfig.intervalInMs = isNaN(retryAfterInSeconds)
571
- ? calculatePollingIntervalFromDate(new Date(retryAfter), pollerConfig.intervalInMs)
572
- : retryAfterInSeconds * 1000;
573
- }
574
- return getLroStatusFromResponse(response);
575
- };
576
- }
577
- function calculatePollingIntervalFromDate(retryAfterDate, defaultIntervalInMs) {
578
- const timeNow = Math.floor(new Date().getTime());
579
- const retryAfterTime = retryAfterDate.getTime();
580
- if (timeNow < retryAfterTime) {
581
- return retryAfterTime - timeNow;
582
- }
583
- return defaultIntervalInMs;
584
- }
585
- function buildResult(inputs) {
586
- const { processResult, response, state } = inputs;
587
- return processResult ? processResult(response, state) : response;
588
- }
589
- /**
590
- * Creates a callback to be used to initialize the polling operation state.
591
- */
592
- function createStateInitializer(inputs) {
593
- const { requestMethod, requestPath, state, lroResourceLocationConfig, processResult } = inputs;
594
- return (response) => {
595
- state.initialRawResponse = response.rawResponse;
596
- state.isStarted = true;
597
- state.config = inferLroMode({
598
- rawResponse: state.initialRawResponse,
599
- requestPath,
600
- requestMethod,
601
- lroResourceLocationConfig,
602
- });
603
- /** short circuit before polling */
604
- if (isDone({
605
- rawResponse: state.initialRawResponse,
606
- state,
607
- info: state.config,
608
- responseKind: "Initial",
609
- })) {
610
- state.result = buildResult({
611
- response: response.flatResponse,
612
- state: state,
613
- processResult,
614
- });
615
- state.isCompleted = true;
616
- }
617
- logger.verbose(`LRO: initial state: ${JSON.stringify(state)}`);
618
- };
619
- }
620
- function createGetLroStatusFromResponse(inputs) {
621
- const { lro, state, info } = inputs;
622
- const location = info.resourceLocation;
623
- return (response) => {
624
- const isTerminalStatus = isDone({
625
- info,
626
- rawResponse: response.rawResponse,
627
- state,
628
- });
629
- return Object.assign(Object.assign({}, response), { done: isTerminalStatus && !location, next: !(isTerminalStatus && location)
630
- ? undefined
631
- : () => lro.sendPollRequest(location).then((res) => (Object.assign(Object.assign({}, res), { done: true }))) });
632
- };
633
- }
634
-
635
- // Copyright (c) Microsoft Corporation.
636
- class GenericPollOperation {
637
- constructor(state, lro, lroResourceLocationConfig, processResult, updateState, isDone, cancelOp) {
638
- this.state = state;
639
- this.lro = lro;
640
- this.lroResourceLocationConfig = lroResourceLocationConfig;
641
- this.processResult = processResult;
642
- this.updateState = updateState;
643
- this.isDone = isDone;
644
- this.cancelOp = cancelOp;
645
- }
646
- setPollerConfig(pollerConfig) {
647
- this.pollerConfig = pollerConfig;
648
- }
649
- /**
650
- * General update function for LROPoller, the general process is as follows
651
- * 1. Check initial operation result to determine the strategy to use
652
- * - Strategies: Location, Azure-AsyncOperation, Original Uri
653
- * 2. Check if the operation result has a terminal state
654
- * - Terminal state will be determined by each strategy
655
- * 2.1 If it is terminal state Check if a final GET request is required, if so
656
- * send final GET request and return result from operation. If no final GET
657
- * is required, just return the result from operation.
658
- * - Determining what to call for final request is responsibility of each strategy
659
- * 2.2 If it is not terminal state, call the polling operation and go to step 1
660
- * - Determining what to call for polling is responsibility of each strategy
661
- * - Strategies will always use the latest URI for polling if provided otherwise
662
- * the last known one
663
- */
664
- async update(options) {
665
- var _a, _b, _c;
666
- const state = this.state;
667
- let lastResponse = undefined;
668
- if (!state.isStarted) {
669
- const initializeState = createStateInitializer({
670
- state,
671
- requestPath: this.lro.requestPath,
672
- requestMethod: this.lro.requestMethod,
673
- lroResourceLocationConfig: this.lroResourceLocationConfig,
674
- processResult: this.processResult,
675
- });
676
- lastResponse = await this.lro.sendInitialRequest();
677
- initializeState(lastResponse);
678
- }
679
- if (!state.isCompleted) {
680
- const config = throwIfUndefined(state.config, {
681
- errorMessage: "Bad state: LRO mode is undefined. Check if the serialized state is well-formed.",
682
- });
683
- if (!this.poll) {
684
- this.poll = createPoll(this.lro);
685
- }
686
- if (!this.getLroStatusFromResponse) {
687
- const isDone = this.isDone;
688
- this.getLroStatusFromResponse = isDone
689
- ? (response) => (Object.assign(Object.assign({}, response), { done: isDone(response.flatResponse, this.state) }))
690
- : createGetLroStatusFromResponse({
691
- lro: this.lro,
692
- info: config,
693
- state: this.state,
694
- });
695
- }
696
- const currentState = await this.poll(throwIfUndefined(config.pollingUrl), this.pollerConfig, this.getLroStatusFromResponse);
697
- logger.verbose(`LRO: polling response: ${JSON.stringify(currentState.rawResponse)}`);
698
- if (currentState.done) {
699
- state.result = buildResult({
700
- response: currentState.flatResponse,
701
- state,
702
- processResult: this.processResult,
703
- });
704
- state.isCompleted = true;
705
- }
706
- else {
707
- this.poll = (_a = currentState.next) !== null && _a !== void 0 ? _a : this.poll;
708
- updatePollingUrl({
709
- rawResponse: currentState.rawResponse,
710
- info: config,
711
- });
712
- /** for backward compatability */
713
- state.pollingURL = config.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
- return this;
731
- }
732
- /**
733
- * Serializes the Poller operation.
734
- */
735
- toString() {
736
- return JSON.stringify({
737
- state: this.state,
738
- });
739
- }
740
- }
741
-
742
- // Copyright (c) Microsoft Corporation.
743
- function deserializeState(serializedState) {
744
- try {
745
- return JSON.parse(serializedState).state;
746
- }
747
- catch (e) {
748
- throw new Error(`LroEngine: Unable to deserialize state: ${serializedState}`);
749
- }
750
- }
751
1085
  /**
752
1086
  * The LRO Engine, a class that performs polling.
753
1087
  */
754
1088
  class LroEngine extends Poller {
755
1089
  constructor(lro, options) {
756
- const { intervalInMs = 2000, resumeFrom } = options || {};
1090
+ const { intervalInMs = POLL_INTERVAL_IN_MS, resumeFrom } = options || {};
757
1091
  const state = resumeFrom
758
1092
  ? deserializeState(resumeFrom)
759
1093
  : {};
760
- 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, options === null || options === void 0 ? void 0 : options.cancel);
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);
761
1095
  super(operation);
762
1096
  this.config = { intervalInMs: intervalInMs };
763
1097
  operation.setPollerConfig(this.config);
@@ -774,4 +1108,5 @@ exports.LroEngine = LroEngine;
774
1108
  exports.Poller = Poller;
775
1109
  exports.PollerCancelledError = PollerCancelledError;
776
1110
  exports.PollerStoppedError = PollerStoppedError;
1111
+ exports.createHttpPoller = createHttpPoller;
777
1112
  //# sourceMappingURL=index.js.map