Skip to content

Commit 6eb5a1a

Browse files
authored
[core-lro] Remove support for cancellation in LroEngine (Azure#22844)
cancelOperation is being deprecated in Azure#22753 so `LroEngine` no longer needs to have support for it. Note that this support was introduced in 2.3.0-beta.1 so removing it is not a breaking change. Furthermore, this PR prepares for release today.
1 parent eb0aa1d commit 6eb5a1a

File tree

9 files changed

+38
-166
lines changed

9 files changed

+38
-166
lines changed

sdk/core/core-lro/CHANGELOG.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
# Release History
22

3-
## 2.3.0-beta.2 (Unreleased)
4-
5-
### Features Added
6-
7-
### Breaking Changes
3+
## 2.2.5 (2022-08-08)
84

95
### Bugs Fixed
106

7+
- `LroEngine` no longer throws an error when it receives a 204 polling response.
8+
119
### Other Changes
1210

1311
- Support LROs with GET as the initial request method.
1412
- Better logging support for the operation and the poller.
13+
- Removed the unused dependency `@azure/core-tracing`.
1514

1615
## 2.3.0-beta.1 (2022-05-18)
1716

sdk/core/core-lro/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@azure/core-lro",
33
"author": "Microsoft Corporation",
44
"sdk-type": "client",
5-
"version": "2.3.0-beta.2",
5+
"version": "2.2.5",
66
"description": "Isomorphic client library for supporting long-running operations in node.js and browser.",
77
"tags": [
88
"isomorphic",

sdk/core/core-lro/review/core-lro.api.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ export class LroEngine<TResult, TState extends PollOperationState<TResult>> exte
2525

2626
// @public
2727
export interface LroEngineOptions<TResult, TState> {
28-
cancel?: (state: TState) => Promise<void>;
2928
intervalInMs?: number;
3029
isDone?: (lastResponse: unknown, state: TState) => boolean;
3130
lroResourceLocationConfig?: LroResourceLocationConfig;

sdk/core/core-lro/src/lroEngine/lroEngine.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ export class LroEngine<TResult, TState extends PollOperationState<TResult>> exte
4242
options?.lroResourceLocationConfig,
4343
options?.processResult,
4444
options?.updateState,
45-
options?.isDone,
46-
options?.cancel
45+
options?.isDone
4746
);
4847
super(operation);
4948

sdk/core/core-lro/src/lroEngine/models.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,6 @@ export interface LroEngineOptions<TResult, TState> {
3131
* A predicate to determine whether the LRO finished processing.
3232
*/
3333
isDone?: (lastResponse: unknown, state: TState) => boolean;
34-
35-
/**
36-
* A function that takes the mutable state as input and attempts to cancel the
37-
* LRO.
38-
*/
39-
cancel?: (state: TState) => Promise<void>;
4034
}
4135

4236
/**

sdk/core/core-lro/src/lroEngine/operation.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ export class GenericPollOperation<TResult, TState extends PollOperationState<TRe
4040
private lroResourceLocationConfig?: LroResourceLocationConfig,
4141
private processResult?: (result: unknown, state: TState) => TResult,
4242
private updateState?: (state: TState, lastResponse: RawResponse) => void,
43-
private isDone?: (lastResponse: TResult, state: TState) => boolean,
44-
private cancelOp?: (state: TState) => Promise<void>
43+
private isDone?: (lastResponse: TResult, state: TState) => boolean
4544
) {}
4645

4746
public setPollerConfig(pollerConfig: PollerConfig): void {
@@ -135,7 +134,7 @@ export class GenericPollOperation<TResult, TState extends PollOperationState<TRe
135134
}
136135

137136
async cancel(): Promise<PollOperation<TState, TResult>> {
138-
await this.cancelOp?.(this.state);
137+
logger.error("`cancelOperation` is deprecated because it wasn't implemented");
139138
return this;
140139
}
141140

sdk/core/core-lro/src/poller.ts

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -309,34 +309,17 @@ export abstract class Poller<TState extends PollOperationState<TResult>, TResult
309309
* @param options - Optional properties passed to the operation's update method.
310310
*/
311311
private async pollOnce(options: { abortSignal?: AbortSignalLike } = {}): Promise<void> {
312-
try {
313-
if (!this.isDone()) {
312+
if (!this.isDone()) {
313+
try {
314314
this.operation = await this.operation.update({
315315
abortSignal: options.abortSignal,
316316
fireProgress: this.fireProgress.bind(this),
317317
});
318-
if (this.operation.state.isCancelled) {
319-
this.stopped = true;
320-
if (this.reject) {
321-
this.reject(new PollerCancelledError("Poller cancelled"));
322-
}
323-
throw new Error(`The long-running operation has been canceled.`);
324-
} else if (this.isDone() && this.resolve) {
325-
// If the poller has finished polling, this means we now have a result.
326-
// However, it can be the case that TResult is instantiated to void, so
327-
// we are not expecting a result anyway. To assert that we might not
328-
// have a result eventually after finishing polling, we cast the result
329-
// to TResult.
330-
this.resolve(this.operation.state.result as TResult);
331-
}
318+
} catch (e: any) {
319+
this.operation.state.error = e;
332320
}
333-
} catch (e: any) {
334-
this.operation.state.error = e;
335-
if (this.reject) {
336-
this.reject(e);
337-
}
338-
throw e;
339321
}
322+
this.processUpdatedState();
340323
}
341324

342325
/**
@@ -379,13 +362,37 @@ export abstract class Poller<TState extends PollOperationState<TResult>, TResult
379362
return this.pollOncePromise;
380363
}
381364

365+
private processUpdatedState(): void {
366+
if (this.operation.state.error) {
367+
this.stopped = true;
368+
this.reject!(this.operation.state.error);
369+
throw this.operation.state.error;
370+
}
371+
if (this.operation.state.isCancelled) {
372+
this.stopped = true;
373+
const error = new PollerCancelledError("Poller cancelled");
374+
this.reject!(error);
375+
throw error;
376+
} else if (this.isDone() && this.resolve) {
377+
// If the poller has finished polling, this means we now have a result.
378+
// However, it can be the case that TResult is instantiated to void, so
379+
// we are not expecting a result anyway. To assert that we might not
380+
// have a result eventually after finishing polling, we cast the result
381+
// to TResult.
382+
this.resolve(this.operation.state.result as TResult);
383+
}
384+
}
385+
382386
/**
383387
* Returns a promise that will resolve once the underlying operation is completed.
384388
*/
385389
public async pollUntilDone(): Promise<TResult> {
386390
if (this.stopped) {
387391
this.startPolling().catch(this.reject);
388392
}
393+
// This is needed because the state could have been updated by
394+
// `cancelOperation`, e.g. the operation is canceled or an error occurred.
395+
this.processUpdatedState();
389396
return this.promise;
390397
}
391398

sdk/core/core-lro/test/engine.spec.ts

Lines changed: 0 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -2098,127 +2098,4 @@ describe("Lro Engine", function () {
20982098
assert.deepInclude(result, { id: "200", name: "foo" });
20992099
});
21002100
});
2101-
2102-
describe("poller cancellation", () => {
2103-
it("isCancel is set after the cancellation callback resolves", async () => {
2104-
const resourceLocationPath = "/LROPostDoubleHeadersFinalAzureHeaderGetDefault/location";
2105-
const pollingPath = "/LROPostDoubleHeadersFinalAzureHeaderGetDefault/asyncOperationUrl";
2106-
let run = false;
2107-
const poller = createPoller({
2108-
routes: [
2109-
{
2110-
method: "POST",
2111-
status: 202,
2112-
body: "",
2113-
headers: {
2114-
Location: resourceLocationPath,
2115-
"Operation-Location": pollingPath,
2116-
},
2117-
},
2118-
{
2119-
method: "GET",
2120-
path: pollingPath,
2121-
status: 200,
2122-
body: `{ "status": "running"}`,
2123-
},
2124-
{
2125-
method: "GET",
2126-
path: pollingPath,
2127-
status: 200,
2128-
body: `{ "status": "canceled" }`,
2129-
},
2130-
],
2131-
cancel: async () => {
2132-
run = true;
2133-
},
2134-
});
2135-
assert.isUndefined(poller.getOperationState().isCancelled);
2136-
await poller.poll();
2137-
assert.isUndefined(poller.getOperationState().isCancelled);
2138-
await poller.cancelOperation();
2139-
assert.isTrue(run);
2140-
assert.isUndefined(poller.getOperationState().isCancelled);
2141-
await Promise.all([
2142-
assertError(poller.pollUntilDone(), {
2143-
messagePattern: /Poller cancelled/,
2144-
}),
2145-
assertError(poller.poll(), {
2146-
messagePattern: /The long-running operation has been canceled./,
2147-
}),
2148-
]);
2149-
assert.isTrue(poller.getOperationState().isCancelled);
2150-
});
2151-
2152-
it("isCancel is not set when the cancellation callback throws", async () => {
2153-
const resourceLocationPath = "/LROPostDoubleHeadersFinalAzureHeaderGetDefault/location";
2154-
const pollingPath = "/LROPostDoubleHeadersFinalAzureHeaderGetDefault/asyncOperationUrl";
2155-
let run = false;
2156-
const poller = createPoller({
2157-
routes: [
2158-
{
2159-
method: "POST",
2160-
status: 202,
2161-
body: "",
2162-
headers: {
2163-
Location: resourceLocationPath,
2164-
"Operation-Location": pollingPath,
2165-
},
2166-
},
2167-
{
2168-
method: "GET",
2169-
path: pollingPath,
2170-
status: 200,
2171-
body: `{ "status": "succeeded"}`,
2172-
},
2173-
{
2174-
method: "GET",
2175-
path: resourceLocationPath,
2176-
status: 200,
2177-
body: `{ "id": "100", "name": "foo" }`,
2178-
},
2179-
],
2180-
cancel: async () => {
2181-
run = true;
2182-
throw new Error();
2183-
},
2184-
});
2185-
assert.isUndefined(poller.getOperationState().isCancelled);
2186-
await poller.poll();
2187-
assert.isUndefined(poller.getOperationState().isCancelled);
2188-
await assert.isRejected(poller.cancelOperation());
2189-
assert.isTrue(run);
2190-
assert.isUndefined(poller.getOperationState().isCancelled);
2191-
});
2192-
2193-
it("cancelled poller gives access to partial results", async () => {
2194-
const pollingPath = "/LROPostDoubleHeadersFinalAzureHeaderGetDefault/asyncOperationUrl";
2195-
const poller = createPoller({
2196-
routes: [
2197-
{
2198-
method: "POST",
2199-
status: 202,
2200-
body: "",
2201-
headers: {
2202-
"Operation-Location": pollingPath,
2203-
},
2204-
},
2205-
{
2206-
method: "GET",
2207-
path: pollingPath,
2208-
status: 200,
2209-
body: `{ "status": "running"}`,
2210-
},
2211-
{
2212-
method: "GET",
2213-
path: pollingPath,
2214-
status: 200,
2215-
body: `{ "status": "canceled", "results": [1,2] }`,
2216-
},
2217-
],
2218-
});
2219-
await assertError(poller.pollUntilDone(), { messagePattern: /Poller cancelled/ });
2220-
const result = poller.getResult();
2221-
assert.deepEqual(result!.results, [1, 2]);
2222-
});
2223-
});
22242101
});

sdk/core/core-lro/test/utils/router.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,8 @@ export function createPoller<TState>(settings: {
116116
lroResourceLocationConfig?: LroResourceLocationConfig;
117117
processResult?: (result: unknown, state: TState) => Response;
118118
updateState?: (state: TState, lastResponse: RawResponse) => void;
119-
cancel?: (state: TState) => Promise<void>;
120119
}): PollerLike<PollOperationState<Response>, Response> {
121-
const { routes, lroResourceLocationConfig, processResult, updateState, cancel } = settings;
120+
const { routes, lroResourceLocationConfig, processResult, updateState } = settings;
122121
const client = createClient(toLroProcessors(routes));
123122
const { method: requestMethod, path = initialPath } = routes[0];
124123
const lro = new CoreRestPipelineLro(createSendOp({ client }), {
@@ -134,7 +133,6 @@ export function createPoller<TState>(settings: {
134133
lroResourceLocationConfig,
135134
processResult,
136135
updateState,
137-
cancel,
138136
});
139137
}
140138

0 commit comments

Comments
 (0)