@opensoftware/contracts 2.0.0 → 2.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/README.md +34 -8
- package/dist/generated/types.d.ts +122 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js.map +1 -1
- package/openapi/openapi.json +150 -1
- package/package.json +2 -2
- package/typespec/foreman.tsp +3 -0
- package/typespec/health.tsp +42 -0
- package/typespec/intake-api.tsp +8 -0
- package/typespec/intake.tsp +5 -0
- package/typespec/main.tsp +1 -0
package/README.md
CHANGED
|
@@ -51,9 +51,11 @@ Each format has one schema, generated from `typespec/`, and fixtures that CONTRA
|
|
|
51
51
|
`openapi/openapi.json` also holds the paths of the module APIs, from the operations in `typespec/` (ADR 0048). The
|
|
52
52
|
first is gatekeeper's: `POST /v1/gates` and `GET /v1/gates/{gateId}` with the models `GateRequest`, `GateView` and
|
|
53
53
|
`ApiProblem` (RFC 9457). The second is intake's (ADR 0063): `POST /v1/work-items` and
|
|
54
|
-
`GET /v1/work-items/{workItemId}` with the models `WorkSubmission`, `WorkItemView` and `WorkRequest
|
|
54
|
+
`GET /v1/work-items/{workItemId}` with the models `WorkSubmission`, `WorkItemView` and `WorkRequest`, and
|
|
55
|
+
`GET /v1/inbox` with `WorkItemList`. The third is the
|
|
55
56
|
foreman's (ADR 0067): `POST /v1/runs`, `GET /v1/runs/{runId}` and `POST /v1/runs/{runId}/signals` with the models
|
|
56
|
-
`RunView`, `StageView`, `CheckView` (ADR 0069) and `RunSignal`.
|
|
57
|
+
`RunView`, `StageView`, `CheckView` (ADR 0069) and `RunSignal`. All three answer `GET /v1/health` with the models
|
|
58
|
+
`Health` and `HealthCheck` (`HealthApi`, ADR 0086). `pnpm build` compiles the operations with the TypeSpec OpenAPI 3 emitter and copies their
|
|
57
59
|
paths next to the named models; an operation that uses a type that is no model fails the build. The package exports
|
|
58
60
|
the type `paths`, which types a client such as openapi-fetch.
|
|
59
61
|
|
|
@@ -91,18 +93,42 @@ the workflow by hand with the tag. The CONTRACT check and `osf-contracts breakin
|
|
|
91
93
|
Only the TypeScript package exists (ADR 0012). A client for another language can be generated from
|
|
92
94
|
`openapi/openapi.json` with any OpenAPI generator when someone needs one.
|
|
93
95
|
|
|
96
|
+
A new package gets its publish workflow and its release-please entries with `osf release-files`; the check RELEASE
|
|
97
|
+
fails until they are current, and also when a workflow differs from `tools/scaffold/publish-workflow.yml`.
|
|
98
|
+
|
|
94
99
|
One-time setup (no credential is ever stored in the repository):
|
|
95
100
|
|
|
96
101
|
1. npm: the organization `opensoftware` owns the scope `@opensoftware`, under which every package of this
|
|
97
102
|
repository is published.
|
|
98
103
|
2. GitHub: the publish job runs in the environment `npm` (Settings → Environments; GitHub creates it on the first
|
|
99
104
|
run).
|
|
100
|
-
3.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
3. New package only: npm adds a trusted publisher only to a package that exists. Before the release pull request
|
|
106
|
+
with its first version merges, a maintainer creates it: build and pack it as version `0.0.0`, then run
|
|
107
|
+
`npm publish <file>.tgz --tag placeholder --access public` without `--otp`. npm stages that version, so the
|
|
108
|
+
package exists and nothing is public (`skills/release-packages`).
|
|
109
|
+
4. Then add the trusted publisher, on npmjs.com (package settings → Trusted publishing) or with
|
|
110
|
+
`npm trust github <package> --file publish-<name>.yml --repository sesigl/open-software-factory --environment npm
|
|
111
|
+
--allow-publish --allow-stage-publish`. Every later version publishes through OIDC only, with a provenance
|
|
112
|
+
statement; the repository holds no npm token.
|
|
113
|
+
|
|
114
|
+
An agent can do step 4 and the checks below with the npm CLI (11.19 or later). It needs a maintainer's
|
|
115
|
+
`npm login --registry https://registry.npmjs.org`. Every `npm trust` call needs two-factor authentication, also
|
|
116
|
+
`list`. Without a terminal, the call fails with `EOTP` and prints an `authUrl` and a `doneUrl`. The maintainer
|
|
117
|
+
opens the `authUrl` and approves. Then a `GET` of the `doneUrl`, with the login's token as a bearer token, returns
|
|
118
|
+
a `token`. The agent passes it as `--otp=<token>` to the next `npm trust` calls; it stays valid for some minutes.
|
|
119
|
+
|
|
120
|
+
To check all packages, run `npm trust list <package>` for each `publish-*.yml` that publishes to npm. Each package
|
|
121
|
+
has exactly one entry: this repository, its own workflow file, the environment `npm`, and the permissions
|
|
122
|
+
`createPackage` and `createStagedPackage`.
|
|
123
|
+
|
|
124
|
+
When a publish fails with `E404 Not Found - PUT https://registry.npmjs.org/@opensoftware%2f<name>`:
|
|
125
|
+
|
|
126
|
+
- npm did not accept the job as a trusted publisher. Check that the job has `id-token: write` and
|
|
127
|
+
`environment: npm`, and compare the package's entry with the rule above.
|
|
128
|
+
- An entry can look correct and still fail. Then remove it with `npm trust revoke <package> --id=<id>`, add it
|
|
129
|
+
again with step 4 and run the workflow again with the tag.
|
|
130
|
+
- After a successful publish, npm can take some minutes before the version is available. A build that installs
|
|
131
|
+
the new version in this time fails with `ETARGET`; run it again.
|
|
106
132
|
|
|
107
133
|
## Configuration
|
|
108
134
|
|
|
@@ -33,6 +33,40 @@ export type paths = {
|
|
|
33
33
|
patch?: never;
|
|
34
34
|
trace?: never;
|
|
35
35
|
};
|
|
36
|
+
"/v1/health": {
|
|
37
|
+
parameters: {
|
|
38
|
+
query?: never;
|
|
39
|
+
header?: never;
|
|
40
|
+
path?: never;
|
|
41
|
+
cookie?: never;
|
|
42
|
+
};
|
|
43
|
+
/** @description The server's health. Needs no token, and names no secret. 503 when a check fails. */
|
|
44
|
+
get: operations["HealthApi_getHealth"];
|
|
45
|
+
put?: never;
|
|
46
|
+
post?: never;
|
|
47
|
+
delete?: never;
|
|
48
|
+
options?: never;
|
|
49
|
+
head?: never;
|
|
50
|
+
patch?: never;
|
|
51
|
+
trace?: never;
|
|
52
|
+
};
|
|
53
|
+
"/v1/inbox": {
|
|
54
|
+
parameters: {
|
|
55
|
+
query?: never;
|
|
56
|
+
header?: never;
|
|
57
|
+
path?: never;
|
|
58
|
+
cookie?: never;
|
|
59
|
+
};
|
|
60
|
+
/** @description The 50 latest work items, the one received last first; with `status`, only the work items in that status. */
|
|
61
|
+
get: operations["IntakeApi_listWorkItems"];
|
|
62
|
+
put?: never;
|
|
63
|
+
post?: never;
|
|
64
|
+
delete?: never;
|
|
65
|
+
options?: never;
|
|
66
|
+
head?: never;
|
|
67
|
+
patch?: never;
|
|
68
|
+
trace?: never;
|
|
69
|
+
};
|
|
36
70
|
"/v1/runs": {
|
|
37
71
|
parameters: {
|
|
38
72
|
query?: never;
|
|
@@ -439,6 +473,19 @@ export type components = {
|
|
|
439
473
|
/** @description The record, once the gate is decided. */
|
|
440
474
|
record?: components["schemas"]["GateRecord"];
|
|
441
475
|
};
|
|
476
|
+
/** @description The state of a server: `ok` when every check is ok. */
|
|
477
|
+
Health: {
|
|
478
|
+
status: "ok" | "failing";
|
|
479
|
+
checks: components["schemas"]["HealthCheck"][];
|
|
480
|
+
};
|
|
481
|
+
/** @description One system a server needs, such as its database or NATS, and whether the server reaches it now. */
|
|
482
|
+
HealthCheck: {
|
|
483
|
+
/** @description The system, like "database", "nats" or "temporal". */
|
|
484
|
+
name: components["schemas"]["Name"];
|
|
485
|
+
ok: boolean;
|
|
486
|
+
/** @description What failed and how to fix it, when `ok` is false. Never a secret. */
|
|
487
|
+
detail?: string;
|
|
488
|
+
};
|
|
442
489
|
/** @description A hostname without scheme or port, for example "api.linear.app". */
|
|
443
490
|
Hostname: string;
|
|
444
491
|
/** @description An identifier chosen by the producer. ULIDs are recommended; consumers treat it as opaque. */
|
|
@@ -840,6 +887,8 @@ export type components = {
|
|
|
840
887
|
startedAt: string;
|
|
841
888
|
/** Format: date-time */
|
|
842
889
|
endedAt?: string;
|
|
890
|
+
/** @description The OpenTelemetry trace of the run: the first 32 hex digits of the SHA-256 of `runId` (ADR 0087). */
|
|
891
|
+
traceId?: components["schemas"]["TraceId"];
|
|
843
892
|
/** @description The stages of the workflow, in order. */
|
|
844
893
|
stages: components["schemas"]["StageView"][];
|
|
845
894
|
};
|
|
@@ -1039,6 +1088,10 @@ export type components = {
|
|
|
1039
1088
|
/** @description Related material: an error report, a change, a document. */
|
|
1040
1089
|
links?: components["schemas"]["Link"][];
|
|
1041
1090
|
};
|
|
1091
|
+
/** @description intake: the latest work items, as `GET /v1/inbox` answers them, the one received last first. */
|
|
1092
|
+
WorkItemList: {
|
|
1093
|
+
workItems: components["schemas"]["WorkItemView"][];
|
|
1094
|
+
};
|
|
1042
1095
|
/**
|
|
1043
1096
|
* @description Where a work item stands: received, then as its run in the foreman goes (ADR 0071).
|
|
1044
1097
|
* @enum {string}
|
|
@@ -1200,6 +1253,75 @@ export interface operations {
|
|
|
1200
1253
|
};
|
|
1201
1254
|
};
|
|
1202
1255
|
};
|
|
1256
|
+
HealthApi_getHealth: {
|
|
1257
|
+
parameters: {
|
|
1258
|
+
query?: never;
|
|
1259
|
+
header?: never;
|
|
1260
|
+
path?: never;
|
|
1261
|
+
cookie?: never;
|
|
1262
|
+
};
|
|
1263
|
+
requestBody?: never;
|
|
1264
|
+
responses: {
|
|
1265
|
+
/** @description The request has succeeded. */
|
|
1266
|
+
200: {
|
|
1267
|
+
headers: {
|
|
1268
|
+
[name: string]: unknown;
|
|
1269
|
+
};
|
|
1270
|
+
content: {
|
|
1271
|
+
"application/json": components["schemas"]["Health"];
|
|
1272
|
+
};
|
|
1273
|
+
};
|
|
1274
|
+
/** @description Service unavailable. */
|
|
1275
|
+
503: {
|
|
1276
|
+
headers: {
|
|
1277
|
+
[name: string]: unknown;
|
|
1278
|
+
};
|
|
1279
|
+
content: {
|
|
1280
|
+
"application/json": components["schemas"]["Health"];
|
|
1281
|
+
};
|
|
1282
|
+
};
|
|
1283
|
+
};
|
|
1284
|
+
};
|
|
1285
|
+
IntakeApi_listWorkItems: {
|
|
1286
|
+
parameters: {
|
|
1287
|
+
query?: {
|
|
1288
|
+
status?: components["schemas"]["WorkItemStatus"];
|
|
1289
|
+
};
|
|
1290
|
+
header?: never;
|
|
1291
|
+
path?: never;
|
|
1292
|
+
cookie?: never;
|
|
1293
|
+
};
|
|
1294
|
+
requestBody?: never;
|
|
1295
|
+
responses: {
|
|
1296
|
+
/** @description The request has succeeded. */
|
|
1297
|
+
200: {
|
|
1298
|
+
headers: {
|
|
1299
|
+
[name: string]: unknown;
|
|
1300
|
+
};
|
|
1301
|
+
content: {
|
|
1302
|
+
"application/json": components["schemas"]["WorkItemList"];
|
|
1303
|
+
};
|
|
1304
|
+
};
|
|
1305
|
+
/** @description The server could not understand the request due to invalid syntax. */
|
|
1306
|
+
400: {
|
|
1307
|
+
headers: {
|
|
1308
|
+
[name: string]: unknown;
|
|
1309
|
+
};
|
|
1310
|
+
content: {
|
|
1311
|
+
"application/problem+json": components["schemas"]["ApiProblem"];
|
|
1312
|
+
};
|
|
1313
|
+
};
|
|
1314
|
+
/** @description Access is unauthorized. */
|
|
1315
|
+
401: {
|
|
1316
|
+
headers: {
|
|
1317
|
+
[name: string]: unknown;
|
|
1318
|
+
};
|
|
1319
|
+
content: {
|
|
1320
|
+
"application/problem+json": components["schemas"]["ApiProblem"];
|
|
1321
|
+
};
|
|
1322
|
+
};
|
|
1323
|
+
};
|
|
1324
|
+
};
|
|
1203
1325
|
ForemanApi_listRuns: {
|
|
1204
1326
|
parameters: {
|
|
1205
1327
|
query?: {
|
package/dist/index.d.ts
CHANGED
|
@@ -26,12 +26,15 @@ export type GateView = Schemas["GateView"];
|
|
|
26
26
|
export type ApiProblem = Schemas["ApiProblem"];
|
|
27
27
|
export type WorkSubmission = Schemas["WorkSubmission"];
|
|
28
28
|
export type WorkItemView = Schemas["WorkItemView"];
|
|
29
|
+
export type WorkItemList = Schemas["WorkItemList"];
|
|
29
30
|
export type WorkRequest = Schemas["WorkRequest"];
|
|
30
31
|
export type RunView = Schemas["RunView"];
|
|
31
32
|
export type StageView = Schemas["StageView"];
|
|
32
33
|
export type CheckView = Schemas["CheckView"];
|
|
33
34
|
export type RunSignal = Schemas["RunSignal"];
|
|
34
35
|
export type SandboxSpec = Schemas["SandboxSpec"];
|
|
36
|
+
export type Health = Schemas["Health"];
|
|
37
|
+
export type HealthCheck = Schemas["HealthCheck"];
|
|
35
38
|
/** One reason a document does not match its format. `path` is a JSON Pointer into the document. */
|
|
36
39
|
export interface Problem {
|
|
37
40
|
path: string;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,oHAAoH;AACpH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,OAAO,EAAE,EAA2C,MAAM,kBAAkB,CAAC;AACpF,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,EAAe,WAAW,EAAE,MAAM,cAAc,CAAC;AAGxD,OAAO,EAAE,IAAI,IAAI,eAAe,EAAe,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,OAAO,EAAe,WAAW,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,EAAqC,QAAQ,EAAuB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/openapi/openapi.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.1.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Open Software Factory contracts",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.2.0",
|
|
6
6
|
"description": "The shared formats as named models, and the HTTP APIs of the modules. Generated by `pnpm build` in contracts/ from typespec/. Do not edit.",
|
|
7
7
|
"license": {
|
|
8
8
|
"name": "Apache-2.0",
|
|
@@ -117,6 +117,87 @@
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
},
|
|
120
|
+
"/v1/health": {
|
|
121
|
+
"get": {
|
|
122
|
+
"operationId": "HealthApi_getHealth",
|
|
123
|
+
"description": "The server's health. Needs no token, and names no secret. 503 when a check fails.",
|
|
124
|
+
"parameters": [],
|
|
125
|
+
"responses": {
|
|
126
|
+
"200": {
|
|
127
|
+
"description": "The request has succeeded.",
|
|
128
|
+
"content": {
|
|
129
|
+
"application/json": {
|
|
130
|
+
"schema": {
|
|
131
|
+
"$ref": "#/components/schemas/Health"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
"503": {
|
|
137
|
+
"description": "Service unavailable.",
|
|
138
|
+
"content": {
|
|
139
|
+
"application/json": {
|
|
140
|
+
"schema": {
|
|
141
|
+
"$ref": "#/components/schemas/Health"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
"security": [
|
|
148
|
+
{}
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
"/v1/inbox": {
|
|
153
|
+
"get": {
|
|
154
|
+
"operationId": "IntakeApi_listWorkItems",
|
|
155
|
+
"description": "The 50 latest work items, the one received last first; with `status`, only the work items in that status.",
|
|
156
|
+
"parameters": [
|
|
157
|
+
{
|
|
158
|
+
"name": "status",
|
|
159
|
+
"in": "query",
|
|
160
|
+
"required": false,
|
|
161
|
+
"schema": {
|
|
162
|
+
"$ref": "#/components/schemas/WorkItemStatus"
|
|
163
|
+
},
|
|
164
|
+
"explode": false
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
"responses": {
|
|
168
|
+
"200": {
|
|
169
|
+
"description": "The request has succeeded.",
|
|
170
|
+
"content": {
|
|
171
|
+
"application/json": {
|
|
172
|
+
"schema": {
|
|
173
|
+
"$ref": "#/components/schemas/WorkItemList"
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
"400": {
|
|
179
|
+
"description": "The server could not understand the request due to invalid syntax.",
|
|
180
|
+
"content": {
|
|
181
|
+
"application/problem+json": {
|
|
182
|
+
"schema": {
|
|
183
|
+
"$ref": "#/components/schemas/ApiProblem"
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
"401": {
|
|
189
|
+
"description": "Access is unauthorized.",
|
|
190
|
+
"content": {
|
|
191
|
+
"application/problem+json": {
|
|
192
|
+
"schema": {
|
|
193
|
+
"$ref": "#/components/schemas/ApiProblem"
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
},
|
|
120
201
|
"/v1/runs": {
|
|
121
202
|
"post": {
|
|
122
203
|
"operationId": "ForemanApi_startRun",
|
|
@@ -1388,6 +1469,55 @@
|
|
|
1388
1469
|
],
|
|
1389
1470
|
"description": "gatekeeper: a gate as the HTTP API shows it (ADR 0048)."
|
|
1390
1471
|
},
|
|
1472
|
+
"Health": {
|
|
1473
|
+
"type": "object",
|
|
1474
|
+
"properties": {
|
|
1475
|
+
"status": {
|
|
1476
|
+
"anyOf": [
|
|
1477
|
+
{
|
|
1478
|
+
"type": "string",
|
|
1479
|
+
"const": "ok"
|
|
1480
|
+
},
|
|
1481
|
+
{
|
|
1482
|
+
"type": "string",
|
|
1483
|
+
"const": "failing"
|
|
1484
|
+
}
|
|
1485
|
+
]
|
|
1486
|
+
},
|
|
1487
|
+
"checks": {
|
|
1488
|
+
"type": "array",
|
|
1489
|
+
"items": {
|
|
1490
|
+
"$ref": "#/components/schemas/HealthCheck"
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
},
|
|
1494
|
+
"required": [
|
|
1495
|
+
"status",
|
|
1496
|
+
"checks"
|
|
1497
|
+
],
|
|
1498
|
+
"description": "The state of a server: `ok` when every check is ok."
|
|
1499
|
+
},
|
|
1500
|
+
"HealthCheck": {
|
|
1501
|
+
"type": "object",
|
|
1502
|
+
"properties": {
|
|
1503
|
+
"name": {
|
|
1504
|
+
"$ref": "#/components/schemas/Name",
|
|
1505
|
+
"description": "The system, like \"database\", \"nats\" or \"temporal\"."
|
|
1506
|
+
},
|
|
1507
|
+
"ok": {
|
|
1508
|
+
"type": "boolean"
|
|
1509
|
+
},
|
|
1510
|
+
"detail": {
|
|
1511
|
+
"type": "string",
|
|
1512
|
+
"description": "What failed and how to fix it, when `ok` is false. Never a secret."
|
|
1513
|
+
}
|
|
1514
|
+
},
|
|
1515
|
+
"required": [
|
|
1516
|
+
"name",
|
|
1517
|
+
"ok"
|
|
1518
|
+
],
|
|
1519
|
+
"description": "One system a server needs, such as its database or NATS, and whether the server reaches it now."
|
|
1520
|
+
},
|
|
1391
1521
|
"Hostname": {
|
|
1392
1522
|
"type": "string",
|
|
1393
1523
|
"pattern": "^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)*$",
|
|
@@ -2423,6 +2553,10 @@
|
|
|
2423
2553
|
"type": "string",
|
|
2424
2554
|
"format": "date-time"
|
|
2425
2555
|
},
|
|
2556
|
+
"traceId": {
|
|
2557
|
+
"$ref": "#/components/schemas/TraceId",
|
|
2558
|
+
"description": "The OpenTelemetry trace of the run: the first 32 hex digits of the SHA-256 of `runId` (ADR 0087)."
|
|
2559
|
+
},
|
|
2426
2560
|
"stages": {
|
|
2427
2561
|
"type": "array",
|
|
2428
2562
|
"items": {
|
|
@@ -2959,6 +3093,21 @@
|
|
|
2959
3093
|
],
|
|
2960
3094
|
"description": "One unit of requested work: the `data` of a `work.received@v1` CloudEvent."
|
|
2961
3095
|
},
|
|
3096
|
+
"WorkItemList": {
|
|
3097
|
+
"type": "object",
|
|
3098
|
+
"properties": {
|
|
3099
|
+
"workItems": {
|
|
3100
|
+
"type": "array",
|
|
3101
|
+
"items": {
|
|
3102
|
+
"$ref": "#/components/schemas/WorkItemView"
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
},
|
|
3106
|
+
"required": [
|
|
3107
|
+
"workItems"
|
|
3108
|
+
],
|
|
3109
|
+
"description": "intake: the latest work items, as `GET /v1/inbox` answers them, the one received last first."
|
|
3110
|
+
},
|
|
2962
3111
|
"WorkItemStatus": {
|
|
2963
3112
|
"type": "string",
|
|
2964
3113
|
"enum": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opensoftware/contracts",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "The shared formats of the open software factory: JSON Schemas, TypeScript types and the osf-contracts CLI.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@typespec/openapi": "1.17.0",
|
|
45
45
|
"@typespec/openapi3": "1.17.0",
|
|
46
46
|
"openapi-typescript": "7.13.0",
|
|
47
|
-
"@opensoftware/testkit": "0.
|
|
47
|
+
"@opensoftware/testkit": "0.31.0"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "node scripts/build.ts",
|
package/typespec/foreman.tsp
CHANGED
|
@@ -97,6 +97,9 @@ model RunView {
|
|
|
97
97
|
startedAt: utcDateTime;
|
|
98
98
|
endedAt?: utcDateTime;
|
|
99
99
|
|
|
100
|
+
/** The OpenTelemetry trace of the run: the first 32 hex digits of the SHA-256 of `runId` (ADR 0087). */
|
|
101
|
+
traceId?: TraceId;
|
|
102
|
+
|
|
100
103
|
/** The stages of the workflow, in order. */
|
|
101
104
|
stages: StageView[];
|
|
102
105
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// The health route that intake, gatekeeper and the foreman each serve (ADR 0086): the state of the server and of each
|
|
2
|
+
// system it needs. `console doctor` and the Compose profile's health checks read it.
|
|
3
|
+
import "@typespec/http";
|
|
4
|
+
import "./common.tsp";
|
|
5
|
+
import "./gatekeeper-api.tsp";
|
|
6
|
+
|
|
7
|
+
using TypeSpec.Http;
|
|
8
|
+
|
|
9
|
+
namespace Osf.Contracts;
|
|
10
|
+
|
|
11
|
+
/** One system a server needs, such as its database or NATS, and whether the server reaches it now. */
|
|
12
|
+
model HealthCheck {
|
|
13
|
+
/** The system, like "database", "nats" or "temporal". */
|
|
14
|
+
name: Name;
|
|
15
|
+
|
|
16
|
+
ok: boolean;
|
|
17
|
+
|
|
18
|
+
/** What failed and how to fix it, when `ok` is false. Never a secret. */
|
|
19
|
+
detail?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** The state of a server: `ok` when every check is ok. */
|
|
23
|
+
model Health {
|
|
24
|
+
status: "ok" | "failing";
|
|
25
|
+
checks: HealthCheck[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
namespace HealthApi {
|
|
29
|
+
/** The server's health. Needs no token, and names no secret. 503 when a check fails. */
|
|
30
|
+
@route("/v1/health")
|
|
31
|
+
@get
|
|
32
|
+
@useAuth(NoAuth)
|
|
33
|
+
op getHealth():
|
|
34
|
+
| {
|
|
35
|
+
@statusCode statusCode: 200;
|
|
36
|
+
@body health: Health;
|
|
37
|
+
}
|
|
38
|
+
| {
|
|
39
|
+
@statusCode statusCode: 503;
|
|
40
|
+
@body health: Health;
|
|
41
|
+
};
|
|
42
|
+
}
|
package/typespec/intake-api.tsp
CHANGED
|
@@ -33,6 +33,14 @@ namespace IntakeApi {
|
|
|
33
33
|
| GatekeeperApi.Failure<401>
|
|
34
34
|
| GatekeeperApi.Failure<404>;
|
|
35
35
|
|
|
36
|
+
/** The 50 latest work items, the one received last first; with `status`, only the work items in that status. */
|
|
37
|
+
@route("/v1/inbox")
|
|
38
|
+
@get
|
|
39
|
+
op listWorkItems(@query status?: WorkItemStatus):
|
|
40
|
+
| WorkItemList
|
|
41
|
+
| GatekeeperApi.Failure<400>
|
|
42
|
+
| GatekeeperApi.Failure<401>;
|
|
43
|
+
|
|
36
44
|
/** A work item, its status and every request that asked for it. */
|
|
37
45
|
@route("/v1/work-items/{workItemId}")
|
|
38
46
|
@get
|
package/typespec/intake.tsp
CHANGED
|
@@ -43,6 +43,11 @@ model WorkRequest {
|
|
|
43
43
|
receivedAt: utcDateTime;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/** intake: the latest work items, as `GET /v1/inbox` answers them, the one received last first. */
|
|
47
|
+
model WorkItemList {
|
|
48
|
+
workItems: WorkItemView[];
|
|
49
|
+
}
|
|
50
|
+
|
|
46
51
|
/** intake: a work item as the HTTP API shows it (ADR 0063). */
|
|
47
52
|
model WorkItemView {
|
|
48
53
|
/** Its ID is "wi_" and 20 hex digits of the SHA-256 of its dedupe key. */
|
package/typespec/main.tsp
CHANGED