@codemation/host 0.2.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/dist/{AppConfigFactory-CqKWXqOm.js → AppConfigFactory-BPp02HMv.js} +82 -5
- package/dist/{AppConfigFactory-CqKWXqOm.js.map → AppConfigFactory-BPp02HMv.js.map} +1 -1
- package/dist/{AppConfigFactory-CK28UPK0.d.ts → AppConfigFactory-Dq7ttwQ_.d.ts} +6001 -144
- package/dist/{AppContainerFactory-CcSGFNLW.js → AppContainerFactory-D9je1sSV.js} +2604 -155
- package/dist/AppContainerFactory-D9je1sSV.js.map +1 -0
- package/dist/{CodemationAppContext-KqDoeHqN.d.ts → CodemationAppContext-P7P-xZhQ.d.ts} +2 -2
- package/dist/{CodemationAuthoring.types-BP6Inucu.d.ts → CodemationAuthoring.types-OMYu7vKP.d.ts} +3 -3
- package/dist/{CodemationConfigNormalizer-DIAE0VHw.d.ts → CodemationConfigNormalizer-BCtBrJDe.d.ts} +2 -2
- package/dist/{CodemationConsumerConfigLoader-nj9kTmfJ.d.ts → CodemationConsumerConfigLoader-evvw4b_a.d.ts} +2 -2
- package/dist/CodemationPluginListMerger-PSTtEQjC.d.ts +674 -0
- package/dist/{CredentialServices-BFQD_VN1.d.ts → CredentialServices-0Hk8RFY1.d.ts} +3 -3
- package/dist/{CredentialServices-BPKUF8Xs.js → CredentialServices-BNBMFOPt.js} +6 -1
- package/dist/CredentialServices-BNBMFOPt.js.map +1 -0
- package/dist/{PublicFrontendBootstrapFactory-DTA1iDo0.d.ts → PublicFrontendBootstrapFactory-D0_ds7nS.d.ts} +2 -2
- package/dist/authoring.d.ts +3 -3
- package/dist/consumer.d.ts +4 -4
- package/dist/credentials.d.ts +3 -3
- package/dist/credentials.js +1 -1
- package/dist/devServerSidecar.d.ts +1 -1
- package/dist/{index-Dd6BrWyH.d.ts → index-CeS2saCe.d.ts} +105 -2
- package/dist/index.d.ts +12 -11
- package/dist/index.js +5 -5
- package/dist/nextServer.d.ts +8 -58
- package/dist/nextServer.js +6 -100
- package/dist/{persistenceServer-DKbFDxoS.js → persistenceServer-CA0_q0D7.js} +2 -2
- package/dist/{persistenceServer-DKbFDxoS.js.map → persistenceServer-CA0_q0D7.js.map} +1 -1
- package/dist/{persistenceServer-Y-u7lV7f.d.ts → persistenceServer-CJeu1STC.d.ts} +2 -2
- package/dist/persistenceServer.d.ts +5 -5
- package/dist/persistenceServer.js +2 -2
- package/dist/{server-CFpgKuVE.js → server-C_ZIEOTY.js} +4 -4
- package/dist/{server-CFpgKuVE.js.map → server-C_ZIEOTY.js.map} +1 -1
- package/dist/{server-axppTMgo.d.ts → server-Clvg5x1w.d.ts} +11 -5
- package/dist/server.d.ts +8 -8
- package/dist/server.js +5 -5
- package/package.json +6 -5
- package/prisma/migrations/20260414120000_telemetry_foundation/migration.sql +112 -0
- package/prisma/migrations/20260414153000_telemetry_retention_metrics_refactor/migration.sql +239 -0
- package/prisma/migrations.sqlite/20260414120000_telemetry_foundation/migration.sql +103 -0
- package/prisma/migrations.sqlite/20260414153000_telemetry_retention_metrics_refactor/migration.sql +540 -0
- package/prisma/schema.postgresql.prisma +100 -1
- package/prisma/schema.sqlite.prisma +100 -1
- package/scripts/generate-prisma-clients.mjs +89 -1
- package/src/application/contracts/TelemetryDashboardContracts.ts +113 -0
- package/src/application/contracts/TelemetryRunTraceContracts.ts +13 -0
- package/src/application/cost/FrameworkCostCatalogEntries.ts +126 -0
- package/src/application/queries/GetTelemetryDashboardDimensionsQuery.ts +11 -0
- package/src/application/queries/GetTelemetryDashboardDimensionsQueryHandler.ts +20 -0
- package/src/application/queries/GetTelemetryDashboardRunsQuery.ts +11 -0
- package/src/application/queries/GetTelemetryDashboardRunsQueryHandler.ts +20 -0
- package/src/application/queries/GetTelemetryDashboardSummaryQuery.ts +11 -0
- package/src/application/queries/GetTelemetryDashboardSummaryQueryHandler.ts +29 -0
- package/src/application/queries/GetTelemetryDashboardTimeseriesQuery.ts +11 -0
- package/src/application/queries/GetTelemetryDashboardTimeseriesQueryHandler.ts +36 -0
- package/src/application/queries/GetTelemetryRunTraceQuery.ts +8 -0
- package/src/application/queries/GetTelemetryRunTraceQueryHandler.ts +20 -0
- package/src/application/queries/WorkflowQueryHandlers.ts +5 -0
- package/src/application/runs/WorkflowRunRetentionPruneScheduler.ts +71 -26
- package/src/application/telemetry/CompositeTelemetryExporter.ts +13 -0
- package/src/application/telemetry/LazyExecutionTelemetryFactory.ts +21 -0
- package/src/application/telemetry/NoOpTelemetryExporter.ts +7 -0
- package/src/application/telemetry/OtelExecutionTelemetry.types.ts +41 -0
- package/src/application/telemetry/OtelExecutionTelemetryFactory.ts +56 -0
- package/src/application/telemetry/OtelIdentityFactory.ts +41 -0
- package/src/application/telemetry/RunEventBusTelemetryReporter.ts +188 -0
- package/src/application/telemetry/StoredExecutionTelemetry.ts +56 -0
- package/src/application/telemetry/StoredNodeExecutionTelemetry.ts +35 -0
- package/src/application/telemetry/StoredTelemetrySpanScope.ts +188 -0
- package/src/application/telemetry/TelemetryEnricherChain.ts +85 -0
- package/src/application/telemetry/TelemetryPrivacyPolicy.ts +19 -0
- package/src/application/telemetry/TelemetryQueryService.ts +815 -0
- package/src/application/telemetry/TelemetryRetentionTimestampFactory.ts +40 -0
- package/src/applicationTokens.ts +18 -0
- package/src/bootstrap/AppContainerFactory.ts +124 -1
- package/src/bootstrap/AppContainerLifecycle.ts +8 -0
- package/src/bootstrap/runtime/FrontendRuntime.ts +8 -0
- package/src/bootstrap/runtime/WorkerRuntime.ts +8 -0
- package/src/domain/runs/WorkflowRunRepository.ts +3 -1
- package/src/domain/telemetry/TelemetryContracts.ts +197 -0
- package/src/infrastructure/persistence/InMemoryRunTraceContextRepository.ts +56 -0
- package/src/infrastructure/persistence/InMemoryTelemetryArtifactStore.ts +56 -0
- package/src/infrastructure/persistence/InMemoryTelemetryMetricPointStore.ts +97 -0
- package/src/infrastructure/persistence/InMemoryTelemetrySpanStore.ts +113 -0
- package/src/infrastructure/persistence/InMemoryWorkflowRunRepository.ts +4 -2
- package/src/infrastructure/persistence/PrismaRunTraceContextRepository.ts +92 -0
- package/src/infrastructure/persistence/PrismaTelemetryArtifactStore.ts +125 -0
- package/src/infrastructure/persistence/PrismaTelemetryMetricPointStore.ts +134 -0
- package/src/infrastructure/persistence/PrismaTelemetrySpanStore.ts +166 -0
- package/src/infrastructure/persistence/PrismaWorkflowRunRepository.ts +20 -7
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/edge.js +85 -4
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/index-browser.js +81 -0
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/index.d.ts +6488 -102
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/index.js +85 -4
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/package.json +1 -1
- package/src/infrastructure/persistence/generated/prisma-postgresql-client/schema.prisma +100 -0
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/edge.js +85 -4
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/index-browser.js +81 -0
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/index.d.ts +6476 -98
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/index.js +85 -4
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/package.json +1 -1
- package/src/infrastructure/persistence/generated/prisma-sqlite-client/schema.prisma +100 -0
- package/src/presentation/http/ApiPaths.ts +22 -0
- package/src/presentation/http/hono/registrars/TelemetryHonoApiRouteRegistrar.ts +19 -0
- package/src/presentation/http/routeHandlers/TelemetryDashboardRequestError.ts +1 -0
- package/src/presentation/http/routeHandlers/TelemetryHttpRouteHandler.ts +181 -0
- package/dist/AppContainerFactory-CcSGFNLW.js.map +0 -1
- package/dist/CodemationPluginListMerger-QvUa2SIt.d.ts +0 -357
- package/dist/CredentialServices-BPKUF8Xs.js.map +0 -1
- package/dist/nextServer.js.map +0 -1
|
@@ -140,6 +140,106 @@ model TriggerSetupState {
|
|
|
140
140
|
@@id([workflowId, nodeId])
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
model RunTraceContext {
|
|
144
|
+
runId String @id @map("run_id")
|
|
145
|
+
workflowId String @map("workflow_id")
|
|
146
|
+
traceId String @unique @map("trace_id")
|
|
147
|
+
rootSpanId String @map("root_span_id")
|
|
148
|
+
serviceName String? @map("service_name")
|
|
149
|
+
createdAt String @map("created_at")
|
|
150
|
+
expiresAt String? @map("expires_at")
|
|
151
|
+
|
|
152
|
+
@@index([workflowId, createdAt])
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
model TelemetrySpan {
|
|
156
|
+
telemetrySpanId String @id @map("telemetry_span_id")
|
|
157
|
+
traceId String @map("trace_id")
|
|
158
|
+
spanId String @map("span_id")
|
|
159
|
+
parentSpanId String? @map("parent_span_id")
|
|
160
|
+
runId String @map("run_id")
|
|
161
|
+
workflowId String @map("workflow_id")
|
|
162
|
+
nodeId String? @map("node_id")
|
|
163
|
+
activationId String? @map("activation_id")
|
|
164
|
+
connectionInvocationId String? @map("connection_invocation_id")
|
|
165
|
+
name String
|
|
166
|
+
kind String
|
|
167
|
+
status String?
|
|
168
|
+
statusMessage String? @map("status_message")
|
|
169
|
+
startTime String? @map("start_time")
|
|
170
|
+
endTime String? @map("end_time")
|
|
171
|
+
workflowFolder String? @map("workflow_folder")
|
|
172
|
+
nodeType String? @map("node_type")
|
|
173
|
+
nodeRole String? @map("node_role")
|
|
174
|
+
modelName String? @map("model_name")
|
|
175
|
+
attributesJson String? @map("attributes_json")
|
|
176
|
+
eventsJson String? @map("events_json")
|
|
177
|
+
retentionExpiresAt String? @map("retention_expires_at")
|
|
178
|
+
updatedAt String @map("updated_at")
|
|
179
|
+
|
|
180
|
+
@@unique([traceId, spanId])
|
|
181
|
+
@@index([traceId, startTime])
|
|
182
|
+
@@index([workflowId, endTime])
|
|
183
|
+
@@index([workflowId, status, endTime])
|
|
184
|
+
@@index([runId, endTime])
|
|
185
|
+
@@index([modelName, endTime])
|
|
186
|
+
@@index([connectionInvocationId])
|
|
187
|
+
@@index([retentionExpiresAt])
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
model TelemetryArtifact {
|
|
191
|
+
artifactId String @id @map("artifact_id")
|
|
192
|
+
traceId String @map("trace_id")
|
|
193
|
+
spanId String @map("span_id")
|
|
194
|
+
runId String @map("run_id")
|
|
195
|
+
workflowId String @map("workflow_id")
|
|
196
|
+
nodeId String? @map("node_id")
|
|
197
|
+
activationId String? @map("activation_id")
|
|
198
|
+
kind String
|
|
199
|
+
contentType String @map("content_type")
|
|
200
|
+
previewText String? @map("preview_text")
|
|
201
|
+
previewJson String? @map("preview_json")
|
|
202
|
+
payloadText String? @map("payload_text")
|
|
203
|
+
payloadJson String? @map("payload_json")
|
|
204
|
+
bytes Int?
|
|
205
|
+
truncated Boolean?
|
|
206
|
+
createdAt String @map("created_at")
|
|
207
|
+
expiresAt String? @map("expires_at")
|
|
208
|
+
retentionExpiresAt String? @map("retention_expires_at")
|
|
209
|
+
|
|
210
|
+
@@index([traceId, createdAt])
|
|
211
|
+
@@index([spanId, createdAt])
|
|
212
|
+
@@index([runId, createdAt])
|
|
213
|
+
@@index([retentionExpiresAt])
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
model TelemetryMetricPoint {
|
|
217
|
+
metricPointId String @id @map("metric_point_id")
|
|
218
|
+
traceId String? @map("trace_id")
|
|
219
|
+
spanId String? @map("span_id")
|
|
220
|
+
runId String? @map("run_id")
|
|
221
|
+
workflowId String @map("workflow_id")
|
|
222
|
+
nodeId String? @map("node_id")
|
|
223
|
+
activationId String? @map("activation_id")
|
|
224
|
+
metricName String @map("metric_name")
|
|
225
|
+
value Float
|
|
226
|
+
unit String?
|
|
227
|
+
observedAt String @map("observed_at")
|
|
228
|
+
workflowFolder String? @map("workflow_folder")
|
|
229
|
+
nodeType String? @map("node_type")
|
|
230
|
+
nodeRole String? @map("node_role")
|
|
231
|
+
modelName String? @map("model_name")
|
|
232
|
+
dimensionsJson String? @map("dimensions_json")
|
|
233
|
+
retentionExpiresAt String? @map("retention_expires_at")
|
|
234
|
+
|
|
235
|
+
@@index([workflowId, observedAt])
|
|
236
|
+
@@index([workflowId, metricName, observedAt])
|
|
237
|
+
@@index([runId, observedAt])
|
|
238
|
+
@@index([traceId, observedAt])
|
|
239
|
+
@@index([modelName, observedAt])
|
|
240
|
+
@@index([retentionExpiresAt])
|
|
241
|
+
}
|
|
242
|
+
|
|
143
243
|
model CredentialInstance {
|
|
144
244
|
instanceId String @id @map("instance_id")
|
|
145
245
|
typeId String @map("type_id")
|
|
@@ -13,6 +13,8 @@ export class ApiPaths {
|
|
|
13
13
|
|
|
14
14
|
private static readonly usersBasePath = `${this.apiBasePath}/users`;
|
|
15
15
|
|
|
16
|
+
private static readonly telemetryBasePath = `${this.apiBasePath}/telemetry`;
|
|
17
|
+
|
|
16
18
|
private static readonly whitelabelBasePath = `${this.apiBasePath}/whitelabel`;
|
|
17
19
|
|
|
18
20
|
private static readonly bootstrapBasePath = `${this.apiBasePath}/bootstrap`;
|
|
@@ -122,6 +124,26 @@ export class ApiPaths {
|
|
|
122
124
|
return this.usersBasePath;
|
|
123
125
|
}
|
|
124
126
|
|
|
127
|
+
static telemetryDashboardSummary(): string {
|
|
128
|
+
return `${this.telemetryBasePath}/dashboard/summary`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static telemetryDashboardTimeseries(): string {
|
|
132
|
+
return `${this.telemetryBasePath}/dashboard/timeseries`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
static telemetryDashboardDimensions(): string {
|
|
136
|
+
return `${this.telemetryBasePath}/dashboard/dimensions`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
static telemetryDashboardRuns(): string {
|
|
140
|
+
return `${this.telemetryBasePath}/dashboard/runs`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static telemetryRunTrace(runId: string): string {
|
|
144
|
+
return `${this.telemetryBasePath}/runs/${runId}/trace`;
|
|
145
|
+
}
|
|
146
|
+
|
|
125
147
|
static authSession(): string {
|
|
126
148
|
return `${this.authBasePath}/session`;
|
|
127
149
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { inject, injectable, registry } from "@codemation/core";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { ApplicationTokens } from "../../../../applicationTokens";
|
|
4
|
+
import { TelemetryHttpRouteHandler } from "../../routeHandlers/TelemetryHttpRouteHandler";
|
|
5
|
+
import type { HonoApiRouteRegistrar } from "../HonoApiRouteRegistrar";
|
|
6
|
+
|
|
7
|
+
@injectable()
|
|
8
|
+
@registry([{ token: ApplicationTokens.HonoApiRouteRegistrar, useClass: TelemetryHonoApiRouteRegistrar }])
|
|
9
|
+
export class TelemetryHonoApiRouteRegistrar implements HonoApiRouteRegistrar {
|
|
10
|
+
constructor(@inject(TelemetryHttpRouteHandler) private readonly handler: TelemetryHttpRouteHandler) {}
|
|
11
|
+
|
|
12
|
+
register(app: Hono): void {
|
|
13
|
+
app.get("/telemetry/dashboard/summary", (c) => this.handler.getDashboardSummary(c.req.raw));
|
|
14
|
+
app.get("/telemetry/dashboard/timeseries", (c) => this.handler.getDashboardTimeseries(c.req.raw));
|
|
15
|
+
app.get("/telemetry/dashboard/dimensions", (c) => this.handler.getDashboardDimensions(c.req.raw));
|
|
16
|
+
app.get("/telemetry/dashboard/runs", (c) => this.handler.getDashboardRuns(c.req.raw));
|
|
17
|
+
app.get("/telemetry/runs/:runId/trace", (c) => this.handler.getRunTrace(c.req.param("runId")));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export class TelemetryDashboardRequestError extends Error {}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { inject, injectable } from "@codemation/core";
|
|
2
|
+
import type {
|
|
3
|
+
TelemetryDashboardFiltersDto,
|
|
4
|
+
TelemetryDashboardRunsRequestDto,
|
|
5
|
+
TelemetryDashboardTimeseriesRequestDto,
|
|
6
|
+
} from "../../../application/contracts/TelemetryDashboardContracts";
|
|
7
|
+
import type { QueryBus } from "../../../application/bus/QueryBus";
|
|
8
|
+
import { GetTelemetryDashboardDimensionsQuery } from "../../../application/queries/GetTelemetryDashboardDimensionsQuery";
|
|
9
|
+
import { GetTelemetryDashboardRunsQuery } from "../../../application/queries/GetTelemetryDashboardRunsQuery";
|
|
10
|
+
import { GetTelemetryRunTraceQuery } from "../../../application/queries/GetTelemetryRunTraceQuery";
|
|
11
|
+
import { GetTelemetryDashboardSummaryQuery } from "../../../application/queries/GetTelemetryDashboardSummaryQuery";
|
|
12
|
+
import { GetTelemetryDashboardTimeseriesQuery } from "../../../application/queries/GetTelemetryDashboardTimeseriesQuery";
|
|
13
|
+
import { ApplicationTokens } from "../../../applicationTokens";
|
|
14
|
+
import { ServerHttpErrorResponseFactory } from "../ServerHttpErrorResponseFactory";
|
|
15
|
+
import { TelemetryDashboardRequestError } from "./TelemetryDashboardRequestError";
|
|
16
|
+
|
|
17
|
+
@injectable()
|
|
18
|
+
export class TelemetryHttpRouteHandler {
|
|
19
|
+
constructor(
|
|
20
|
+
@inject(ApplicationTokens.QueryBus)
|
|
21
|
+
private readonly queryBus: QueryBus,
|
|
22
|
+
) {}
|
|
23
|
+
|
|
24
|
+
async getDashboardSummary(request: Request): Promise<Response> {
|
|
25
|
+
try {
|
|
26
|
+
const filters = this.parseFilters(request);
|
|
27
|
+
return Response.json(await this.queryBus.execute(new GetTelemetryDashboardSummaryQuery(filters)));
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (error instanceof TelemetryDashboardRequestError) {
|
|
30
|
+
return Response.json({ error: error.message }, { status: 400 });
|
|
31
|
+
}
|
|
32
|
+
return ServerHttpErrorResponseFactory.fromUnknown(error);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async getDashboardTimeseries(request: Request): Promise<Response> {
|
|
37
|
+
try {
|
|
38
|
+
const parsed = this.parseTimeseriesRequest(request);
|
|
39
|
+
return Response.json(await this.queryBus.execute(new GetTelemetryDashboardTimeseriesQuery(parsed)));
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (error instanceof TelemetryDashboardRequestError) {
|
|
42
|
+
return Response.json({ error: error.message }, { status: 400 });
|
|
43
|
+
}
|
|
44
|
+
return ServerHttpErrorResponseFactory.fromUnknown(error);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async getDashboardDimensions(request: Request): Promise<Response> {
|
|
49
|
+
try {
|
|
50
|
+
const filters = this.parseFilters(request);
|
|
51
|
+
return Response.json(await this.queryBus.execute(new GetTelemetryDashboardDimensionsQuery(filters)));
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (error instanceof TelemetryDashboardRequestError) {
|
|
54
|
+
return Response.json({ error: error.message }, { status: 400 });
|
|
55
|
+
}
|
|
56
|
+
return ServerHttpErrorResponseFactory.fromUnknown(error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getDashboardRuns(request: Request): Promise<Response> {
|
|
61
|
+
try {
|
|
62
|
+
const parsed = this.parseRunsRequest(request);
|
|
63
|
+
return Response.json(await this.queryBus.execute(new GetTelemetryDashboardRunsQuery(parsed)));
|
|
64
|
+
} catch (error) {
|
|
65
|
+
if (error instanceof TelemetryDashboardRequestError) {
|
|
66
|
+
return Response.json({ error: error.message }, { status: 400 });
|
|
67
|
+
}
|
|
68
|
+
return ServerHttpErrorResponseFactory.fromUnknown(error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async getRunTrace(runId: string): Promise<Response> {
|
|
73
|
+
try {
|
|
74
|
+
if (!runId.trim()) {
|
|
75
|
+
throw new TelemetryDashboardRequestError("Run trace request requires a run id.");
|
|
76
|
+
}
|
|
77
|
+
return Response.json(await this.queryBus.execute(new GetTelemetryRunTraceQuery(runId)));
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (error instanceof TelemetryDashboardRequestError) {
|
|
80
|
+
return Response.json({ error: error.message }, { status: 400 });
|
|
81
|
+
}
|
|
82
|
+
return ServerHttpErrorResponseFactory.fromUnknown(error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private parseTimeseriesRequest(request: Request): TelemetryDashboardTimeseriesRequestDto {
|
|
87
|
+
const url = new URL(request.url);
|
|
88
|
+
const interval = url.searchParams.get("interval");
|
|
89
|
+
if (
|
|
90
|
+
interval !== "minute_5" &&
|
|
91
|
+
interval !== "minute_15" &&
|
|
92
|
+
interval !== "hour" &&
|
|
93
|
+
interval !== "day" &&
|
|
94
|
+
interval !== "week"
|
|
95
|
+
) {
|
|
96
|
+
throw new TelemetryDashboardRequestError("Query string must include interval=minute_5|minute_15|hour|day|week.");
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
interval,
|
|
100
|
+
filters: this.parseFilters(request),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private parseRunsRequest(request: Request): TelemetryDashboardRunsRequestDto {
|
|
105
|
+
const url = new URL(request.url);
|
|
106
|
+
return {
|
|
107
|
+
filters: this.parseFilters(request),
|
|
108
|
+
page: this.readPositiveInt(url, "page", 1),
|
|
109
|
+
pageSize: this.readPositiveInt(url, "pageSize", 10),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private parseFilters(request: Request): TelemetryDashboardFiltersDto {
|
|
114
|
+
const url = new URL(request.url);
|
|
115
|
+
return {
|
|
116
|
+
workflowIds: this.readMany(url, "workflowId"),
|
|
117
|
+
statuses: this.readStatuses(url),
|
|
118
|
+
runOrigins: this.readRunOrigins(url),
|
|
119
|
+
modelNames: this.readMany(url, "modelName"),
|
|
120
|
+
startTimeGte: this.readIso(url, "startTimeGte"),
|
|
121
|
+
endTimeLte: this.readIso(url, "endTimeLte"),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private readMany(url: URL, key: string): ReadonlyArray<string> | undefined {
|
|
126
|
+
const values = url.searchParams
|
|
127
|
+
.getAll(key)
|
|
128
|
+
.map((value) => value.trim())
|
|
129
|
+
.filter((value) => value.length > 0);
|
|
130
|
+
return values.length > 0 ? values : undefined;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private readStatuses(url: URL): TelemetryDashboardFiltersDto["statuses"] {
|
|
134
|
+
const values = this.readMany(url, "status");
|
|
135
|
+
if (!values) {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
for (const value of values) {
|
|
139
|
+
if (value !== "running" && value !== "completed" && value !== "failed") {
|
|
140
|
+
throw new TelemetryDashboardRequestError(`Unsupported telemetry status filter: ${value}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return values as TelemetryDashboardFiltersDto["statuses"];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private readRunOrigins(url: URL): TelemetryDashboardFiltersDto["runOrigins"] {
|
|
147
|
+
const values = this.readMany(url, "runOrigin");
|
|
148
|
+
if (!values) {
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
for (const value of values) {
|
|
152
|
+
if (value !== "triggered" && value !== "manual") {
|
|
153
|
+
throw new TelemetryDashboardRequestError(`Unsupported telemetry run origin filter: ${value}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return values as TelemetryDashboardFiltersDto["runOrigins"];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private readIso(url: URL, key: string): string | undefined {
|
|
160
|
+
const value = url.searchParams.get(key)?.trim();
|
|
161
|
+
if (!value) {
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
if (Number.isNaN(new Date(value).getTime())) {
|
|
165
|
+
throw new TelemetryDashboardRequestError(`Invalid ISO timestamp for ${key}.`);
|
|
166
|
+
}
|
|
167
|
+
return value;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private readPositiveInt(url: URL, key: string, fallback: number): number {
|
|
171
|
+
const raw = url.searchParams.get(key)?.trim();
|
|
172
|
+
if (!raw) {
|
|
173
|
+
return fallback;
|
|
174
|
+
}
|
|
175
|
+
const value = Number(raw);
|
|
176
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
177
|
+
throw new TelemetryDashboardRequestError(`${key} must be a positive integer.`);
|
|
178
|
+
}
|
|
179
|
+
return value;
|
|
180
|
+
}
|
|
181
|
+
}
|