@ancplua/qyl-api-schema 0.2.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/LICENSE +201 -0
- package/README.md +89 -0
- package/VERSIONING.md +74 -0
- package/api/routes.tsp +1052 -0
- package/api/streaming.tsp +335 -0
- package/common/errors.tsp +206 -0
- package/common/pagination.tsp +254 -0
- package/common/types.tsp +345 -0
- package/generated/README.md +26 -0
- package/generated/otel-keys.gen.tsp +1648 -0
- package/index.tsp +55 -0
- package/intelligence/causal-rules.tsp +34 -0
- package/intelligence/diagnostic-patterns.tsp +54 -0
- package/intelligence/investigation-strategies.tsp +37 -0
- package/intelligence/main.tsp +19 -0
- package/intelligence/seed/patterns.tsp +172 -0
- package/intelligence/seed/rules.tsp +44 -0
- package/intelligence/seed/strategies.tsp +56 -0
- package/intelligence/signals.tsp +60 -0
- package/models/agent/agent-run.tsp +154 -0
- package/models/agent/tool-call.tsp +122 -0
- package/models/agent/workflow-checkpoint.tsp +50 -0
- package/models/agent/workflow-execution.tsp +136 -0
- package/models/alerting.tsp +436 -0
- package/models/configurator.tsp +433 -0
- package/models/control-graph.tsp +197 -0
- package/models/db.tsp +810 -0
- package/models/deployment.tsp +365 -0
- package/models/error.tsp +433 -0
- package/models/genai.tsp +1368 -0
- package/models/http.tsp +600 -0
- package/models/identity.tsp +213 -0
- package/models/issues.tsp +484 -0
- package/models/log.tsp +140 -0
- package/models/messaging.tsp +304 -0
- package/models/otel-config.tsp +455 -0
- package/models/retention.tsp +240 -0
- package/models/rpc.tsp +309 -0
- package/models/search.tsp +243 -0
- package/models/session.tsp +274 -0
- package/models/system.tsp +400 -0
- package/models/test.tsp +346 -0
- package/models/triage.tsp +113 -0
- package/models/workflow.tsp +396 -0
- package/models/workspace.tsp +435 -0
- package/otel/enums.tsp +392 -0
- package/otel/logs.tsp +135 -0
- package/otel/metrics.tsp +358 -0
- package/otel/otel-conventions.tsp +14 -0
- package/otel/profiles.tsp +335 -0
- package/otel/resource.tsp +257 -0
- package/otel/span.tsp +307 -0
- package/package.json +88 -0
- package/tspconfig.yaml +49 -0
package/api/routes.tsp
ADDED
|
@@ -0,0 +1,1052 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// qyl API routes
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// RESTful API routes with proper pagination, streaming, and error handling.
|
|
5
|
+
// =============================================================================
|
|
6
|
+
|
|
7
|
+
import "@typespec/http";
|
|
8
|
+
import "@typespec/rest";
|
|
9
|
+
import "@typespec/openapi";
|
|
10
|
+
import "@typespec/versioning";
|
|
11
|
+
|
|
12
|
+
import "../common/types.tsp";
|
|
13
|
+
import "../common/errors.tsp";
|
|
14
|
+
import "../common/pagination.tsp";
|
|
15
|
+
import "../otel/span.tsp";
|
|
16
|
+
import "../otel/logs.tsp";
|
|
17
|
+
import "../otel/metrics.tsp";
|
|
18
|
+
import "../otel/profiles.tsp";
|
|
19
|
+
import "../models/session.tsp";
|
|
20
|
+
import "../models/log.tsp";
|
|
21
|
+
import "../models/error.tsp";
|
|
22
|
+
import "../models/deployment.tsp";
|
|
23
|
+
import "../models/identity.tsp";
|
|
24
|
+
|
|
25
|
+
// New domain models
|
|
26
|
+
import "../models/workspace.tsp";
|
|
27
|
+
import "../models/configurator.tsp";
|
|
28
|
+
import "../models/issues.tsp";
|
|
29
|
+
import "../models/alerting.tsp";
|
|
30
|
+
import "../models/workflow.tsp";
|
|
31
|
+
import "../models/search.tsp";
|
|
32
|
+
|
|
33
|
+
using TypeSpec.Http;
|
|
34
|
+
using TypeSpec.OpenAPI;
|
|
35
|
+
using TypeSpec.Versioning;
|
|
36
|
+
|
|
37
|
+
using Qyl.Api.Contracts.Common;
|
|
38
|
+
using Qyl.Api.Contracts.Common.Errors;
|
|
39
|
+
using Qyl.Api.Contracts.Common.Pagination;
|
|
40
|
+
using Qyl.Api.Contracts.OTel.Traces;
|
|
41
|
+
using Qyl.Api.Contracts.OTel.Logs;
|
|
42
|
+
using Qyl.Api.Contracts.OTel.Enums;
|
|
43
|
+
using Qyl.Api.Contracts.OTel.Profiles;
|
|
44
|
+
using Qyl.Api.Contracts.Domains.Observe.Session;
|
|
45
|
+
using Qyl.Api.Contracts.Domains.Observe.Log;
|
|
46
|
+
using Qyl.Api.Contracts.Domains.Observe.Error;
|
|
47
|
+
using Qyl.Api.Contracts.Domains.Ops.Deployment;
|
|
48
|
+
using Qyl.Api.Contracts.Domains.Identity;
|
|
49
|
+
using Qyl.Api.Contracts.Domains.Workspace;
|
|
50
|
+
using Qyl.Api.Contracts.Domains.Configurator;
|
|
51
|
+
using Qyl.Api.Contracts.Domains.Issues;
|
|
52
|
+
using Qyl.Api.Contracts.Domains.Alerting;
|
|
53
|
+
using Qyl.Api.Contracts.Domains.Workflow;
|
|
54
|
+
using Qyl.Api.Contracts.Domains.Search;
|
|
55
|
+
|
|
56
|
+
// =============================================================================
|
|
57
|
+
// API Versioning
|
|
58
|
+
// =============================================================================
|
|
59
|
+
|
|
60
|
+
@versioned(ApiVersions)
|
|
61
|
+
@service(#{
|
|
62
|
+
title: "qyl Observability API",
|
|
63
|
+
})
|
|
64
|
+
@server("https://api.qyl.dev", "qyl production API")
|
|
65
|
+
@server("https://api.staging.qyl.dev", "qyl staging API")
|
|
66
|
+
namespace Qyl.Api.Contracts;
|
|
67
|
+
|
|
68
|
+
enum ApiVersions {
|
|
69
|
+
@doc("Initial API version")
|
|
70
|
+
v1: "2025-12-01",
|
|
71
|
+
|
|
72
|
+
@doc("GenAI observability additions")
|
|
73
|
+
v2: "2026-01-15",
|
|
74
|
+
|
|
75
|
+
@doc("Full OTel v1.40 compliance")
|
|
76
|
+
v3: "2026-01-26",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// =============================================================================
|
|
80
|
+
// Common Response Models
|
|
81
|
+
// =============================================================================
|
|
82
|
+
|
|
83
|
+
@doc("Health status for component-level reporting (HealthUiResponse, dashboard).")
|
|
84
|
+
enum HealthStatus {
|
|
85
|
+
healthy: "healthy",
|
|
86
|
+
degraded: "degraded",
|
|
87
|
+
unhealthy: "unhealthy",
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@doc("Result of clearing telemetry data from the local collector store.")
|
|
91
|
+
model ClearTelemetryResponse {
|
|
92
|
+
@doc("Deleted span row count.")
|
|
93
|
+
spansDeleted: int32;
|
|
94
|
+
|
|
95
|
+
@doc("Deleted log row count.")
|
|
96
|
+
logsDeleted: int32;
|
|
97
|
+
|
|
98
|
+
@doc("Deleted profile row count.")
|
|
99
|
+
profilesDeleted: int32;
|
|
100
|
+
|
|
101
|
+
@doc("Deleted session row count.")
|
|
102
|
+
sessionsDeleted: int32;
|
|
103
|
+
|
|
104
|
+
@doc("Deleted console row count.")
|
|
105
|
+
consoleCleared: int32;
|
|
106
|
+
|
|
107
|
+
@doc("Cleared signal type.")
|
|
108
|
+
type: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@doc("Collector metadata response.")
|
|
112
|
+
model MetaResponse {
|
|
113
|
+
@doc("Collector informational version.")
|
|
114
|
+
version: string;
|
|
115
|
+
|
|
116
|
+
@doc("Runtime identifier.")
|
|
117
|
+
runtime: string;
|
|
118
|
+
|
|
119
|
+
@doc("Build metadata.")
|
|
120
|
+
build: MetaBuild;
|
|
121
|
+
|
|
122
|
+
@doc("Enabled collector capabilities.")
|
|
123
|
+
capabilities: MetaCapabilities;
|
|
124
|
+
|
|
125
|
+
@doc("Runtime status.")
|
|
126
|
+
status: MetaStatus;
|
|
127
|
+
|
|
128
|
+
@doc("Local endpoint links.")
|
|
129
|
+
links: MetaLinks;
|
|
130
|
+
|
|
131
|
+
@doc("Local listener ports.")
|
|
132
|
+
ports: MetaPorts;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@doc("Collector build metadata.")
|
|
136
|
+
model MetaBuild {
|
|
137
|
+
commit?: string;
|
|
138
|
+
informationalVersion?: string;
|
|
139
|
+
dashboardBuildId?: string;
|
|
140
|
+
dashboardEntryAsset?: string;
|
|
141
|
+
dashboardBuiltAtUtc?: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@doc("Collector capability flags.")
|
|
145
|
+
model MetaCapabilities {
|
|
146
|
+
tracing: boolean;
|
|
147
|
+
grpc: boolean;
|
|
148
|
+
genAi: boolean;
|
|
149
|
+
profiles: boolean;
|
|
150
|
+
copilot: boolean;
|
|
151
|
+
embeddedDashboard: boolean;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
@doc("Collector runtime status.")
|
|
155
|
+
model MetaStatus {
|
|
156
|
+
grpcEnabled: boolean;
|
|
157
|
+
authMode: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@doc("Collector local links.")
|
|
161
|
+
model MetaLinks {
|
|
162
|
+
dashboard?: string;
|
|
163
|
+
otlpHttp?: string;
|
|
164
|
+
otlpGrpc?: string;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@doc("Collector local ports.")
|
|
168
|
+
model MetaPorts {
|
|
169
|
+
http: int32;
|
|
170
|
+
grpc: int32;
|
|
171
|
+
otlpHttp: int32;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// =============================================================================
|
|
175
|
+
// Traces API
|
|
176
|
+
// =============================================================================
|
|
177
|
+
|
|
178
|
+
@route("/api/v1/traces")
|
|
179
|
+
@tag("Traces")
|
|
180
|
+
interface TracesApi {
|
|
181
|
+
@doc("List traces with pagination")
|
|
182
|
+
@get
|
|
183
|
+
list(
|
|
184
|
+
@query @doc("Page size") @minValue(1) @maxValue(1000) limit?: int32 = 100,
|
|
185
|
+
): CursorPage<Trace> | NotFoundError | ValidationError | InternalServerError;
|
|
186
|
+
|
|
187
|
+
@doc("Get a specific trace by ID")
|
|
188
|
+
@get
|
|
189
|
+
@route("/{traceId}")
|
|
190
|
+
get(@path traceId: TraceId): Trace | NotFoundError | InternalServerError;
|
|
191
|
+
|
|
192
|
+
@doc("Get spans for a trace")
|
|
193
|
+
@get
|
|
194
|
+
@route("/{traceId}/spans")
|
|
195
|
+
getSpans(
|
|
196
|
+
@path traceId: TraceId,
|
|
197
|
+
): CursorPage<Span> | NotFoundError | InternalServerError;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// =============================================================================
|
|
201
|
+
// Logs API
|
|
202
|
+
// =============================================================================
|
|
203
|
+
|
|
204
|
+
@route("/api/v1/logs")
|
|
205
|
+
@tag("Logs")
|
|
206
|
+
interface LogsApi {
|
|
207
|
+
@doc("Query logs with filtering and pagination")
|
|
208
|
+
@get
|
|
209
|
+
list(
|
|
210
|
+
@query @doc("Session ID filter") sessionId?: SessionId,
|
|
211
|
+
@query @doc("Service name filter") serviceName?: string,
|
|
212
|
+
@query @doc("Severity text filter") level?: string,
|
|
213
|
+
@query @doc("Minimum severity") severityMin?: SeverityNumber,
|
|
214
|
+
@query @doc("Trace ID filter") traceId?: TraceId,
|
|
215
|
+
@query @doc("Start time") startTime?: utcDateTime,
|
|
216
|
+
@query @doc("End time") endTime?: utcDateTime,
|
|
217
|
+
@query @doc("Free text search") query?: string,
|
|
218
|
+
@query @doc("Page size") @minValue(1) @maxValue(10000) limit?: int32 = 100,
|
|
219
|
+
): CursorPage<LogRecord> | ValidationError | InternalServerError;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// =============================================================================
|
|
223
|
+
// Metrics API
|
|
224
|
+
// =============================================================================
|
|
225
|
+
|
|
226
|
+
@route("/api/v1/metrics")
|
|
227
|
+
@tag("Metrics")
|
|
228
|
+
interface MetricsApi {
|
|
229
|
+
@doc("List available metrics")
|
|
230
|
+
@get
|
|
231
|
+
list(
|
|
232
|
+
@query @doc("Service name filter") serviceName?: string,
|
|
233
|
+
@query @doc("Metric name pattern") namePattern?: string,
|
|
234
|
+
@query @doc("Page size") @minValue(1) @maxValue(1000) limit?: int32 = 100,
|
|
235
|
+
@query @doc("Cursor") cursor?: string,
|
|
236
|
+
): CursorPage<MetricMetadata> | InternalServerError;
|
|
237
|
+
|
|
238
|
+
@doc("Query metric data points")
|
|
239
|
+
@post
|
|
240
|
+
@route("/query")
|
|
241
|
+
query(@body request: MetricQueryRequest): MetricQueryResponse | ValidationError | InternalServerError;
|
|
242
|
+
|
|
243
|
+
@doc("Get metric metadata")
|
|
244
|
+
@get
|
|
245
|
+
@route("/{metricName}")
|
|
246
|
+
getMetadata(@path metricName: string): MetricMetadata | NotFoundError | InternalServerError;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
@doc("Metric metadata")
|
|
250
|
+
model MetricMetadata {
|
|
251
|
+
@doc("Metric name")
|
|
252
|
+
name: string;
|
|
253
|
+
|
|
254
|
+
@doc("Metric description")
|
|
255
|
+
description?: string;
|
|
256
|
+
|
|
257
|
+
@doc("Metric unit")
|
|
258
|
+
unit?: string;
|
|
259
|
+
|
|
260
|
+
@doc("Metric type")
|
|
261
|
+
type: MetricType;
|
|
262
|
+
|
|
263
|
+
@doc("Available label keys")
|
|
264
|
+
@encodedName("application/json", "label_keys")
|
|
265
|
+
labelKeys: string[];
|
|
266
|
+
|
|
267
|
+
@doc("Services reporting this metric")
|
|
268
|
+
services: string[];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
@doc("Metric query request")
|
|
272
|
+
model MetricQueryRequest {
|
|
273
|
+
@doc("Metric name")
|
|
274
|
+
@encodedName("application/json", "metric_name")
|
|
275
|
+
metricName: string;
|
|
276
|
+
|
|
277
|
+
@doc("Label filters")
|
|
278
|
+
filters?: Record<string>;
|
|
279
|
+
|
|
280
|
+
@doc("Start time")
|
|
281
|
+
@encodedName("application/json", "start_time")
|
|
282
|
+
startTime: utcDateTime;
|
|
283
|
+
|
|
284
|
+
@doc("End time")
|
|
285
|
+
@encodedName("application/json", "end_time")
|
|
286
|
+
endTime: utcDateTime;
|
|
287
|
+
|
|
288
|
+
@doc("Step interval")
|
|
289
|
+
step?: Qyl.Api.Contracts.Common.Pagination.TimeBucket;
|
|
290
|
+
|
|
291
|
+
@doc("Aggregation function")
|
|
292
|
+
aggregation?: Qyl.Api.Contracts.OTel.Metrics.AggregationFunction;
|
|
293
|
+
|
|
294
|
+
@doc("Group by labels")
|
|
295
|
+
@encodedName("application/json", "group_by")
|
|
296
|
+
groupBy?: string[];
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
@doc("Metric query response")
|
|
300
|
+
model MetricQueryResponse {
|
|
301
|
+
@doc("Metric name")
|
|
302
|
+
@encodedName("application/json", "metric_name")
|
|
303
|
+
metricName: string;
|
|
304
|
+
|
|
305
|
+
@doc("Time series data")
|
|
306
|
+
series: MetricTimeSeries[];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
@doc("Metric time series")
|
|
310
|
+
model MetricTimeSeries {
|
|
311
|
+
@doc("Labels")
|
|
312
|
+
labels: Record<string>;
|
|
313
|
+
|
|
314
|
+
@doc("Data points")
|
|
315
|
+
points: MetricDataPoint[];
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
@doc("Metric data point")
|
|
319
|
+
model MetricDataPoint {
|
|
320
|
+
@doc("Timestamp")
|
|
321
|
+
timestamp: utcDateTime;
|
|
322
|
+
|
|
323
|
+
@doc("Value")
|
|
324
|
+
value: float64;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// =============================================================================
|
|
328
|
+
// Profiles API (v1development, Development stability)
|
|
329
|
+
// =============================================================================
|
|
330
|
+
|
|
331
|
+
@route("/api/v1/profiles")
|
|
332
|
+
@tag("Profiles")
|
|
333
|
+
interface ProfilesApi {
|
|
334
|
+
@doc("List profiles with filtering")
|
|
335
|
+
@get
|
|
336
|
+
list(
|
|
337
|
+
@query @doc("Session ID filter") sessionId?: SessionId,
|
|
338
|
+
@query @doc("Trace ID filter") traceId?: TraceId,
|
|
339
|
+
@query @doc("Service name filter") serviceName?: string,
|
|
340
|
+
@query @doc("Sample type filter (e.g., cpu, alloc_objects, wall)") sampleType?: string,
|
|
341
|
+
@query @doc("Page size") @minValue(1) @maxValue(1000) limit?: int32 = 100,
|
|
342
|
+
): Profile[] | ValidationError | InternalServerError;
|
|
343
|
+
|
|
344
|
+
@doc("Get a specific profile by ID")
|
|
345
|
+
@get
|
|
346
|
+
@route("/{profileId}")
|
|
347
|
+
get(@path profileId: string): Profile | NotFoundError | InternalServerError;
|
|
348
|
+
|
|
349
|
+
@doc("Get profiles for a trace")
|
|
350
|
+
@get
|
|
351
|
+
@route("/by-trace/{traceId}")
|
|
352
|
+
getByTrace(
|
|
353
|
+
@path traceId: TraceId,
|
|
354
|
+
@query @doc("Page size") @minValue(1) @maxValue(1000) limit?: int32 = 100,
|
|
355
|
+
): Profile[] | InternalServerError;
|
|
356
|
+
|
|
357
|
+
@doc("Get profiles for a span")
|
|
358
|
+
@get
|
|
359
|
+
@route("/by-span/{spanId}")
|
|
360
|
+
getBySpan(
|
|
361
|
+
@path spanId: SpanId,
|
|
362
|
+
@query @doc("Page size") @minValue(1) @maxValue(1000) limit?: int32 = 100,
|
|
363
|
+
): Profile[] | InternalServerError;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// =============================================================================
|
|
367
|
+
// Sessions API
|
|
368
|
+
// =============================================================================
|
|
369
|
+
|
|
370
|
+
@route("/api/v1/sessions")
|
|
371
|
+
@tag("Sessions")
|
|
372
|
+
interface SessionsApi {
|
|
373
|
+
@doc("List sessions")
|
|
374
|
+
@get
|
|
375
|
+
list(
|
|
376
|
+
@query @doc("Is active filter") isActive?: boolean,
|
|
377
|
+
@query @doc("Start time") startTime?: utcDateTime,
|
|
378
|
+
@query @doc("End time") endTime?: utcDateTime,
|
|
379
|
+
@query @doc("Page size") @minValue(1) @maxValue(1000) limit?: int32 = 100,
|
|
380
|
+
@query @doc("Cursor") cursor?: string,
|
|
381
|
+
): CursorPage<SessionEntity> | ValidationError | InternalServerError;
|
|
382
|
+
|
|
383
|
+
@doc("Get session by ID")
|
|
384
|
+
@get
|
|
385
|
+
@route("/{sessionId}")
|
|
386
|
+
get(@path sessionId: string): SessionEntity | NotFoundError | InternalServerError;
|
|
387
|
+
|
|
388
|
+
@doc("Get session traces")
|
|
389
|
+
@get
|
|
390
|
+
@route("/{sessionId}/traces")
|
|
391
|
+
getTraces(
|
|
392
|
+
@path sessionId: string,
|
|
393
|
+
): CursorPage<Trace> | NotFoundError | InternalServerError;
|
|
394
|
+
|
|
395
|
+
@doc("Get session statistics")
|
|
396
|
+
@get
|
|
397
|
+
@route("/stats")
|
|
398
|
+
getStats(
|
|
399
|
+
@query @doc("Start time") startTime?: utcDateTime,
|
|
400
|
+
@query @doc("End time") endTime?: utcDateTime,
|
|
401
|
+
): SessionStats | InternalServerError;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// =============================================================================
|
|
405
|
+
// Errors API
|
|
406
|
+
// =============================================================================
|
|
407
|
+
|
|
408
|
+
@route("/api/v1/errors")
|
|
409
|
+
@tag("Errors")
|
|
410
|
+
interface ErrorsApi {
|
|
411
|
+
@doc("List error groups")
|
|
412
|
+
@get
|
|
413
|
+
list(
|
|
414
|
+
@query @doc("Service name filter") serviceName?: string,
|
|
415
|
+
@query @doc("Status filter") status?: ErrorStatus,
|
|
416
|
+
@query @doc("Category filter") category?: ErrorCategory,
|
|
417
|
+
@query @doc("Start time") startTime?: utcDateTime,
|
|
418
|
+
@query @doc("End time") endTime?: utcDateTime,
|
|
419
|
+
@query @doc("Page size") @minValue(1) @maxValue(1000) limit?: int32 = 100,
|
|
420
|
+
@query @doc("Cursor") cursor?: string,
|
|
421
|
+
): CursorPage<ErrorEntity> | ValidationError | InternalServerError;
|
|
422
|
+
|
|
423
|
+
@doc("Get error by ID")
|
|
424
|
+
@get
|
|
425
|
+
@route("/{errorId}")
|
|
426
|
+
get(@path errorId: string): ErrorEntity | NotFoundError | InternalServerError;
|
|
427
|
+
|
|
428
|
+
@doc("Update error status")
|
|
429
|
+
@patch
|
|
430
|
+
@route("/{errorId}")
|
|
431
|
+
update(
|
|
432
|
+
@path errorId: string,
|
|
433
|
+
@body update: MergePatchUpdate<ErrorEntity>,
|
|
434
|
+
): ErrorEntity | NotFoundError | ValidationError | InternalServerError;
|
|
435
|
+
|
|
436
|
+
@doc("Get error statistics")
|
|
437
|
+
@get
|
|
438
|
+
@route("/stats")
|
|
439
|
+
getStats(
|
|
440
|
+
@query @doc("Service name filter") serviceName?: string,
|
|
441
|
+
@query @doc("Start time") startTime?: utcDateTime,
|
|
442
|
+
@query @doc("End time") endTime?: utcDateTime,
|
|
443
|
+
): ErrorStats | InternalServerError;
|
|
444
|
+
|
|
445
|
+
@doc("Get error correlations")
|
|
446
|
+
@get
|
|
447
|
+
@route("/{errorId}/correlations")
|
|
448
|
+
getCorrelations(@path errorId: string): ErrorCorrelation | NotFoundError | InternalServerError;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
// =============================================================================
|
|
453
|
+
// Deployments API
|
|
454
|
+
// =============================================================================
|
|
455
|
+
|
|
456
|
+
@route("/api/v1/deployments")
|
|
457
|
+
@tag("Deployments")
|
|
458
|
+
interface DeploymentsApi {
|
|
459
|
+
@doc("List deployments")
|
|
460
|
+
@get
|
|
461
|
+
list(
|
|
462
|
+
@query @doc("Service name filter") serviceName?: string,
|
|
463
|
+
@query @doc("Environment filter") environment?: DeploymentEnvironment,
|
|
464
|
+
@query @doc("Status filter") status?: DeploymentStatus,
|
|
465
|
+
@query @doc("Start time") startTime?: utcDateTime,
|
|
466
|
+
@query @doc("End time") endTime?: utcDateTime,
|
|
467
|
+
@query @doc("Page size") @minValue(1) @maxValue(1000) limit?: int32 = 100,
|
|
468
|
+
@query @doc("Cursor") cursor?: string,
|
|
469
|
+
): CursorPage<DeploymentEntity> | ValidationError | InternalServerError;
|
|
470
|
+
|
|
471
|
+
@doc("Get deployment by ID")
|
|
472
|
+
@get
|
|
473
|
+
@route("/{deploymentId}")
|
|
474
|
+
get(@path deploymentId: string): DeploymentEntity | NotFoundError | InternalServerError;
|
|
475
|
+
|
|
476
|
+
@doc("Record new deployment")
|
|
477
|
+
@post
|
|
478
|
+
create(@body deployment: Create<DeploymentEntity>): DeploymentEntity | ValidationError | InternalServerError;
|
|
479
|
+
|
|
480
|
+
@doc("Update deployment status")
|
|
481
|
+
@patch
|
|
482
|
+
@route("/{deploymentId}")
|
|
483
|
+
update(
|
|
484
|
+
@path deploymentId: string,
|
|
485
|
+
@body update: MergePatchUpdate<DeploymentEntity>,
|
|
486
|
+
): DeploymentEntity | NotFoundError | ValidationError | InternalServerError;
|
|
487
|
+
|
|
488
|
+
@doc("Get DORA metrics")
|
|
489
|
+
@get
|
|
490
|
+
@route("/metrics/dora")
|
|
491
|
+
getDoraMetrics(
|
|
492
|
+
@query @doc("Service name filter") serviceName?: string,
|
|
493
|
+
@query @doc("Environment filter") environment?: DeploymentEnvironment,
|
|
494
|
+
@query @doc("Start time") startTime?: utcDateTime,
|
|
495
|
+
@query @doc("End time") endTime?: utcDateTime,
|
|
496
|
+
): DoraMetrics | InternalServerError;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
@doc("DORA metrics response")
|
|
500
|
+
model DoraMetrics {
|
|
501
|
+
@doc("Deployment frequency (per day)")
|
|
502
|
+
@encodedName("application/json", "deployment_frequency")
|
|
503
|
+
deploymentFrequency: float64;
|
|
504
|
+
|
|
505
|
+
@doc("Lead time for changes (hours)")
|
|
506
|
+
@encodedName("application/json", "lead_time_hours")
|
|
507
|
+
leadTimeHours: float64;
|
|
508
|
+
|
|
509
|
+
@doc("Change failure rate")
|
|
510
|
+
@encodedName("application/json", "change_failure_rate")
|
|
511
|
+
changeFailureRate: Ratio;
|
|
512
|
+
|
|
513
|
+
@doc("Mean time to recovery (hours)")
|
|
514
|
+
@encodedName("application/json", "mttr_hours")
|
|
515
|
+
mttrHours: float64;
|
|
516
|
+
|
|
517
|
+
@doc("Performance level")
|
|
518
|
+
@encodedName("application/json", "performance_level")
|
|
519
|
+
performanceLevel: DoraPerformanceLevel;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
@doc("DORA performance levels")
|
|
523
|
+
enum DoraPerformanceLevel {
|
|
524
|
+
@doc("Elite performer")
|
|
525
|
+
elite: "elite",
|
|
526
|
+
|
|
527
|
+
@doc("High performer")
|
|
528
|
+
high: "high",
|
|
529
|
+
|
|
530
|
+
@doc("Medium performer")
|
|
531
|
+
medium: "medium",
|
|
532
|
+
|
|
533
|
+
@doc("Low performer")
|
|
534
|
+
low: "low",
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// =============================================================================
|
|
538
|
+
// Services API
|
|
539
|
+
// =============================================================================
|
|
540
|
+
|
|
541
|
+
@route("/api/v1/services")
|
|
542
|
+
@tag("Services")
|
|
543
|
+
interface ServicesApi {
|
|
544
|
+
@doc("List discovered services")
|
|
545
|
+
@get
|
|
546
|
+
list(
|
|
547
|
+
@query @doc("Namespace filter") namespaceName?: string,
|
|
548
|
+
@query @doc("Page size") @minValue(1) @maxValue(1000) limit?: int32 = 100,
|
|
549
|
+
@query @doc("Cursor") cursor?: string,
|
|
550
|
+
): CursorPage<ServiceInfo> | InternalServerError;
|
|
551
|
+
|
|
552
|
+
@doc("Get service details")
|
|
553
|
+
@get
|
|
554
|
+
@route("/{serviceName}")
|
|
555
|
+
get(@path serviceName: string): ServiceDetails | NotFoundError | InternalServerError;
|
|
556
|
+
|
|
557
|
+
@doc("Get service dependencies")
|
|
558
|
+
@get
|
|
559
|
+
@route("/{serviceName}/dependencies")
|
|
560
|
+
getDependencies(@path serviceName: string): ServiceDependency[] | NotFoundError | InternalServerError;
|
|
561
|
+
|
|
562
|
+
@doc("Get service operations")
|
|
563
|
+
@get
|
|
564
|
+
@route("/{serviceName}/operations")
|
|
565
|
+
getOperations(
|
|
566
|
+
@path serviceName: string,
|
|
567
|
+
@query @doc("Page size") limit?: int32 = 100,
|
|
568
|
+
@query @doc("Cursor") cursor?: string,
|
|
569
|
+
): CursorPage<OperationInfo> | NotFoundError | InternalServerError;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
@doc("Service information")
|
|
573
|
+
model ServiceInfo {
|
|
574
|
+
@doc("Service name")
|
|
575
|
+
name: string;
|
|
576
|
+
|
|
577
|
+
@doc("Service namespace")
|
|
578
|
+
@encodedName("application/json", "namespace_name")
|
|
579
|
+
namespaceName?: string;
|
|
580
|
+
|
|
581
|
+
@doc("Service version")
|
|
582
|
+
version?: SemVer;
|
|
583
|
+
|
|
584
|
+
@doc("Instance count")
|
|
585
|
+
@encodedName("application/json", "instance_count")
|
|
586
|
+
instanceCount: int32;
|
|
587
|
+
|
|
588
|
+
@doc("Last seen")
|
|
589
|
+
@encodedName("application/json", "last_seen")
|
|
590
|
+
lastSeen: utcDateTime;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
@doc("Service details")
|
|
594
|
+
model ServiceDetails {
|
|
595
|
+
...ServiceInfo;
|
|
596
|
+
|
|
597
|
+
@doc("Resource attributes")
|
|
598
|
+
@encodedName("application/json", "resource_attributes")
|
|
599
|
+
resourceAttributes: Attribute[];
|
|
600
|
+
|
|
601
|
+
@doc("Instrumentation libraries")
|
|
602
|
+
@encodedName("application/json", "instrumentation_libraries")
|
|
603
|
+
instrumentationLibraries: InstrumentationScope[];
|
|
604
|
+
|
|
605
|
+
@doc("Request rate (per second)")
|
|
606
|
+
@encodedName("application/json", "request_rate")
|
|
607
|
+
requestRate: float64;
|
|
608
|
+
|
|
609
|
+
@doc("Error rate")
|
|
610
|
+
@encodedName("application/json", "error_rate")
|
|
611
|
+
errorRate: Ratio;
|
|
612
|
+
|
|
613
|
+
@doc("Average latency in milliseconds")
|
|
614
|
+
@encodedName("application/json", "avg_latency_ms")
|
|
615
|
+
avgLatencyMs: float64;
|
|
616
|
+
|
|
617
|
+
@doc("P99 latency in milliseconds")
|
|
618
|
+
@encodedName("application/json", "p99_latency_ms")
|
|
619
|
+
p99LatencyMs: float64;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
@doc("Operation information")
|
|
623
|
+
model OperationInfo {
|
|
624
|
+
@doc("Operation name")
|
|
625
|
+
name: string;
|
|
626
|
+
|
|
627
|
+
@doc("Span kind")
|
|
628
|
+
@encodedName("application/json", "span_kind")
|
|
629
|
+
spanKind: SpanKind;
|
|
630
|
+
|
|
631
|
+
@doc("Request count")
|
|
632
|
+
@encodedName("application/json", "request_count")
|
|
633
|
+
requestCount: Count;
|
|
634
|
+
|
|
635
|
+
@doc("Error count")
|
|
636
|
+
@encodedName("application/json", "error_count")
|
|
637
|
+
errorCount: Count;
|
|
638
|
+
|
|
639
|
+
@doc("Average duration in milliseconds")
|
|
640
|
+
@encodedName("application/json", "avg_duration_ms")
|
|
641
|
+
avgDurationMs: float64;
|
|
642
|
+
|
|
643
|
+
@doc("P99 duration in milliseconds")
|
|
644
|
+
@encodedName("application/json", "p99_duration_ms")
|
|
645
|
+
p99DurationMs: float64;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// =============================================================================
|
|
649
|
+
// Health API — Aspire-shape probes. /alive is cheap liveness (live-tagged checks),
|
|
650
|
+
// /health runs all ready-tagged checks. Collector exposes a rich /health/ui DTO for
|
|
651
|
+
// the dashboard, modelled separately as HealthUiResponse.
|
|
652
|
+
// =============================================================================
|
|
653
|
+
|
|
654
|
+
@tag("Health")
|
|
655
|
+
interface HealthApi {
|
|
656
|
+
@doc("Liveness probe — runs live-tagged health checks.")
|
|
657
|
+
@route("/alive")
|
|
658
|
+
@get
|
|
659
|
+
@operationId("health_alive")
|
|
660
|
+
alive(): {
|
|
661
|
+
@statusCode statusCode: 200;
|
|
662
|
+
} | {
|
|
663
|
+
@statusCode statusCode: 503;
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
@doc("Readiness probe — runs all ready-tagged health checks.")
|
|
667
|
+
@route("/health")
|
|
668
|
+
@get
|
|
669
|
+
@operationId("health_ready")
|
|
670
|
+
ready(): {
|
|
671
|
+
@statusCode statusCode: 200;
|
|
672
|
+
} | {
|
|
673
|
+
@statusCode statusCode: 503;
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// =============================================================================
|
|
678
|
+
// Workspace API
|
|
679
|
+
// =============================================================================
|
|
680
|
+
|
|
681
|
+
@route("/api/v1/workspaces")
|
|
682
|
+
@tag("Workspaces")
|
|
683
|
+
interface WorkspacesApi {
|
|
684
|
+
@doc("Get current workspace envelope")
|
|
685
|
+
@get
|
|
686
|
+
@route("/current")
|
|
687
|
+
getCurrent(): WorkspaceEnvelopeEntity | NotFoundError | InternalServerError;
|
|
688
|
+
|
|
689
|
+
@doc("Register workspace heartbeat")
|
|
690
|
+
@post
|
|
691
|
+
@route("/current/heartbeat")
|
|
692
|
+
heartbeat(): WorkspaceEnvelopeEntity | NotFoundError | InternalServerError;
|
|
693
|
+
|
|
694
|
+
@doc("List projects")
|
|
695
|
+
@get
|
|
696
|
+
@route("/projects")
|
|
697
|
+
listProjects(
|
|
698
|
+
@query @doc("Page size") @minValue(1) @maxValue(100) limit?: int32 = 20,
|
|
699
|
+
@query @doc("Cursor") cursor?: string,
|
|
700
|
+
): CursorPage<ProjectEntity> | InternalServerError;
|
|
701
|
+
|
|
702
|
+
@doc("Get project by ID")
|
|
703
|
+
@get
|
|
704
|
+
@route("/projects/{projectId}")
|
|
705
|
+
getProject(@path projectId: string): ProjectEntity | NotFoundError | InternalServerError;
|
|
706
|
+
|
|
707
|
+
@doc("Create a new project")
|
|
708
|
+
@post
|
|
709
|
+
@route("/projects")
|
|
710
|
+
createProject(@body project: Create<ProjectEntity>): ProjectEntity | ValidationError | InternalServerError;
|
|
711
|
+
|
|
712
|
+
@doc("List environments for a project")
|
|
713
|
+
@get
|
|
714
|
+
@route("/projects/{projectId}/environments")
|
|
715
|
+
listEnvironments(@path projectId: string): ProjectEnvironmentEntity[] | NotFoundError | InternalServerError;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// =============================================================================
|
|
719
|
+
// Onboarding API
|
|
720
|
+
// =============================================================================
|
|
721
|
+
|
|
722
|
+
@route("/api/v1/onboarding")
|
|
723
|
+
@tag("Onboarding")
|
|
724
|
+
interface OnboardingApi {
|
|
725
|
+
@doc("Start a new handshake session")
|
|
726
|
+
@post
|
|
727
|
+
@route("/handshake")
|
|
728
|
+
startHandshake(@body request: HandshakeStartRequest): HandshakeSessionEntity | ValidationError | InternalServerError;
|
|
729
|
+
|
|
730
|
+
@doc("Complete handshake verification")
|
|
731
|
+
@post
|
|
732
|
+
@route("/handshake/{sessionId}/verify")
|
|
733
|
+
verifyHandshake(
|
|
734
|
+
@path sessionId: string,
|
|
735
|
+
@body request: HandshakeVerifyRequest,
|
|
736
|
+
): HandshakeVerifyResponse | NotFoundError | ValidationError | InternalServerError;
|
|
737
|
+
|
|
738
|
+
@doc("Get handshake session status")
|
|
739
|
+
@get
|
|
740
|
+
@route("/handshake/{sessionId}")
|
|
741
|
+
getHandshake(@path sessionId: string): HandshakeSessionEntity | NotFoundError | InternalServerError;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
@doc("Handshake start request")
|
|
745
|
+
model HandshakeStartRequest {
|
|
746
|
+
@doc("PKCE code challenge")
|
|
747
|
+
@encodedName("application/json", "code_challenge")
|
|
748
|
+
codeChallenge: string;
|
|
749
|
+
|
|
750
|
+
@doc("Client identifier")
|
|
751
|
+
@encodedName("application/json", "client_id")
|
|
752
|
+
clientId: string;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
@doc("Handshake verification request")
|
|
756
|
+
model HandshakeVerifyRequest {
|
|
757
|
+
@doc("PKCE code verifier")
|
|
758
|
+
@encodedName("application/json", "code_verifier")
|
|
759
|
+
codeVerifier: string;
|
|
760
|
+
|
|
761
|
+
@doc("Authorization code")
|
|
762
|
+
code: string;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
@doc("Handshake verification response")
|
|
766
|
+
model HandshakeVerifyResponse {
|
|
767
|
+
@doc("Access token")
|
|
768
|
+
@encodedName("application/json", "access_token")
|
|
769
|
+
accessToken: string;
|
|
770
|
+
|
|
771
|
+
@doc("Token expiration")
|
|
772
|
+
@encodedName("application/json", "expires_at")
|
|
773
|
+
expiresAt: utcDateTime;
|
|
774
|
+
|
|
775
|
+
@doc("Workspace ID")
|
|
776
|
+
@encodedName("application/json", "workspace_id")
|
|
777
|
+
workspaceId: string;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// =============================================================================
|
|
781
|
+
// Configurator API
|
|
782
|
+
// =============================================================================
|
|
783
|
+
|
|
784
|
+
@route("/api/v1/configurator")
|
|
785
|
+
@tag("Configurator")
|
|
786
|
+
interface ConfiguratorApi {
|
|
787
|
+
@doc("List generation profiles")
|
|
788
|
+
@get
|
|
789
|
+
@route("/profiles")
|
|
790
|
+
listProfiles(
|
|
791
|
+
@query @doc("Page size") @minValue(1) @maxValue(100) limit?: int32 = 20,
|
|
792
|
+
@query @doc("Cursor") cursor?: string,
|
|
793
|
+
): CursorPage<GenerationProfileEntity> | InternalServerError;
|
|
794
|
+
|
|
795
|
+
@doc("Get generation profile by ID")
|
|
796
|
+
@get
|
|
797
|
+
@route("/profiles/{profileId}")
|
|
798
|
+
getProfile(@path profileId: string): GenerationProfileEntity | NotFoundError | InternalServerError;
|
|
799
|
+
|
|
800
|
+
@doc("Create generation profile")
|
|
801
|
+
@post
|
|
802
|
+
@route("/profiles")
|
|
803
|
+
createProfile(@body profile: Create<GenerationProfileEntity>): GenerationProfileEntity | ValidationError | InternalServerError;
|
|
804
|
+
|
|
805
|
+
@doc("Get generation selections for a workspace")
|
|
806
|
+
@get
|
|
807
|
+
@route("/selections")
|
|
808
|
+
getSelections(
|
|
809
|
+
@query @doc("Workspace ID") workspaceId: string,
|
|
810
|
+
): GenerationSelectionEntity[] | InternalServerError;
|
|
811
|
+
|
|
812
|
+
@doc("Save generation selections")
|
|
813
|
+
@post
|
|
814
|
+
@route("/selections")
|
|
815
|
+
saveSelections(@body selections: GenerationSelectionSaveRequest): GenerationSelectionEntity | ValidationError | InternalServerError;
|
|
816
|
+
|
|
817
|
+
@doc("Create a generation job")
|
|
818
|
+
@post
|
|
819
|
+
@route("/jobs")
|
|
820
|
+
createJob(@body job: Create<GenerationJobEntity>): GenerationJobEntity | ValidationError | InternalServerError;
|
|
821
|
+
|
|
822
|
+
@doc("Get generation job status")
|
|
823
|
+
@get
|
|
824
|
+
@route("/jobs/{jobId}")
|
|
825
|
+
getJob(@path jobId: string): GenerationJobEntity | NotFoundError | InternalServerError;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
@doc("Batch upsert request — workspace+profile scope from GenerationSelectionEntity, plus an array of selection keys")
|
|
829
|
+
model GenerationSelectionSaveRequest {
|
|
830
|
+
...PickProperties<GenerationSelectionEntity, "workspaceId" | "profileId">;
|
|
831
|
+
|
|
832
|
+
@doc("Array of selection keys to enable in a single round-trip")
|
|
833
|
+
@encodedName("application/json", "selected_keys")
|
|
834
|
+
selectedKeys: string[];
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// =============================================================================
|
|
838
|
+
// Issues API (Sentry-style error grouping)
|
|
839
|
+
// =============================================================================
|
|
840
|
+
|
|
841
|
+
@route("/api/v1/issues")
|
|
842
|
+
@tag("Issues")
|
|
843
|
+
interface IssuesApi {
|
|
844
|
+
@doc("List error issues")
|
|
845
|
+
@get
|
|
846
|
+
list(
|
|
847
|
+
@query @doc("Project ID filter") projectId?: string,
|
|
848
|
+
@query @doc("Status filter") status?: IssueStatus,
|
|
849
|
+
@query @doc("Priority filter") priority?: IssuePriority,
|
|
850
|
+
@query @doc("Level filter") level?: IssueLevel,
|
|
851
|
+
@query @doc("Start time") startTime?: utcDateTime,
|
|
852
|
+
@query @doc("End time") endTime?: utcDateTime,
|
|
853
|
+
@query @doc("Page size") @minValue(1) @maxValue(100) limit?: int32 = 20,
|
|
854
|
+
@query @doc("Cursor") cursor?: string,
|
|
855
|
+
): CursorPage<ErrorIssueEntity> | ValidationError | InternalServerError;
|
|
856
|
+
|
|
857
|
+
@doc("Get issue by ID")
|
|
858
|
+
@get
|
|
859
|
+
@route("/{issueId}")
|
|
860
|
+
get(@path issueId: string): ErrorIssueEntity | NotFoundError | InternalServerError;
|
|
861
|
+
|
|
862
|
+
@doc("Update issue status")
|
|
863
|
+
@patch
|
|
864
|
+
@route("/{issueId}")
|
|
865
|
+
update(
|
|
866
|
+
@path issueId: string,
|
|
867
|
+
@body update: MergePatchUpdate<ErrorIssueEntity>,
|
|
868
|
+
): ErrorIssueEntity | NotFoundError | ValidationError | InternalServerError;
|
|
869
|
+
|
|
870
|
+
@doc("Get issue events")
|
|
871
|
+
@get
|
|
872
|
+
@route("/{issueId}/events")
|
|
873
|
+
getEvents(
|
|
874
|
+
@path issueId: string,
|
|
875
|
+
@query @doc("Page size") @minValue(1) @maxValue(100) limit?: int32 = 20,
|
|
876
|
+
@query @doc("Cursor") cursor?: string,
|
|
877
|
+
): CursorPage<ErrorIssueEventEntity> | NotFoundError | InternalServerError;
|
|
878
|
+
|
|
879
|
+
@doc("Get issue breadcrumbs")
|
|
880
|
+
@get
|
|
881
|
+
@route("/{issueId}/breadcrumbs")
|
|
882
|
+
getBreadcrumbs(
|
|
883
|
+
@path issueId: string,
|
|
884
|
+
@query @doc("Page size") @minValue(1) @maxValue(50) limit?: int32 = 20,
|
|
885
|
+
): ErrorBreadcrumbEntity[] | NotFoundError | InternalServerError;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
// =============================================================================
|
|
890
|
+
// Workflow API
|
|
891
|
+
// =============================================================================
|
|
892
|
+
|
|
893
|
+
@route("/api/v1/workflows")
|
|
894
|
+
@tag("Workflows")
|
|
895
|
+
interface WorkflowsApi {
|
|
896
|
+
@doc("List workflow runs")
|
|
897
|
+
@get
|
|
898
|
+
@route("/runs")
|
|
899
|
+
listRuns(
|
|
900
|
+
@query @doc("Project ID filter") projectId?: string,
|
|
901
|
+
@query @doc("Workflow ID filter") workflowId?: string,
|
|
902
|
+
@query @doc("Status filter") status?: WorkflowRunStatus,
|
|
903
|
+
@query @doc("Start time") startTime?: utcDateTime,
|
|
904
|
+
@query @doc("End time") endTime?: utcDateTime,
|
|
905
|
+
@query @doc("Page size") @minValue(1) @maxValue(100) limit?: int32 = 20,
|
|
906
|
+
@query @doc("Cursor") cursor?: string,
|
|
907
|
+
): CursorPage<WorkflowRunEntity> | ValidationError | InternalServerError;
|
|
908
|
+
|
|
909
|
+
@doc("Get workflow run by ID")
|
|
910
|
+
@get
|
|
911
|
+
@route("/runs/{runId}")
|
|
912
|
+
getRun(@path runId: string): WorkflowRunEntity | NotFoundError | InternalServerError;
|
|
913
|
+
|
|
914
|
+
@doc("Get workflow run nodes")
|
|
915
|
+
@get
|
|
916
|
+
@route("/runs/{runId}/nodes")
|
|
917
|
+
getRunNodes(
|
|
918
|
+
@path runId: string,
|
|
919
|
+
@query @doc("Page size") @minValue(1) @maxValue(100) limit?: int32 = 50,
|
|
920
|
+
@query @doc("Cursor") cursor?: string,
|
|
921
|
+
): CursorPage<WorkflowNodeEntity> | NotFoundError | InternalServerError;
|
|
922
|
+
|
|
923
|
+
@doc("Get workflow events")
|
|
924
|
+
@get
|
|
925
|
+
@route("/runs/{runId}/events")
|
|
926
|
+
getRunEvents(
|
|
927
|
+
@path runId: string,
|
|
928
|
+
@query @doc("After sequence number") afterSequence?: int64,
|
|
929
|
+
@query @doc("Page size") @minValue(1) @maxValue(100) limit?: int32 = 50,
|
|
930
|
+
): WorkflowEventEntity[] | NotFoundError | InternalServerError;
|
|
931
|
+
|
|
932
|
+
@doc("Resume a paused workflow")
|
|
933
|
+
@post
|
|
934
|
+
@route("/runs/{runId}/resume")
|
|
935
|
+
resumeRun(@path runId: string): WorkflowRunEntity | NotFoundError | ValidationError | InternalServerError;
|
|
936
|
+
|
|
937
|
+
@doc("Approve a pending workflow step")
|
|
938
|
+
@post
|
|
939
|
+
@route("/runs/{runId}/nodes/{nodeId}/approve")
|
|
940
|
+
approveStep(
|
|
941
|
+
@path runId: string,
|
|
942
|
+
@path nodeId: string,
|
|
943
|
+
): WorkflowNodeEntity | NotFoundError | ValidationError | InternalServerError;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// =============================================================================
|
|
947
|
+
// Search API
|
|
948
|
+
// =============================================================================
|
|
949
|
+
|
|
950
|
+
@route("/api/v1/search")
|
|
951
|
+
@tag("Search")
|
|
952
|
+
interface SearchApi {
|
|
953
|
+
@doc("Unified search across all entity types")
|
|
954
|
+
@post
|
|
955
|
+
search(@body request: SearchRequest): SearchResponse | ValidationError | InternalServerError;
|
|
956
|
+
|
|
957
|
+
@doc("Get search suggestions")
|
|
958
|
+
@get
|
|
959
|
+
@route("/suggestions")
|
|
960
|
+
getSuggestions(
|
|
961
|
+
@query @doc("Query prefix") query: string,
|
|
962
|
+
@query @doc("Maximum suggestions") @minValue(1) @maxValue(20) limit?: int32 = 5,
|
|
963
|
+
): string[] | InternalServerError;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// =============================================================================
|
|
967
|
+
// Alerts API
|
|
968
|
+
// =============================================================================
|
|
969
|
+
|
|
970
|
+
@route("/api/v1/alerts")
|
|
971
|
+
@tag("Alerts")
|
|
972
|
+
interface AlertsApi {
|
|
973
|
+
@doc("List alert rules")
|
|
974
|
+
@get
|
|
975
|
+
@route("/rules")
|
|
976
|
+
listRules(
|
|
977
|
+
@query @doc("Project ID filter") projectId?: string,
|
|
978
|
+
@query @doc("Enabled filter") enabled?: boolean,
|
|
979
|
+
@query @doc("Page size") @minValue(1) @maxValue(100) limit?: int32 = 20,
|
|
980
|
+
@query @doc("Cursor") cursor?: string,
|
|
981
|
+
): CursorPage<AlertRuleEntity> | InternalServerError;
|
|
982
|
+
|
|
983
|
+
@doc("Get alert rule by ID")
|
|
984
|
+
@get
|
|
985
|
+
@route("/rules/{ruleId}")
|
|
986
|
+
getRule(@path ruleId: string): AlertRuleEntity | NotFoundError | InternalServerError;
|
|
987
|
+
|
|
988
|
+
@doc("Create a new alert rule")
|
|
989
|
+
@post
|
|
990
|
+
@route("/rules")
|
|
991
|
+
createRule(@body rule: AlertRuleEntity): AlertRuleEntity | ValidationError | InternalServerError;
|
|
992
|
+
|
|
993
|
+
@doc("Update an existing alert rule")
|
|
994
|
+
@put
|
|
995
|
+
@route("/rules/{ruleId}")
|
|
996
|
+
updateRule(
|
|
997
|
+
@path ruleId: string,
|
|
998
|
+
@body rule: AlertRuleEntity,
|
|
999
|
+
): AlertRuleEntity | NotFoundError | ValidationError | InternalServerError;
|
|
1000
|
+
|
|
1001
|
+
@doc("Delete an alert rule")
|
|
1002
|
+
@delete
|
|
1003
|
+
@route("/rules/{ruleId}")
|
|
1004
|
+
deleteRule(@path ruleId: string): NoContentResponse | NotFoundError | InternalServerError;
|
|
1005
|
+
|
|
1006
|
+
@doc("List alert firings")
|
|
1007
|
+
@get
|
|
1008
|
+
@route("/firings")
|
|
1009
|
+
listFirings(
|
|
1010
|
+
@query @doc("Rule ID filter") ruleId?: string,
|
|
1011
|
+
@query @doc("Status filter") status?: AlertFiringStatus,
|
|
1012
|
+
@query @doc("Start time") startTime?: utcDateTime,
|
|
1013
|
+
@query @doc("End time") endTime?: utcDateTime,
|
|
1014
|
+
@query @doc("Page size") @minValue(1) @maxValue(100) limit?: int32 = 20,
|
|
1015
|
+
@query @doc("Cursor") cursor?: string,
|
|
1016
|
+
): CursorPage<AlertFiringEntity> | ValidationError | InternalServerError;
|
|
1017
|
+
|
|
1018
|
+
@doc("Acknowledge an alert firing")
|
|
1019
|
+
@post
|
|
1020
|
+
@route("/firings/{firingId}/acknowledge")
|
|
1021
|
+
acknowledgeFiring(
|
|
1022
|
+
@path firingId: string,
|
|
1023
|
+
@body acknowledgement: AlertFiringAcknowledgement,
|
|
1024
|
+
): AlertFiringEntity | NotFoundError | InternalServerError;
|
|
1025
|
+
|
|
1026
|
+
@doc("Resolve an alert firing")
|
|
1027
|
+
@post
|
|
1028
|
+
@route("/firings/{firingId}/resolve")
|
|
1029
|
+
resolveFiring(@path firingId: string): AlertFiringEntity | NotFoundError | InternalServerError;
|
|
1030
|
+
|
|
1031
|
+
@doc("List fix runs")
|
|
1032
|
+
@get
|
|
1033
|
+
@route("/fixes")
|
|
1034
|
+
listFixRuns(
|
|
1035
|
+
@query @doc("Issue ID filter") issueId?: string,
|
|
1036
|
+
@query @doc("Status filter") status?: FixRunStatus,
|
|
1037
|
+
@query @doc("Page size") @minValue(1) @maxValue(100) limit?: int32 = 20,
|
|
1038
|
+
@query @doc("Cursor") cursor?: string,
|
|
1039
|
+
): CursorPage<FixRunEntity> | ValidationError | InternalServerError;
|
|
1040
|
+
|
|
1041
|
+
@doc("Get fix run by ID")
|
|
1042
|
+
@get
|
|
1043
|
+
@route("/fixes/{fixId}")
|
|
1044
|
+
getFixRun(@path fixId: string): FixRunEntity | NotFoundError | InternalServerError;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
@doc("Acknowledge request body for an alert firing")
|
|
1048
|
+
model AlertFiringAcknowledgement {
|
|
1049
|
+
@doc("User or service that acknowledges the firing")
|
|
1050
|
+
@encodedName("application/json", "acknowledged_by")
|
|
1051
|
+
acknowledgedBy: string;
|
|
1052
|
+
}
|