@opensoftware/contracts 1.0.0 → 1.5.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.
@@ -2,14 +2,127 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Open Software Factory contracts",
5
- "version": "1.0.0",
6
- "description": "The shared formats as named models. No operations: module APIs are added per module. Generated by `pnpm build` in contracts/ from typespec/. Do not edit.",
5
+ "version": "1.5.0",
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",
9
9
  "identifier": "Apache-2.0"
10
10
  }
11
11
  },
12
- "paths": {},
12
+ "paths": {
13
+ "/v1/gates": {
14
+ "post": {
15
+ "operationId": "GatekeeperApi_requestGate",
16
+ "description": "Ask the policy's approver groups for a decision. The same request again answers the gate it made before.",
17
+ "parameters": [],
18
+ "responses": {
19
+ "200": {
20
+ "description": "The request has succeeded.",
21
+ "content": {
22
+ "application/json": {
23
+ "schema": {
24
+ "$ref": "#/components/schemas/GateView"
25
+ }
26
+ }
27
+ }
28
+ },
29
+ "201": {
30
+ "description": "The request has succeeded and a new resource has been created as a result.",
31
+ "content": {
32
+ "application/json": {
33
+ "schema": {
34
+ "$ref": "#/components/schemas/GateView"
35
+ }
36
+ }
37
+ }
38
+ },
39
+ "400": {
40
+ "description": "The server could not understand the request due to invalid syntax.",
41
+ "content": {
42
+ "application/problem+json": {
43
+ "schema": {
44
+ "$ref": "#/components/schemas/ApiProblem"
45
+ }
46
+ }
47
+ }
48
+ },
49
+ "401": {
50
+ "description": "Access is unauthorized.",
51
+ "content": {
52
+ "application/problem+json": {
53
+ "schema": {
54
+ "$ref": "#/components/schemas/ApiProblem"
55
+ }
56
+ }
57
+ }
58
+ }
59
+ },
60
+ "requestBody": {
61
+ "required": true,
62
+ "content": {
63
+ "application/json": {
64
+ "schema": {
65
+ "$ref": "#/components/schemas/GateRequest"
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ },
72
+ "/v1/gates/{gateId}": {
73
+ "get": {
74
+ "operationId": "GatekeeperApi_getGate",
75
+ "description": "A gate: its state, and its record once it is decided.",
76
+ "parameters": [
77
+ {
78
+ "name": "gateId",
79
+ "in": "path",
80
+ "required": true,
81
+ "schema": {
82
+ "type": "string"
83
+ }
84
+ }
85
+ ],
86
+ "responses": {
87
+ "200": {
88
+ "description": "The request has succeeded.",
89
+ "content": {
90
+ "application/json": {
91
+ "schema": {
92
+ "$ref": "#/components/schemas/GateView"
93
+ }
94
+ }
95
+ }
96
+ },
97
+ "401": {
98
+ "description": "Access is unauthorized.",
99
+ "content": {
100
+ "application/problem+json": {
101
+ "schema": {
102
+ "$ref": "#/components/schemas/ApiProblem"
103
+ }
104
+ }
105
+ }
106
+ },
107
+ "404": {
108
+ "description": "The server cannot find the requested resource.",
109
+ "content": {
110
+ "application/problem+json": {
111
+ "schema": {
112
+ "$ref": "#/components/schemas/ApiProblem"
113
+ }
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
119
+ }
120
+ },
121
+ "security": [
122
+ {
123
+ "BearerAuth": []
124
+ }
125
+ ],
13
126
  "components": {
14
127
  "schemas": {
15
128
  "Actor": {
@@ -101,6 +214,31 @@
101
214
  ],
102
215
  "additionalProperties": false
103
216
  },
217
+ "ApiProblem": {
218
+ "type": "object",
219
+ "properties": {
220
+ "title": {
221
+ "type": "string",
222
+ "minLength": 1,
223
+ "description": "A short summary, the same for every occurrence of the problem."
224
+ },
225
+ "status": {
226
+ "type": "integer",
227
+ "minimum": -2147483648,
228
+ "maximum": 2147483647,
229
+ "description": "The HTTP status code."
230
+ },
231
+ "detail": {
232
+ "type": "string",
233
+ "description": "What went wrong this time, for example the field that is missing."
234
+ }
235
+ },
236
+ "required": [
237
+ "title",
238
+ "status"
239
+ ],
240
+ "description": "An error of an HTTP API (RFC 9457)."
241
+ },
104
242
  "Artifact": {
105
243
  "type": "object",
106
244
  "properties": {
@@ -489,6 +627,127 @@
489
627
  "expired"
490
628
  ]
491
629
  },
630
+ "GateRecord": {
631
+ "type": "object",
632
+ "properties": {
633
+ "gate": {
634
+ "type": "string",
635
+ "pattern": "^g[0-9a-f]{16}$"
636
+ },
637
+ "workItem": {
638
+ "type": "string"
639
+ },
640
+ "stage": {
641
+ "$ref": "#/components/schemas/Name"
642
+ },
643
+ "summary": {
644
+ "type": "string"
645
+ },
646
+ "request": {
647
+ "type": "string",
648
+ "pattern": "^sha256:[0-9a-f]{64}$",
649
+ "description": "SHA-256 of the canonical request: what was approved."
650
+ },
651
+ "policy": {
652
+ "type": "string",
653
+ "minLength": 1,
654
+ "description": "The policy file."
655
+ },
656
+ "policyDigest": {
657
+ "type": "string",
658
+ "pattern": "^sha256:[0-9a-f]{64}$",
659
+ "description": "SHA-256 of the policy's text: the rules that picked the approvers."
660
+ },
661
+ "risk": {
662
+ "type": "string"
663
+ },
664
+ "approvers": {
665
+ "type": "array",
666
+ "items": {
667
+ "$ref": "#/components/schemas/Name"
668
+ }
669
+ },
670
+ "channel": {
671
+ "type": "string"
672
+ },
673
+ "requestedAt": {
674
+ "type": "string",
675
+ "format": "date-time"
676
+ },
677
+ "decision": {
678
+ "$ref": "#/components/schemas/GateDecision"
679
+ },
680
+ "decidedBy": {
681
+ "$ref": "#/components/schemas/Actor",
682
+ "description": "Who decided. Absent only when the gate expired. A policy that names no group decides as a trigger."
683
+ },
684
+ "decidedAt": {
685
+ "type": "string",
686
+ "format": "date-time"
687
+ },
688
+ "reason": {
689
+ "type": "string",
690
+ "description": "The reason of a rejection."
691
+ },
692
+ "signature": {
693
+ "$ref": "#/components/schemas/RecordSignature",
694
+ "description": "Ed25519 over the canonical JSON of the rest of the record. Absent when the record is unsigned."
695
+ }
696
+ },
697
+ "required": [
698
+ "gate",
699
+ "workItem",
700
+ "stage",
701
+ "summary",
702
+ "request",
703
+ "policy",
704
+ "policyDigest",
705
+ "approvers",
706
+ "channel",
707
+ "requestedAt",
708
+ "decision",
709
+ "decidedAt"
710
+ ],
711
+ "description": "gatekeeper: who decided what and when, signed with Ed25519 (ADR 0047)."
712
+ },
713
+ "GateRequest": {
714
+ "type": "object",
715
+ "properties": {
716
+ "workItem": {
717
+ "type": "string",
718
+ "minLength": 1,
719
+ "maxLength": 128,
720
+ "description": "What waits: a work item ID or a URL."
721
+ },
722
+ "stage": {
723
+ "$ref": "#/components/schemas/Name",
724
+ "description": "The stage that waits, for example \"merge\"."
725
+ },
726
+ "summary": {
727
+ "type": "string",
728
+ "minLength": 1,
729
+ "description": "What the approvers decide, shown in the channel."
730
+ },
731
+ "links": {
732
+ "type": "array",
733
+ "items": {
734
+ "$ref": "#/components/schemas/Link"
735
+ }
736
+ },
737
+ "change": {
738
+ "type": "object",
739
+ "properties": {},
740
+ "unevaluatedProperties": {},
741
+ "description": "Any facts the policy needs, such as changed paths, labels or the author."
742
+ }
743
+ },
744
+ "required": [
745
+ "workItem",
746
+ "stage",
747
+ "summary"
748
+ ],
749
+ "description": "gatekeeper: what a person is asked to approve, and the facts the policy decides on (ADR 0047)."
750
+ },
492
751
  "GateRequested": {
493
752
  "type": "object",
494
753
  "properties": {
@@ -597,6 +856,91 @@
597
856
  "summary"
598
857
  ]
599
858
  },
859
+ "GateState": {
860
+ "type": "string",
861
+ "enum": [
862
+ "pending",
863
+ "approved",
864
+ "rejected",
865
+ "expired"
866
+ ],
867
+ "description": "A gate's state: waiting, or how it was decided."
868
+ },
869
+ "GateView": {
870
+ "type": "object",
871
+ "properties": {
872
+ "id": {
873
+ "type": "string",
874
+ "pattern": "^g[0-9a-f]{16}$",
875
+ "description": "\"g\" and 16 hex digits of the SHA-256 of the work item, the stage and the change."
876
+ },
877
+ "workItem": {
878
+ "type": "string"
879
+ },
880
+ "stage": {
881
+ "$ref": "#/components/schemas/Name"
882
+ },
883
+ "summary": {
884
+ "type": "string"
885
+ },
886
+ "risk": {
887
+ "type": "string",
888
+ "description": "The risk the policy named, for example \"high\"."
889
+ },
890
+ "approvers": {
891
+ "type": "array",
892
+ "items": {
893
+ "$ref": "#/components/schemas/Name"
894
+ },
895
+ "description": "The groups whose members may decide. Empty when the policy approved without a person."
896
+ },
897
+ "escalateTo": {
898
+ "type": "array",
899
+ "items": {
900
+ "$ref": "#/components/schemas/Name"
901
+ },
902
+ "description": "The groups asked after the escalation time."
903
+ },
904
+ "channel": {
905
+ "type": "string",
906
+ "description": "Where people decide, for example \"slack:C0GATES\"."
907
+ },
908
+ "state": {
909
+ "$ref": "#/components/schemas/GateState"
910
+ },
911
+ "waitingFor": {
912
+ "type": "array",
913
+ "items": {
914
+ "$ref": "#/components/schemas/Name"
915
+ },
916
+ "description": "The groups that may decide now. Only while the gate is pending."
917
+ },
918
+ "requestedAt": {
919
+ "type": "string",
920
+ "format": "date-time"
921
+ },
922
+ "expiresAt": {
923
+ "type": "string",
924
+ "format": "date-time",
925
+ "description": "When the gate expires without a decision."
926
+ },
927
+ "record": {
928
+ "$ref": "#/components/schemas/GateRecord",
929
+ "description": "The record, once the gate is decided."
930
+ }
931
+ },
932
+ "required": [
933
+ "id",
934
+ "workItem",
935
+ "stage",
936
+ "summary",
937
+ "approvers",
938
+ "channel",
939
+ "state",
940
+ "requestedAt"
941
+ ],
942
+ "description": "gatekeeper: a gate as the HTTP API shows it (ADR 0048)."
943
+ },
600
944
  "Identifier": {
601
945
  "type": "string",
602
946
  "minLength": 1,
@@ -644,6 +988,81 @@
644
988
  "source"
645
989
  ]
646
990
  },
991
+ "PrClosed": {
992
+ "type": "object",
993
+ "properties": {
994
+ "specversion": {
995
+ "type": "string",
996
+ "const": "1.0"
997
+ },
998
+ "id": {
999
+ "$ref": "#/components/schemas/Identifier",
1000
+ "description": "Unique per source. A consumer that sees the same source and id twice handles the event once."
1001
+ },
1002
+ "source": {
1003
+ "type": "string",
1004
+ "minLength": 1,
1005
+ "description": "The producing module, for example \"/osf/intake\"."
1006
+ },
1007
+ "type": {
1008
+ "type": "string",
1009
+ "const": "pr.closed@v1"
1010
+ },
1011
+ "time": {
1012
+ "type": "string",
1013
+ "format": "date-time"
1014
+ },
1015
+ "subject": {
1016
+ "type": "string",
1017
+ "description": "The ID of the thing the event is about: work item, gate, run, score or pull request."
1018
+ },
1019
+ "datacontenttype": {
1020
+ "type": "string",
1021
+ "const": "application/json"
1022
+ },
1023
+ "dataschema": {
1024
+ "type": "string",
1025
+ "format": "uri"
1026
+ },
1027
+ "traceparent": {
1028
+ "type": "string",
1029
+ "description": "W3C trace context (CloudEvents distributed tracing extension)."
1030
+ },
1031
+ "data": {
1032
+ "$ref": "#/components/schemas/PrClosedData"
1033
+ }
1034
+ },
1035
+ "required": [
1036
+ "specversion",
1037
+ "id",
1038
+ "source",
1039
+ "type",
1040
+ "time",
1041
+ "data"
1042
+ ],
1043
+ "description": "forge adapters: a pull request was merged or closed without a merge."
1044
+ },
1045
+ "PrClosedData": {
1046
+ "type": "object",
1047
+ "properties": {
1048
+ "url": {
1049
+ "type": "string",
1050
+ "format": "uri",
1051
+ "description": "The pull request."
1052
+ },
1053
+ "merged": {
1054
+ "type": "boolean",
1055
+ "description": "True when the pull request was merged, false when it was closed without a merge."
1056
+ },
1057
+ "mergeCommit": {
1058
+ "$ref": "#/components/schemas/CommitHash"
1059
+ }
1060
+ },
1061
+ "required": [
1062
+ "url",
1063
+ "merged"
1064
+ ]
1065
+ },
647
1066
  "PrOpened": {
648
1067
  "type": "object",
649
1068
  "properties": {
@@ -747,6 +1166,85 @@
747
1166
  "author"
748
1167
  ]
749
1168
  },
1169
+ "PrReviewed": {
1170
+ "type": "object",
1171
+ "properties": {
1172
+ "specversion": {
1173
+ "type": "string",
1174
+ "const": "1.0"
1175
+ },
1176
+ "id": {
1177
+ "$ref": "#/components/schemas/Identifier",
1178
+ "description": "Unique per source. A consumer that sees the same source and id twice handles the event once."
1179
+ },
1180
+ "source": {
1181
+ "type": "string",
1182
+ "minLength": 1,
1183
+ "description": "The producing module, for example \"/osf/intake\"."
1184
+ },
1185
+ "type": {
1186
+ "type": "string",
1187
+ "const": "pr.reviewed@v1"
1188
+ },
1189
+ "time": {
1190
+ "type": "string",
1191
+ "format": "date-time"
1192
+ },
1193
+ "subject": {
1194
+ "type": "string",
1195
+ "description": "The ID of the thing the event is about: work item, gate, run, score or pull request."
1196
+ },
1197
+ "datacontenttype": {
1198
+ "type": "string",
1199
+ "const": "application/json"
1200
+ },
1201
+ "dataschema": {
1202
+ "type": "string",
1203
+ "format": "uri"
1204
+ },
1205
+ "traceparent": {
1206
+ "type": "string",
1207
+ "description": "W3C trace context (CloudEvents distributed tracing extension)."
1208
+ },
1209
+ "data": {
1210
+ "$ref": "#/components/schemas/PrReviewedData"
1211
+ }
1212
+ },
1213
+ "required": [
1214
+ "specversion",
1215
+ "id",
1216
+ "source",
1217
+ "type",
1218
+ "time",
1219
+ "data"
1220
+ ],
1221
+ "description": "forge adapters: a person, an agent or a bot reviewed or commented on a pull request."
1222
+ },
1223
+ "PrReviewedData": {
1224
+ "type": "object",
1225
+ "properties": {
1226
+ "url": {
1227
+ "type": "string",
1228
+ "format": "uri",
1229
+ "description": "The pull request."
1230
+ },
1231
+ "reviewer": {
1232
+ "$ref": "#/components/schemas/Actor"
1233
+ },
1234
+ "automated": {
1235
+ "type": "boolean",
1236
+ "description": "True when an agent or a bot wrote the review, not a person."
1237
+ },
1238
+ "state": {
1239
+ "$ref": "#/components/schemas/ReviewState"
1240
+ }
1241
+ },
1242
+ "required": [
1243
+ "url",
1244
+ "reviewer",
1245
+ "state"
1246
+ ]
1247
+ },
750
1248
  "Priority": {
751
1249
  "type": "string",
752
1250
  "enum": [
@@ -843,6 +1341,38 @@
843
1341
  "description": "The adapters a factory uses, per port (docs/architecture/internals.md). Keys are adapter names.",
844
1342
  "additionalProperties": false
845
1343
  },
1344
+ "RecordSignature": {
1345
+ "type": "object",
1346
+ "properties": {
1347
+ "algorithm": {
1348
+ "type": "string",
1349
+ "const": "Ed25519"
1350
+ },
1351
+ "key": {
1352
+ "type": "string",
1353
+ "pattern": "^sha256:",
1354
+ "description": "SHA-256 of the public key in DER, like \"sha256:…\"."
1355
+ },
1356
+ "value": {
1357
+ "type": "string",
1358
+ "minLength": 1,
1359
+ "description": "The signature in base64."
1360
+ }
1361
+ },
1362
+ "required": [
1363
+ "algorithm",
1364
+ "key",
1365
+ "value"
1366
+ ]
1367
+ },
1368
+ "ReviewState": {
1369
+ "type": "string",
1370
+ "enum": [
1371
+ "approved",
1372
+ "changes_requested",
1373
+ "commented"
1374
+ ]
1375
+ },
846
1376
  "RunCompleted": {
847
1377
  "type": "object",
848
1378
  "properties": {
@@ -1548,6 +2078,12 @@
1548
2078
  ],
1549
2079
  "description": "intake: a work item was accepted."
1550
2080
  }
2081
+ },
2082
+ "securitySchemes": {
2083
+ "BearerAuth": {
2084
+ "type": "http",
2085
+ "scheme": "Bearer"
2086
+ }
1551
2087
  }
1552
2088
  }
1553
2089
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opensoftware/contracts",
3
- "version": "1.0.0",
3
+ "version": "1.5.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": {
@@ -39,9 +39,12 @@
39
39
  "devDependencies": {
40
40
  "@asyncapi/parser": "3.6.6",
41
41
  "@typespec/compiler": "1.17.0",
42
+ "@typespec/http": "1.17.0",
42
43
  "@typespec/json-schema": "1.17.0",
44
+ "@typespec/openapi": "1.17.0",
45
+ "@typespec/openapi3": "1.17.0",
43
46
  "openapi-typescript": "7.13.0",
44
- "@opensoftware/testkit": "0.0.0"
47
+ "@opensoftware/testkit": "0.12.0"
45
48
  },
46
49
  "scripts": {
47
50
  "build": "node scripts/build.ts",
@@ -54,6 +54,12 @@ model ScoreRecorded is CloudEvent<"score.recorded@v1", Score>;
54
54
  /** forge adapters: the factory opened a pull request. */
55
55
  model PrOpened is CloudEvent<"pr.opened@v1", PrOpenedData>;
56
56
 
57
+ /** forge adapters: a person, an agent or a bot reviewed or commented on a pull request. */
58
+ model PrReviewed is CloudEvent<"pr.reviewed@v1", PrReviewedData>;
59
+
60
+ /** forge adapters: a pull request was merged or closed without a merge. */
61
+ model PrClosed is CloudEvent<"pr.closed@v1", PrClosedData>;
62
+
57
63
  model RunCompletedData {
58
64
  runId: Identifier;
59
65
  workItemId: Identifier;
@@ -132,3 +138,31 @@ model PrOpenedData {
132
138
  workItemId?: Identifier;
133
139
  runId?: Identifier;
134
140
  }
141
+
142
+ enum ReviewState {
143
+ approved,
144
+ changes_requested,
145
+ commented,
146
+ }
147
+
148
+ model PrReviewedData {
149
+ /** The pull request. */
150
+ url: url;
151
+
152
+ reviewer: Actor;
153
+
154
+ /** True when an agent or a bot wrote the review, not a person. */
155
+ automated?: boolean;
156
+
157
+ state: ReviewState;
158
+ }
159
+
160
+ model PrClosedData {
161
+ /** The pull request. */
162
+ url: url;
163
+
164
+ /** True when the pull request was merged, false when it was closed without a merge. */
165
+ merged: boolean;
166
+
167
+ mergeCommit?: CommitHash;
168
+ }