Skip to content

Commit 59011f3

Browse files
committed
wait.retrieveToken working
1 parent e17d25c commit 59011f3

File tree

9 files changed

+156
-12
lines changed

9 files changed

+156
-12
lines changed

apps/webapp/app/components/runs/v3/WaitpointDetails.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export function WaitpointDetailTable({
8787
{waitpoint.status === "WAITING" && <ForceTimeout waitpoint={waitpoint} />}
8888
</div>
8989
<Paragraph variant="extra-small" className="text-text-dimmed/70">
90-
{waitpoint.isTimeout
90+
{waitpoint.status === "TIMED_OUT"
9191
? "The waitpoint timed out"
9292
: waitpoint.status === "COMPLETED"
9393
? "The waitpoint completed before this timeout was reached"
@@ -112,7 +112,7 @@ export function WaitpointDetailTable({
112112
</Property.Item>
113113
</>
114114
)}
115-
{waitpoint.status === "WAITING" ? null : waitpoint.isTimeout ? (
115+
{waitpoint.status === "WAITING" ? null : waitpoint.status === "TIMED_OUT" ? (
116116
<></>
117117
) : waitpoint.output ? (
118118
<PacketDisplay title="Output" data={waitpoint.output} dataType={waitpoint.outputType} />
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { type RuntimeEnvironmentType } from "@trigger.dev/core/v3";
2+
import { type RunEngineVersion } from "@trigger.dev/database";
3+
import { ServiceValidationError } from "~/v3/services/baseService.server";
4+
import { BasePresenter } from "./basePresenter.server";
5+
import { WaitpointPresenter } from "./WaitpointPresenter.server";
6+
7+
export class ApiWaitpointPresenter extends BasePresenter {
8+
public async call(
9+
environment: {
10+
id: string;
11+
type: RuntimeEnvironmentType;
12+
project: {
13+
id: string;
14+
engine: RunEngineVersion;
15+
};
16+
},
17+
waitpointId: string
18+
) {
19+
return this.trace("call", async (span) => {
20+
const presenter = new WaitpointPresenter();
21+
const result = await presenter.call({
22+
friendlyId: waitpointId,
23+
environmentId: environment.id,
24+
projectId: environment.project.id,
25+
});
26+
27+
if (!result) {
28+
throw new ServiceValidationError("Waitpoint not found");
29+
}
30+
31+
return {
32+
id: result.id,
33+
status: result.status,
34+
completedAt: result.completedAt ?? undefined,
35+
timeoutAt: result.timeoutAt ?? undefined,
36+
completedAfter: result.completedAfter ?? undefined,
37+
idempotencyKey: result.userProvidedIdempotencyKey ? result.idempotencyKey : undefined,
38+
idempotencyKeyExpiresAt: result.idempotencyKeyExpiresAt ?? undefined,
39+
tags: result.tags ?? [],
40+
createdAt: result.createdAt,
41+
output: result.output,
42+
outputType: result.outputType,
43+
outputIsError: result.outputIsError,
44+
};
45+
});
46+
}
47+
}

apps/webapp/app/presenters/v3/WaitpointPresenter.server.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export class WaitpointPresenter extends BasePresenter {
3333
outputType: true,
3434
outputIsError: true,
3535
completedAfter: true,
36+
completedAt: true,
37+
createdAt: true,
3638
connectedRuns: {
3739
select: {
3840
friendlyId: true,
@@ -89,9 +91,11 @@ export class WaitpointPresenter extends BasePresenter {
8991
output: output,
9092
outputType: waitpoint.outputType,
9193
outputIsError: waitpoint.outputIsError,
94+
timeoutAt: waitpoint.completedAfter,
9295
completedAfter: waitpoint.completedAfter,
96+
completedAt: waitpoint.completedAt,
97+
createdAt: waitpoint.createdAt,
9398
tags: waitpoint.tags,
94-
isTimeout,
9599
connectedRuns,
96100
};
97101
}

apps/webapp/app/presenters/v3/WaitpointTokenListPresenter.server.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,14 +248,13 @@ export class WaitpointTokenListPresenter extends BasePresenter {
248248
id: token.friendlyId,
249249
status: waitpointStatusToApiStatus(token.status, token.outputIsError),
250250
completedAt: token.completedAt ?? undefined,
251+
timeoutAt: token.completedAfter ?? undefined,
251252
completedAfter: token.completedAfter ?? undefined,
252253
idempotencyKey: token.userProvidedIdempotencyKey
253254
? token.inactiveIdempotencyKey ?? token.idempotencyKey
254255
: undefined,
255256
idempotencyKeyExpiresAt: token.idempotencyKeyExpiresAt ?? undefined,
256257
tags: token.tags ? token.tags.sort((a, b) => a.localeCompare(b)) : [],
257-
//we can assume that all errors for tokens are timeouts
258-
isTimeout: token.outputIsError,
259258
createdAt: token.createdAt,
260259
})),
261260
pagination: {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { json } from "@remix-run/server-runtime";
2+
import { type WaitpointRetrieveTokenResponse } from "@trigger.dev/core/v3";
3+
import { z } from "zod";
4+
import { ApiWaitpointPresenter } from "~/presenters/v3/ApiWaitpointPresenter.server";
5+
import { createLoaderApiRoute } from "~/services/routeBuilders/apiBuilder.server";
6+
7+
export const loader = createLoaderApiRoute(
8+
{
9+
params: z.object({
10+
waitpointFriendlyId: z.string(),
11+
}),
12+
findResource: async () => 1, // This is a dummy function, we don't need to find a resource
13+
},
14+
async ({ params, authentication }) => {
15+
const presenter = new ApiWaitpointPresenter();
16+
const result: WaitpointRetrieveTokenResponse = await presenter.call(
17+
authentication.environment,
18+
params.waitpointFriendlyId
19+
);
20+
return json(result);
21+
}
22+
);

packages/core/src/v3/apiClient/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
WaitForDurationRequestBody,
3939
WaitForDurationResponseBody,
4040
WaitForWaitpointTokenResponseBody,
41+
WaitpointRetrieveTokenResponse,
4142
WaitpointTokenItem,
4243
} from "../schemas/index.js";
4344
import { taskContext } from "../task-context-api.js";
@@ -697,7 +698,7 @@ export class ApiClient {
697698

698699
retrieveWaitpointToken(friendlyId: string, requestOptions?: ZodFetchOptions) {
699700
return zodfetch(
700-
WaitpointTokenItem,
701+
WaitpointRetrieveTokenResponse,
701702
`${this.baseUrl}/api/v1/waitpoints/tokens/${friendlyId}`,
702703
{
703704
method: "GET",

packages/core/src/v3/schemas/api.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -957,14 +957,28 @@ export const WaitpointTokenItem = z.object({
957957
status: WaitpointTokenStatus,
958958
completedAt: z.coerce.date().optional(),
959959
completedAfter: z.coerce.date().optional(),
960+
timeoutAt: z.coerce.date().optional(),
960961
idempotencyKey: z.string().optional(),
961962
idempotencyKeyExpiresAt: z.coerce.date().optional(),
962963
tags: z.array(z.string()),
963-
isTimeout: z.boolean(),
964964
createdAt: z.coerce.date(),
965965
});
966966
export type WaitpointTokenItem = z.infer<typeof WaitpointTokenItem>;
967967

968+
export const WaitpointListTokenItem = WaitpointTokenItem.omit({
969+
completedAfter: true,
970+
});
971+
export type WaitpointListTokenItem = z.infer<typeof WaitpointListTokenItem>;
972+
973+
export const WaitpointRetrieveTokenResponse = WaitpointListTokenItem.and(
974+
z.object({
975+
output: z.string().optional(),
976+
outputType: z.string().optional(),
977+
outputIsError: z.boolean().optional(),
978+
})
979+
);
980+
export type WaitpointRetrieveTokenResponse = z.infer<typeof WaitpointRetrieveTokenResponse>;
981+
968982
export const CompleteWaitpointTokenRequestBody = z.object({
969983
data: z.any().nullish(),
970984
});

packages/trigger-sdk/src/v3/wait.ts

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import {
1616
CursorPagePromise,
1717
WaitpointTokenItem,
1818
flattenAttributes,
19+
WaitpointListTokenItem,
20+
WaitpointTokenStatus,
1921
} from "@trigger.dev/core/v3";
2022
import { tracer } from "./tracer.js";
2123
import { conditionallyImportAndParsePacket } from "@trigger.dev/core/v3/utils/ioSerialization";
@@ -120,7 +122,7 @@ function createToken(
120122
function listTokens(
121123
params?: ListWaitpointTokensQueryParams,
122124
requestOptions?: ApiRequestOptions
123-
): CursorPagePromise<typeof WaitpointTokenItem> {
125+
): CursorPagePromise<typeof WaitpointListTokenItem> {
124126
const apiClient = apiClientManager.clientOrThrow();
125127

126128
const $requestOptions = mergeRequestOptions(
@@ -138,6 +140,26 @@ function listTokens(
138140
return apiClient.listWaitpointTokens(params, $requestOptions);
139141
}
140142

143+
/**
144+
* A waitpoint token that has been retrieved.
145+
*
146+
* If the status is `WAITING`, this means the waitpoint is still pending.
147+
* For `COMPLETED` the `output` will be the data you passed in when completing the waitpoint.
148+
* For `TIMED_OUT` there will be an `error`.
149+
*/
150+
export type WaitpointRetrievedToken<T> = {
151+
id: string;
152+
status: WaitpointTokenStatus;
153+
completedAt?: Date;
154+
timeoutAt?: Date;
155+
idempotencyKey?: string;
156+
idempotencyKeyExpiresAt?: Date;
157+
tags: string[];
158+
createdAt: Date;
159+
output?: T;
160+
error?: Error;
161+
};
162+
141163
/**
142164
* Retrieves a waitpoint token by its ID.
143165
*
@@ -151,12 +173,12 @@ function listTokens(
151173
* @param token - The token to retrieve.
152174
* This can be a string token ID or an object with an `id` property.
153175
* @param requestOptions - Optional API request options.
154-
* @returns The waitpoint token details.
176+
* @returns The waitpoint token details, including the output or error if the waitpoint is completed or timed out.
155177
*/
156-
function retrieveToken(
178+
async function retrieveToken<T>(
157179
token: string | { id: string },
158180
requestOptions?: ApiRequestOptions
159-
): ApiPromise<WaitpointTokenItem> {
181+
): Promise<WaitpointRetrievedToken<T>> {
160182
const apiClient = apiClientManager.clientOrThrow();
161183

162184
const $tokenId = typeof token === "string" ? token : token.id;
@@ -182,7 +204,36 @@ function retrieveToken(
182204
requestOptions
183205
);
184206

185-
return apiClient.retrieveWaitpointToken($tokenId, $requestOptions);
207+
const result = await apiClient.retrieveWaitpointToken($tokenId, $requestOptions);
208+
209+
const data = result.output
210+
? await conditionallyImportAndParsePacket(
211+
{ data: result.output, dataType: result.outputType ?? "application/json" },
212+
apiClient
213+
)
214+
: undefined;
215+
216+
let error: Error | undefined = undefined;
217+
let output: T | undefined = undefined;
218+
219+
if (result.outputIsError) {
220+
error = new WaitpointTimeoutError(data.message);
221+
} else {
222+
output = data as T;
223+
}
224+
225+
return {
226+
id: result.id,
227+
status: result.status,
228+
completedAt: result.completedAt,
229+
timeoutAt: result.timeoutAt,
230+
idempotencyKey: result.idempotencyKey,
231+
idempotencyKeyExpiresAt: result.idempotencyKeyExpiresAt,
232+
tags: result.tags,
233+
createdAt: result.createdAt,
234+
output,
235+
error,
236+
};
186237
}
187238

188239
/**

references/hello-world/src/trigger/waits.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ export const waitToken = task({
5151
logger.log("Token", token);
5252
}
5353

54+
const retrievedToken = await wait.retrieveToken(token.id);
55+
logger.log("Retrieved token", retrievedToken);
56+
5457
//wait for the token
5558
const result = await wait.forToken<{ foo: string }>(token, { releaseConcurrency: true });
5659
if (!result.ok) {
@@ -63,6 +66,9 @@ export const waitToken = task({
6366
for await (const token of tokens2) {
6467
logger.log("Token2", token);
6568
}
69+
70+
const retrievedToken2 = await wait.retrieveToken(token.id);
71+
logger.log("Retrieved token2", retrievedToken2);
6672
},
6773
});
6874

0 commit comments

Comments
 (0)