Skip to content

Commit b3dfc5e

Browse files
committed
Loads more stuff
1 parent 8a8a853 commit b3dfc5e

File tree

28 files changed

+1056
-247
lines changed

28 files changed

+1056
-247
lines changed

apps/webapp/app/env.server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1234,7 +1234,7 @@ const EnvironmentSchema = z
12341234
.default("info"),
12351235
REALTIME_STREAMS_S2_FLUSH_INTERVAL_MS: z.coerce.number().int().default(100),
12361236
REALTIME_STREAMS_S2_MAX_RETRIES: z.coerce.number().int().default(10),
1237-
REALTIME_STREAMS_S2_WAIT_SECONDS: z.coerce.number().int().default(600),
1237+
REALTIME_STREAMS_S2_WAIT_SECONDS: z.coerce.number().int().default(60),
12381238
})
12391239
.and(GithubAppEnvSchema)
12401240
.and(S2EnvSchema);

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,36 @@ export class SpanPresenter extends BasePresenter {
551551
},
552552
};
553553
}
554+
case "realtime-stream": {
555+
if (!span.entity.id) {
556+
logger.error(`SpanPresenter: No realtime stream id`, {
557+
spanId,
558+
realtimeStreamId: span.entity.id,
559+
});
560+
return { ...data, entity: null };
561+
}
562+
563+
const [runId, streamKey] = span.entity.id.split(":");
564+
565+
if (!runId || !streamKey) {
566+
logger.error(`SpanPresenter: Invalid realtime stream id`, {
567+
spanId,
568+
realtimeStreamId: span.entity.id,
569+
});
570+
return { ...data, entity: null };
571+
}
572+
573+
return {
574+
...data,
575+
entity: {
576+
type: "realtime-stream" as const,
577+
object: {
578+
runId,
579+
streamKey,
580+
},
581+
},
582+
};
583+
}
554584
default:
555585
return { ...data, entity: null };
556586
}

apps/webapp/app/routes/realtime.v1.streams.$runId.$streamId.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,29 @@ export const loader = createLoaderApiRoute(
4343
// Get Last-Event-ID header for resuming from a specific position
4444
const lastEventId = request.headers.get("Last-Event-ID") || undefined;
4545

46+
const timeoutInSecondsRaw = request.headers.get("Timeout-Seconds") ?? undefined;
47+
const timeoutInSeconds = timeoutInSecondsRaw ? parseInt(timeoutInSecondsRaw) : undefined;
48+
49+
if (timeoutInSeconds && isNaN(timeoutInSeconds)) {
50+
return new Response("Invalid timeout seconds", { status: 400 });
51+
}
52+
53+
if (timeoutInSeconds && timeoutInSeconds < 1) {
54+
return new Response("Timeout seconds must be greater than 0", { status: 400 });
55+
}
56+
57+
if (timeoutInSeconds && timeoutInSeconds > 600) {
58+
return new Response("Timeout seconds must be less than 600", { status: 400 });
59+
}
60+
4661
const realtimeStream = getRealtimeStreamInstance(
4762
authentication.environment,
4863
run.realtimeStreamsVersion
4964
);
5065

51-
return realtimeStream.streamResponse(
52-
request,
53-
run.friendlyId,
54-
params.streamId,
55-
request.signal,
56-
lastEventId
57-
);
66+
return realtimeStream.streamResponse(request, run.friendlyId, params.streamId, request.signal, {
67+
lastEventId,
68+
timeoutInSeconds,
69+
});
5870
}
5971
);

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ import { createTimelineSpanEventsFromSpanEvents } from "~/utils/timelineSpanEven
8080
import { CompleteWaitpointForm } from "../resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.$waitpointFriendlyId.complete/route";
8181
import { requireUserId } from "~/services/session.server";
8282
import type { SpanOverride } from "~/v3/eventRepository/eventRepository.types";
83+
import { RealtimeStreamViewer } from "../resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.streams.$streamKey/route";
8384

8485
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
8586
const userId = await requireUserId(request);
@@ -1146,6 +1147,24 @@ function SpanEntity({ span }: { span: Span }) {
11461147
</div>
11471148
);
11481149
}
1150+
case "realtime-stream": {
1151+
return (
1152+
<div className="grid h-full grid-rows-[1fr_auto]">
1153+
<div className="flex flex-col gap-4 overflow-y-auto px-3 pt-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
1154+
<div>
1155+
<Header2>Realtime stream</Header2>
1156+
<Paragraph variant="small">
1157+
A realtime stream is a stream of data that is sent to the client.
1158+
</Paragraph>
1159+
</div>
1160+
</div>
1161+
<RealtimeStreamViewer
1162+
runId={span.entity.object.runId}
1163+
streamKey={span.entity.object.streamKey}
1164+
/>
1165+
</div>
1166+
);
1167+
}
11491168
default: {
11501169
assertNever(span.entity);
11511170
}

0 commit comments

Comments
 (0)