Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/spicy-geese-arrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@smithy/util-waiter": minor
---

add type information to WaiterResult reason
20 changes: 10 additions & 10 deletions packages/util-waiter/src/createWaiter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import { validateWaiterOptions } from "./utils";
import type { WaiterOptions, WaiterResult } from "./waiter";
import { waiterServiceDefaults, WaiterState } from "./waiter";

const abortTimeout = (
const abortTimeout = <R>(
abortSignal: AbortSignal | DeprecatedAbortSignal
): {
clearListener: () => void;
aborted: Promise<WaiterResult>;
aborted: Promise<WaiterResult<R>>;
} => {
let onAbort: () => void;

const promise = new Promise<WaiterResult>((resolve) => {
const promise = new Promise<WaiterResult<R>>((resolve) => {
onAbort = () => resolve({ state: WaiterState.ABORTED });
if (typeof (abortSignal as AbortSignal).addEventListener === "function") {
// preferred.
Expand Down Expand Up @@ -43,33 +43,33 @@ const abortTimeout = (
*
* @internal
*/
export const createWaiter = async <Client, Input>(
export const createWaiter = async <Client, Input, Reason = any>(
options: WaiterOptions<Client>,
input: Input,
acceptorChecks: (client: Client, input: Input) => Promise<WaiterResult>
): Promise<WaiterResult> => {
acceptorChecks: (client: Client, input: Input) => Promise<WaiterResult<Reason>>
): Promise<WaiterResult<Reason>> => {
const params = {
...waiterServiceDefaults,
...options,
};
validateWaiterOptions(params);

const exitConditions = [runPolling<Client, Input>(params, input, acceptorChecks)];
const exitConditions = [runPolling<Client, Input, Reason>(params, input, acceptorChecks)];

const finalize = [] as Array<() => void>;

if (options.abortSignal) {
const { aborted, clearListener } = abortTimeout(options.abortSignal);
const { aborted, clearListener } = abortTimeout<Reason>(options.abortSignal);
finalize.push(clearListener);
exitConditions.push(aborted);
}
if (options.abortController?.signal) {
const { aborted, clearListener } = abortTimeout(options.abortController.signal);
const { aborted, clearListener } = abortTimeout<Reason>(options.abortController.signal);
finalize.push(clearListener);
exitConditions.push(aborted);
}

return Promise.race<WaiterResult>(exitConditions).then((result) => {
return Promise.race<WaiterResult<Reason>>(exitConditions).then((result) => {
for (const fn of finalize) {
fn();
}
Expand Down
14 changes: 7 additions & 7 deletions packages/util-waiter/src/poller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import type { WaiterOptions, WaiterResult } from "./waiter";
import { WaiterState } from "./waiter";

/**
* @internal
*
* Reference: https://smithy.io/2.0/additional-specs/waiters.html#waiter-retries
* @internal
*/
const exponentialBackoffWithJitter = (minDelay: number, maxDelay: number, attemptCeiling: number, attempt: number) => {
if (attempt > attemptCeiling) return maxDelay;
Expand All @@ -24,11 +23,11 @@ const randomInRange = (min: number, max: number) => min + Math.random() * (max -
* @param input - client input
* @param acceptorChecks - function that checks the acceptor states on each poll.
*/
export const runPolling = async <Client, Input>(
export const runPolling = async <Client, Input, Reason = any>(
{ minDelay, maxDelay, maxWaitTime, abortController, client, abortSignal }: WaiterOptions<Client>,
input: Input,
acceptorChecks: (client: Client, input: Input) => Promise<WaiterResult>
): Promise<WaiterResult> => {
acceptorChecks: (client: Client, input: Input) => Promise<WaiterResult<Reason>>
): Promise<WaiterResult<Reason>> => {
const observedResponses: Record<string, number> = {};

const { state, reason } = await acceptorChecks(client, input);
Expand Down Expand Up @@ -78,9 +77,10 @@ export const runPolling = async <Client, Input>(
};

/**
* @internal
* convert the result of an SDK operation, either an error or response object, to a
* Convert the result of an SDK operation, either an error or response object, to a
* readable string.
*
* @internal
*/
const createMessageFromResponse = (reason: any): string => {
if (reason?.$responseBodyText) {
Expand Down
20 changes: 8 additions & 12 deletions packages/util-waiter/src/waiter.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import type { WaiterConfiguration as WaiterConfiguration__ } from "@smithy/types";
import type { WaiterConfiguration } from "@smithy/types";

import { getCircularReplacer } from "./circularReplacer";

/**
* @internal
*/
export interface WaiterConfiguration<T> extends WaiterConfiguration__<T> {}
export { WaiterConfiguration };

/**
* @internal
Expand All @@ -22,7 +19,7 @@ export type WaiterOptions<Client> = WaiterConfiguration<Client> &
Required<Pick<WaiterConfiguration<Client>, "minDelay" | "maxDelay">>;

/**
* @internal
* @public
*/
export enum WaiterState {
ABORTED = "ABORTED",
Expand All @@ -33,15 +30,15 @@ export enum WaiterState {
}

/**
* @internal
* @public
*/
export type WaiterResult = {
export type WaiterResult<R = any> = {
state: WaiterState;

/**
* (optional) Indicates a reason for why a waiter has reached its state.
*/
reason?: any;
reason?: R;

/**
* Responses observed by the waiter during its polling, where the value
Expand All @@ -51,12 +48,11 @@ export type WaiterResult = {
};

/**
* @internal
*
* Handles and throws exceptions resulting from the waiterResult
* @internal
* @param result - WaiterResult
*/
export const checkExceptions = (result: WaiterResult): WaiterResult => {
export const checkExceptions = <R>(result: WaiterResult<R>): WaiterResult<R> => {
if (result.state === WaiterState.ABORTED) {
const abortError = new Error(
`${JSON.stringify(
Expand Down
1 change: 1 addition & 0 deletions private/my-local-model-schema/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@smithy/util-middleware": "workspace:^",
"@smithy/util-retry": "workspace:^",
"@smithy/util-utf8": "workspace:^",
"@smithy/util-waiter": "workspace:^",
"tslib": "^2.6.2"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions private/my-local-model-schema/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type { RuntimeExtension } from "./runtimeExtensions";
export type { XYZServiceExtensionConfiguration } from "./extensionConfiguration";
export * from "./commands";
export * from "./schemas/schemas_0";
export * from "./waiters";

export * from "./models/errors";
export * from "./models/models_0";
Expand Down
2 changes: 2 additions & 0 deletions private/my-local-model-schema/src/waiters/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// smithy-typescript generated code
export * from "./waitForNumbersAligned";
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// smithy-typescript generated code
import { checkExceptions, createWaiter, WaiterConfiguration, WaiterResult, WaiterState } from "@smithy/util-waiter";

import {
type GetNumbersCommandInput,
type GetNumbersCommandOutput,
GetNumbersCommand,
} from "../commands/GetNumbersCommand";
import { XYZServiceClient } from "../XYZServiceClient";

const checkState = async (client: XYZServiceClient, input: GetNumbersCommandInput): Promise<WaiterResult<GetNumbersCommandOutput | Error>> => {
let reason;
try {
let result: GetNumbersCommandOutput & any = await client.send(new GetNumbersCommand(input));
reason = result;
return { state: WaiterState.SUCCESS, reason };
} catch (exception) {
reason = exception;
if (exception.name === "MysteryThrottlingError") {
return { state: WaiterState.RETRY, reason };
}
if (exception.name === "HaltError") {
return { state: WaiterState.FAILURE, reason };
}
}
return { state: WaiterState.RETRY, reason };
};
/**
* wait until the numbers align
* @deprecated Use waitUntilNumbersAligned instead. waitForNumbersAligned does not throw error in non-success cases.
*/
export const waitForNumbersAligned = async (
params: WaiterConfiguration<XYZServiceClient>,
input: GetNumbersCommandInput
): Promise<WaiterResult<GetNumbersCommandOutput | Error>> => {
const serviceDefaults = { minDelay: 2, maxDelay: 120 };
return createWaiter({ ...serviceDefaults, ...params }, input, checkState);
};
/**
* wait until the numbers align
* @param params - Waiter configuration options.
* @param input - The input to GetNumbersCommand for polling.
*/
export const waitUntilNumbersAligned = async (
params: WaiterConfiguration<XYZServiceClient>,
input: GetNumbersCommandInput
): Promise<WaiterResult<GetNumbersCommandOutput | Error>> => {
const serviceDefaults = { minDelay: 2, maxDelay: 120 };
const result = await createWaiter({ ...serviceDefaults, ...params }, input, checkState);
return checkExceptions(result);
};
5 changes: 5 additions & 0 deletions private/my-local-model-schema/test/index-objects.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
TradeEventStreamCommand,
TradeEventStreamRequest$,
TradeEventStreamResponse$,
waitForNumbersAligned,
waitUntilNumbersAligned,
XYZService,
XYZServiceClient,
XYZServiceServiceException,
Expand Down Expand Up @@ -55,4 +57,7 @@ assert(typeof RetryableError$ === "object");
assert(XYZServiceServiceException.prototype instanceof XYZServiceSyntheticServiceException);
assert(typeof XYZServiceServiceException$ === "object");
assert(XYZServiceSyntheticServiceException.prototype instanceof Error);
// waiters
assert(typeof waitForNumbersAligned === "function");
assert(typeof waitUntilNumbersAligned === "function");
console.log(`XYZService index test passed.`);
2 changes: 2 additions & 0 deletions private/my-local-model-schema/test/index-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ export type {
RetryableError,
XYZServiceServiceException,
XYZServiceSyntheticServiceException,
waitForNumbersAligned,
waitUntilNumbersAligned,
} from "../dist-types/index.d";
1 change: 1 addition & 0 deletions private/my-local-model/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"@smithy/util-middleware": "workspace:^",
"@smithy/util-retry": "workspace:^",
"@smithy/util-utf8": "workspace:^",
"@smithy/util-waiter": "workspace:^",
"tslib": "^2.6.2"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions private/my-local-model/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export { ClientInputEndpointParameters } from "./endpoint/EndpointParameters";
export type { RuntimeExtension } from "./runtimeExtensions";
export type { XYZServiceExtensionConfiguration } from "./extensionConfiguration";
export * from "./commands";
export * from "./waiters";

export * from "./models/errors";
export * from "./models/models_0";
Expand Down
2 changes: 2 additions & 0 deletions private/my-local-model/src/waiters/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// smithy-typescript generated code
export * from "./waitForNumbersAligned";
47 changes: 47 additions & 0 deletions private/my-local-model/src/waiters/waitForNumbersAligned.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// smithy-typescript generated code
import { checkExceptions, createWaiter, WaiterConfiguration, WaiterResult, WaiterState } from "@smithy/util-waiter";

import { GetNumbersCommand, GetNumbersCommandInput, GetNumbersCommandOutput } from "../commands/GetNumbersCommand";
import { XYZServiceClient } from "../XYZServiceClient";

const checkState = async (client: XYZServiceClient, input: GetNumbersCommandInput): Promise<WaiterResult<GetNumbersCommandOutput | Error>> => {
let reason;
try {
let result: GetNumbersCommandOutput & any = await client.send(new GetNumbersCommand(input));
reason = result;
return { state: WaiterState.SUCCESS, reason };
} catch (exception) {
reason = exception;
if (exception.name === "MysteryThrottlingError") {
return { state: WaiterState.RETRY, reason };
}
if (exception.name === "HaltError") {
return { state: WaiterState.FAILURE, reason };
}
}
return { state: WaiterState.RETRY, reason };
};
/**
* wait until the numbers align
* @deprecated Use waitUntilNumbersAligned instead. waitForNumbersAligned does not throw error in non-success cases.
*/
export const waitForNumbersAligned = async (
params: WaiterConfiguration<XYZServiceClient>,
input: GetNumbersCommandInput
): Promise<WaiterResult<GetNumbersCommandOutput | Error>> => {
const serviceDefaults = { minDelay: 2, maxDelay: 120 };
return createWaiter({ ...serviceDefaults, ...params }, input, checkState);
};
/**
* wait until the numbers align
* @param params - Waiter configuration options.
* @param input - The input to GetNumbersCommand for polling.
*/
export const waitUntilNumbersAligned = async (
params: WaiterConfiguration<XYZServiceClient>,
input: GetNumbersCommandInput
): Promise<WaiterResult<GetNumbersCommandOutput | Error>> => {
const serviceDefaults = { minDelay: 2, maxDelay: 120 };
const result = await createWaiter({ ...serviceDefaults, ...params }, input, checkState);
return checkExceptions(result);
};
Loading