UNPKG

27.4 kBJavaScriptView Raw
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT License. See License.txt in the project root for license information.
3import { __assign, __awaiter, __extends, __generator } from "tslib";
4import { delay, RestError, stripRequest, WebResource } from "@azure/ms-rest-js";
5/**
6 * A long-running operation polling strategy base class that other polling strategies should extend.
7 */
8var LROPollStrategy = /** @class */ (function () {
9 function LROPollStrategy(_azureServiceClient, _pollState) {
10 this._azureServiceClient = _azureServiceClient;
11 this._pollState = _pollState;
12 }
13 LROPollStrategy.prototype.getOperationStatus = function () {
14 return this._pollState.state;
15 };
16 /**
17 * Get whether or not this poll strategy's LRO is finished.
18 * @returns Whether or not this poll strategy's LRO is finished.
19 */
20 LROPollStrategy.prototype.isFinished = function () {
21 return isFinished(this._pollState.state);
22 };
23 /**
24 * Send poll requests that check the LRO's status until it is determined that the LRO is finished.
25 * @returns Whether or not the LRO succeeded.
26 */
27 LROPollStrategy.prototype.pollUntilFinished = function () {
28 return __awaiter(this, void 0, void 0, function () {
29 var delayInSeconds;
30 return __generator(this, function (_a) {
31 switch (_a.label) {
32 case 0:
33 if (!!this.isFinished()) return [3 /*break*/, 3];
34 delayInSeconds = getDelayInSeconds(this._azureServiceClient, this._pollState.mostRecentResponse);
35 return [4 /*yield*/, delay(delayInSeconds * 1000)];
36 case 1:
37 _a.sent();
38 return [4 /*yield*/, this.sendPollRequest()];
39 case 2:
40 _a.sent();
41 return [3 /*break*/, 0];
42 case 3: return [2 /*return*/, this.isFinalStatusAcceptable()];
43 }
44 });
45 });
46 };
47 LROPollStrategy.prototype.shouldDoFinalGetResourceRequest = function () {
48 var initialRequestMethod = this._pollState.initialResponse.request.method;
49 return !this._pollState.resource && (initialRequestMethod === "PUT" || initialRequestMethod === "PATCH" || initialRequestMethod === "POST");
50 };
51 LROPollStrategy.prototype.getMostRecentResponse = function () {
52 return this._pollState.mostRecentResponse;
53 };
54 LROPollStrategy.prototype.getOperationResponse = function () {
55 return __awaiter(this, void 0, void 0, function () {
56 var response, result, resource;
57 return __generator(this, function (_a) {
58 switch (_a.label) {
59 case 0:
60 if (!this.shouldDoFinalGetResourceRequest()) return [3 /*break*/, 2];
61 return [4 /*yield*/, this.doFinalGetResourceRequest()];
62 case 1:
63 _a.sent();
64 _a.label = 2;
65 case 2:
66 response = this._pollState.mostRecentResponse;
67 result = __assign(__assign({}, response), { headers: response.headers.clone() });
68 resource = this._pollState.resource;
69 if (!resource) {
70 result.bodyAsText = response.bodyAsText;
71 result.parsedBody = response.parsedBody;
72 }
73 else if (typeof resource.valueOf() === "string") {
74 result.bodyAsText = resource;
75 try {
76 result.parsedBody = JSON.parse(resource);
77 }
78 catch (err) {
79 // There was an error parsing the JSON. Hence we set the resource as-is. Most likely, the
80 // resource is a string that was already parsed.
81 result.parsedBody = resource;
82 }
83 }
84 else {
85 result.bodyAsText = JSON.stringify(resource);
86 result.parsedBody = resource;
87 }
88 return [2 /*return*/, result];
89 }
90 });
91 });
92 };
93 LROPollStrategy.prototype.getRestError = function () {
94 var error = new RestError("");
95 error.request = stripRequest(this._pollState.mostRecentRequest);
96 error.response = this._pollState.mostRecentResponse;
97 error.message = "Long running operation failed with status: \"" + this._pollState.state + "\".";
98 error.body = this._pollState.resource;
99 if (error.body) {
100 var innerError = error.body.error;
101 if (innerError) {
102 if (innerError.message) {
103 error.message = "Long running operation failed with error: \"" + innerError.message + "\".";
104 }
105 if (innerError.code) {
106 error.code = innerError.code;
107 }
108 }
109 }
110 return error;
111 };
112 LROPollStrategy.prototype.updateState = function (url, shouldDeserialize) {
113 var _this = this;
114 return this.updateOperationStatus(url, shouldDeserialize).then(function (result) {
115 _this._pollState.state = getProvisioningState(result.parsedBody) || "Succeeded";
116 _this._pollState.mostRecentResponse = result;
117 _this._pollState.mostRecentRequest = result.request;
118 _this._pollState.resource = getResponseBody(result);
119 }).catch(function (error) {
120 var resultStatus;
121 if (error.response && error.response.status) {
122 resultStatus = error.response.status;
123 if (_this._pollState.initialResponse.request.method !== "DELETE" || resultStatus < 400 || 499 < resultStatus) {
124 throw error;
125 }
126 }
127 else {
128 throw error;
129 }
130 });
131 };
132 /**
133 * Retrieves operation status by querying the operation URL.
134 * @param {string} statusUrl URL used to poll operation result.
135 */
136 LROPollStrategy.prototype.updateOperationStatus = function (statusUrl, shouldDeserialize) {
137 var requestUrl = statusUrl.replace(" ", "%20");
138 var httpRequest = new WebResource(requestUrl, "GET");
139 var pollState = this._pollState;
140 httpRequest.operationSpec = pollState.mostRecentRequest.operationSpec;
141 httpRequest.shouldDeserialize = shouldDeserialize;
142 httpRequest.operationResponseGetter = getOperationResponse;
143 var options = pollState.options;
144 if (options && options.customHeaders) {
145 var customHeaders = options.customHeaders;
146 for (var _i = 0, _a = Object.keys(customHeaders); _i < _a.length; _i++) {
147 var headerName = _a[_i];
148 httpRequest.headers.set(headerName, customHeaders[headerName]);
149 }
150 }
151 return this._azureServiceClient.sendRequest(httpRequest);
152 };
153 LROPollStrategy.prototype.getPollState = function () {
154 return this._pollState;
155 };
156 return LROPollStrategy;
157}());
158export { LROPollStrategy };
159function getOperationResponse(operationSpec, response) {
160 var statusCode = response.status;
161 var operationResponses = operationSpec.responses;
162 var result = operationResponses[statusCode];
163 if (!result) {
164 if (statusCode === 200) {
165 result = operationResponses[201] || operationResponses[202];
166 }
167 else if (201 <= statusCode && statusCode <= 299) {
168 result = {};
169 }
170 }
171 return result;
172}
173export function getDelayInSeconds(azureServiceClient, previousResponse) {
174 var delayInSeconds = 30;
175 if (azureServiceClient.longRunningOperationRetryTimeout != undefined) {
176 delayInSeconds = azureServiceClient.longRunningOperationRetryTimeout;
177 }
178 else {
179 var retryAfterHeaderValue = previousResponse.headers.get("retry-after");
180 if (retryAfterHeaderValue) {
181 var retryAfterDelayInSeconds = parseInt(retryAfterHeaderValue);
182 if (!Number.isNaN(retryAfterDelayInSeconds)) {
183 delayInSeconds = retryAfterDelayInSeconds;
184 }
185 }
186 }
187 return delayInSeconds;
188}
189function getProvisioningState(responseBody) {
190 var result;
191 if (responseBody) {
192 if (responseBody.provisioningState) {
193 result = responseBody.provisioningState;
194 }
195 else if (responseBody.properties) {
196 result = responseBody.properties.provisioningState;
197 }
198 }
199 return result;
200}
201function getResponseBody(response) {
202 var result;
203 try {
204 if (response.parsedBody) {
205 result = response.parsedBody;
206 }
207 else if (response.bodyAsText && response.bodyAsText.length > 0) {
208 result = JSON.parse(response.bodyAsText);
209 }
210 }
211 catch (error) {
212 var deserializationError = new RestError("Error \"" + error + "\" occurred in parsing the responseBody \" +\n \"while creating the PollingState for Long Running Operation- \"" + response.bodyAsText + "\"");
213 deserializationError.request = response.request;
214 deserializationError.response = response;
215 throw deserializationError;
216 }
217 return result;
218}
219function getStatusFromResponse(response, responseBody) {
220 if (responseBody == undefined) {
221 responseBody = getResponseBody(response);
222 }
223 var result;
224 switch (response.status) {
225 case 202:
226 result = "InProgress";
227 break;
228 case 204:
229 result = "Succeeded";
230 break;
231 case 201:
232 result = getProvisioningState(responseBody) || "InProgress";
233 break;
234 case 200:
235 var provisioningState = getProvisioningState(responseBody);
236 if (provisioningState) {
237 result = provisioningState;
238 }
239 else if (getAzureAsyncOperationHeaderValue(response) || getLocationHeaderValue(response)) {
240 result = "InProgress";
241 }
242 else {
243 result = "Succeeded";
244 }
245 break;
246 default:
247 result = "Failed";
248 break;
249 }
250 return result;
251}
252var terminalStates = ["Succeeded", "Failed", "Canceled", "Cancelled"];
253/**
254 * Get whether or not a long-running operation with the provided status is finished.
255 * @param status The current status of a long-running operation.
256 * @returns Whether or not a long-running operation with the provided status is finished.
257 */
258export function isFinished(status) {
259 var result = false;
260 for (var _i = 0, terminalStates_1 = terminalStates; _i < terminalStates_1.length; _i++) {
261 var terminalState = terminalStates_1[_i];
262 if (longRunningOperationStatesEqual(status, terminalState)) {
263 result = true;
264 break;
265 }
266 }
267 return result;
268}
269export function longRunningOperationStatesEqual(lhs, rhs) {
270 var lhsLowerCased = lhs && lhs.toLowerCase();
271 var rhsLowerCased = rhs && rhs.toLowerCase();
272 return lhsLowerCased === rhsLowerCased;
273}
274/**
275 * Create a new long-running operation polling strategy based on the provided initial response.
276 * @param initialResponse The initial response to the long-running operation's initial request.
277 * @param azureServiceClient The AzureServiceClient that was used to send the initial request.
278 * @param options Any options that were provided to the initial request.
279 */
280export function createLROPollStrategyFromInitialResponse(initialResponse, azureServiceClient, options) {
281 var initialRequestMethod = initialResponse.request.method;
282 var initialResponseStatus = initialResponse.status;
283 var lroPollStrategyType;
284 if (getAzureAsyncOperationHeaderValue(initialResponse)) {
285 lroPollStrategyType = "AzureAsyncOperation";
286 }
287 else if (getLocationHeaderValue(initialResponse)) {
288 lroPollStrategyType = "Location";
289 }
290 else if (initialRequestMethod === "PUT" || initialRequestMethod === "PATCH") {
291 lroPollStrategyType = "GetResource";
292 }
293 else if (initialResponseStatus !== 201 && initialResponseStatus !== 202 && !isFinished(getStatusFromResponse(initialResponse))) {
294 throw new Error("Can't determine long running operation polling strategy.");
295 }
296 var result;
297 if (lroPollStrategyType) {
298 var resource = getResponseBody(initialResponse);
299 var lroPollState = {
300 pollStrategyType: lroPollStrategyType,
301 options: options,
302 initialResponse: initialResponse,
303 mostRecentResponse: initialResponse,
304 mostRecentRequest: initialResponse.request,
305 azureAsyncOperationHeaderValue: getAzureAsyncOperationHeaderValue(initialResponse),
306 locationHeaderValue: getLocationHeaderValue(initialResponse),
307 resource: resource,
308 state: getStatusFromResponse(initialResponse, resource)
309 };
310 result = createLROPollStrategyFromPollState(azureServiceClient, lroPollState);
311 }
312 else {
313 result = undefined;
314 }
315 return result;
316}
317export function createLROPollStrategyFromPollState(azureServiceClient, lroPollState) {
318 var result;
319 switch (lroPollState.pollStrategyType) {
320 case "AzureAsyncOperation":
321 result = new AzureAsyncOperationLROPollStrategy(azureServiceClient, lroPollState);
322 break;
323 case "Location":
324 result = new LocationLROPollStrategy(azureServiceClient, lroPollState);
325 break;
326 case "GetResource":
327 result = new GetResourceLROPollStrategy(azureServiceClient, lroPollState);
328 break;
329 default:
330 throw new Error("Unrecognized LRO poll strategy type: \"" + lroPollState.pollStrategyType + "\"");
331 break;
332 }
333 return result;
334}
335function getLocationHeaderValue(response) {
336 return response.headers.get("location");
337}
338/**
339 * A long-running operation polling strategy that is based on the location header.
340 */
341var LocationLROPollStrategy = /** @class */ (function (_super) {
342 __extends(LocationLROPollStrategy, _super);
343 function LocationLROPollStrategy() {
344 return _super !== null && _super.apply(this, arguments) || this;
345 }
346 LocationLROPollStrategy.prototype.locationStrategyShouldDeserialize = function (parsedResponse) {
347 var shouldDeserialize = false;
348 var initialResponse = this._pollState.initialResponse;
349 var initialRequestMethod = initialResponse.request.method;
350 var statusCode = parsedResponse.status;
351 if (statusCode === 200 ||
352 (statusCode === 201 && (initialRequestMethod === "PUT" || initialRequestMethod === "PATCH")) ||
353 (statusCode === 204 && (initialRequestMethod === "DELETE" || initialRequestMethod === "POST"))) {
354 shouldDeserialize = true;
355 }
356 return shouldDeserialize;
357 };
358 /**
359 * Retrieve PUT operation status by polling from "location" header.
360 * @param {string} method - The HTTP method.
361 * @param {PollingState} pollingState - The object to persist current operation state.
362 */
363 LocationLROPollStrategy.prototype.sendPollRequest = function () {
364 var lroPollState = this._pollState;
365 return this.updateOperationStatus(lroPollState.locationHeaderValue, this.locationStrategyShouldDeserialize.bind(this)).then(function (result) {
366 var locationHeaderValue = getLocationHeaderValue(result);
367 if (locationHeaderValue) {
368 lroPollState.locationHeaderValue = locationHeaderValue;
369 }
370 lroPollState.mostRecentResponse = result;
371 lroPollState.mostRecentRequest = result.request;
372 var initialResponse = lroPollState.initialResponse;
373 var initialRequestMethod = initialResponse.request.method;
374 var initialResponseStatusCode = initialResponse.status;
375 var statusCode = result.status;
376 if (statusCode === 202) {
377 lroPollState.state = "InProgress";
378 }
379 else if (statusCode === 200 ||
380 (statusCode === 201 && (initialRequestMethod === "PUT" || initialRequestMethod === "PATCH")) ||
381 (statusCode === 204 && (initialRequestMethod === "DELETE" || initialRequestMethod === "POST"))) {
382 lroPollState.state = "Succeeded";
383 lroPollState.resource = getResponseBody(result);
384 }
385 else if (statusCode === 404 && initialRequestMethod === "POST" &&
386 (initialResponseStatusCode === 200 || initialResponseStatusCode === 201 || initialResponseStatusCode === 202)) {
387 lroPollState.state = "Failed";
388 lroPollState.resource = getResponseBody(result);
389 }
390 else if (400 <= statusCode && statusCode <= 499) {
391 var resultBody = result.bodyAsText;
392 var errorMessage = resultBody;
393 try {
394 var resultObject = JSON.parse(resultBody);
395 errorMessage = resultObject.message;
396 }
397 catch (parseError) {
398 // Ignore the exception, use resultBody as the error message
399 }
400 throw new RestError(errorMessage, undefined, statusCode, stripRequest(result.request), result, resultBody);
401 }
402 else {
403 throw new Error("The response with status code " + statusCode + " from polling for long running operation url \"" + lroPollState.locationHeaderValue + "\" is not valid.");
404 }
405 });
406 };
407 LocationLROPollStrategy.prototype.isFinalStatusAcceptable = function () {
408 var lroPollState = this._pollState;
409 var initialResponse = lroPollState.initialResponse;
410 var initialResponseStatusCode = initialResponse.status;
411 return longRunningOperationStatesEqual(lroPollState.state, "Succeeded") ||
412 (initialResponse.request.method === "POST" && lroPollState.mostRecentResponse.status === 404 &&
413 (initialResponseStatusCode === 200 ||
414 initialResponseStatusCode === 201 ||
415 initialResponseStatusCode === 202));
416 };
417 LocationLROPollStrategy.prototype.shouldDoFinalGetResourceRequest = function () {
418 var lroPollState = this._pollState;
419 var initialResponse = lroPollState.initialResponse;
420 var result;
421 var initialRequestMethod = initialResponse.request.method;
422 var initialResponseStatusCode = initialResponse.status;
423 if (initialRequestMethod === "POST" && lroPollState.mostRecentResponse.status === 404 &&
424 (initialResponseStatusCode === 200 ||
425 initialResponseStatusCode === 201 ||
426 initialResponseStatusCode === 202)) {
427 result = false;
428 }
429 else {
430 result = _super.prototype.shouldDoFinalGetResourceRequest.call(this) ||
431 (initialRequestMethod === "POST" && initialResponseStatusCode === 201);
432 }
433 return result;
434 };
435 LocationLROPollStrategy.prototype.doFinalGetResourceRequest = function () {
436 var lroPollState = this._pollState;
437 var initialResponse = lroPollState.initialResponse;
438 var getResourceRequestUrl;
439 var initialResponseStatusCode = initialResponse.status;
440 var initialRequest = initialResponse.request;
441 if (initialRequest.method === "POST" &&
442 (initialResponseStatusCode === 200 ||
443 initialResponseStatusCode === 201 ||
444 initialResponseStatusCode === 202)) {
445 getResourceRequestUrl = lroPollState.locationHeaderValue;
446 }
447 else {
448 getResourceRequestUrl = initialRequest.url;
449 }
450 return this.updateState(getResourceRequestUrl, true);
451 };
452 return LocationLROPollStrategy;
453}(LROPollStrategy));
454function getAzureAsyncOperationHeaderValue(response) {
455 return response.headers.get("azure-asyncoperation");
456}
457/**
458 * A long-running operation polling strategy that is based on the azure-asyncoperation header.
459 */
460var AzureAsyncOperationLROPollStrategy = /** @class */ (function (_super) {
461 __extends(AzureAsyncOperationLROPollStrategy, _super);
462 function AzureAsyncOperationLROPollStrategy() {
463 return _super !== null && _super.apply(this, arguments) || this;
464 }
465 /**
466 * Retrieve operation status by polling from "azure-asyncoperation" header.
467 * @param {PollingState} pollingState - The object to persist current operation state.
468 * @param {boolean} inPostOrDelete - Invoked by Post Or Delete operation.
469 */
470 AzureAsyncOperationLROPollStrategy.prototype.sendPollRequest = function () {
471 var lroPollState = this._pollState;
472 return this.updateOperationStatus(lroPollState.azureAsyncOperationHeaderValue, false).then(function (response) {
473 var statusCode = response.status;
474 var parsedResponse = response.parsedBody;
475 if (statusCode !== 200 && statusCode !== 201 && statusCode !== 202 && statusCode !== 204) {
476 var error = new RestError("Invalid status code (" + statusCode + ") with response body \"" + response.bodyAsText + "\" occurred when polling for operation status.");
477 error.statusCode = statusCode;
478 error.request = stripRequest(response.request);
479 error.response = response;
480 error.body = parsedResponse;
481 throw error;
482 }
483 if (!parsedResponse) {
484 throw new Error("The response from long running operation does not contain a body.");
485 }
486 else if (!parsedResponse.status) {
487 throw new Error("The response \"" + response.bodyAsText + "\" from long running operation does not contain the status property.");
488 }
489 var azureAsyncOperationHeaderValue = getAzureAsyncOperationHeaderValue(response);
490 if (azureAsyncOperationHeaderValue) {
491 lroPollState.azureAsyncOperationHeaderValue = azureAsyncOperationHeaderValue;
492 }
493 lroPollState.state = parsedResponse.status;
494 lroPollState.mostRecentResponse = response;
495 lroPollState.mostRecentRequest = response.request;
496 lroPollState.resource = getResponseBody(response);
497 });
498 };
499 AzureAsyncOperationLROPollStrategy.prototype.shouldDoFinalGetResourceRequest = function () {
500 var lroPollState = this._pollState;
501 var initialResponse = lroPollState.initialResponse;
502 var initialRequestMethod = initialResponse.request.method;
503 var result = false;
504 if (initialRequestMethod === "PUT" || initialRequestMethod === "PATCH") {
505 result = true;
506 }
507 else {
508 if (lroPollState.locationHeaderValue) {
509 var initialResponseStatusCode = initialResponse.status;
510 if (initialRequestMethod === "POST") {
511 result = initialResponseStatusCode === 200 || initialResponseStatusCode === 201;
512 }
513 else if (initialRequestMethod === "DELETE") {
514 result = initialResponseStatusCode === 200 || initialResponseStatusCode === 202;
515 }
516 }
517 }
518 return result;
519 };
520 AzureAsyncOperationLROPollStrategy.prototype.doFinalGetResourceRequest = function () {
521 var lroPollState = this._pollState;
522 var locationHeaderValue = lroPollState.locationHeaderValue;
523 var initialResponse = lroPollState.initialResponse;
524 var initialRequest = initialResponse.request;
525 var getResourceRequestUrl = initialRequest.url;
526 if (locationHeaderValue) {
527 var initialRequestMethod = initialRequest.method;
528 var initialResponseStatusCode = initialResponse.status;
529 if (initialRequestMethod === "POST" && (initialResponseStatusCode === 200 || initialResponseStatusCode === 201 || initialResponseStatusCode === 202)) {
530 getResourceRequestUrl = locationHeaderValue;
531 }
532 else if (initialRequestMethod === "DELETE" && (initialResponseStatusCode === 200 || initialResponseStatusCode === 202)) {
533 getResourceRequestUrl = locationHeaderValue;
534 }
535 }
536 return this.updateState(getResourceRequestUrl, true);
537 };
538 AzureAsyncOperationLROPollStrategy.prototype.isFinalStatusAcceptable = function () {
539 var lroPollState = this._pollState;
540 var initialResponse = lroPollState.initialResponse;
541 var initialResponseStatusCode = initialResponse.status;
542 return longRunningOperationStatesEqual(lroPollState.state, "Succeeded") ||
543 (initialResponse.request.method === "POST" && (initialResponseStatusCode === 200 || initialResponseStatusCode === 201));
544 };
545 return AzureAsyncOperationLROPollStrategy;
546}(LROPollStrategy));
547/**
548 * A long-running operation polling strategy that is based on the resource's provisioning state.
549 */
550var GetResourceLROPollStrategy = /** @class */ (function (_super) {
551 __extends(GetResourceLROPollStrategy, _super);
552 function GetResourceLROPollStrategy() {
553 return _super !== null && _super.apply(this, arguments) || this;
554 }
555 GetResourceLROPollStrategy.prototype.sendPollRequest = function () {
556 var lroPollState = this._pollState;
557 return this.updateOperationStatus(lroPollState.initialResponse.request.url, false).then(function (result) {
558 var statusCode = result.status;
559 var responseBody = result.parsedBody;
560 if (statusCode !== 200 && statusCode !== 201 && statusCode !== 202 && statusCode !== 204) {
561 var error = new RestError("Invalid status code with response body \"" + result.bodyAsText + "\" occurred when polling for operation status.");
562 error.statusCode = statusCode;
563 error.request = stripRequest(result.request);
564 error.response = result;
565 error.body = responseBody;
566 throw error;
567 }
568 if (!result.parsedBody) {
569 throw new Error("The response from long running operation does not contain a body.");
570 }
571 lroPollState.state = getProvisioningState(result.parsedBody) || "Succeeded";
572 lroPollState.mostRecentResponse = result;
573 lroPollState.mostRecentRequest = result.request;
574 lroPollState.resource = getResponseBody(result);
575 });
576 };
577 GetResourceLROPollStrategy.prototype.isFinalStatusAcceptable = function () {
578 return longRunningOperationStatesEqual(this._pollState.state, "Succeeded");
579 };
580 GetResourceLROPollStrategy.prototype.doFinalGetResourceRequest = function () {
581 return this.sendPollRequest();
582 };
583 return GetResourceLROPollStrategy;
584}(LROPollStrategy));
585//# sourceMappingURL=lroPollStrategy.js.map
\No newline at end of file