@crvouga/atlas 1.19.0 → 1.20.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 +7 -1
- package/dist/schema/src/api.d.ts +55 -0
- package/dist/schema/src/api.js +33 -0
- package/dist/server/src/app.js +3 -1
- package/dist/server/src/openapi.d.ts +1 -1
- package/dist/server/src/openapi.js +24 -2
- package/dist/server/src/recording-verification.d.ts +34 -0
- package/dist/server/src/recording-verification.js +118 -0
- package/docs/reference/http-api.md +52 -0
- package/docs/reference/schema.md +41 -0
- package/docs/reference/server.md +39 -0
- package/docs/rich-evidence.md +12 -0
- package/docs/server.md +12 -0
- package/llms-full.txt +76 -0
- package/package.json +1 -1
- package/packages/server/openapi.json +527 -1
- package/packages/visualizer/src/explorer/RunsView.tsx +32 -6
- package/packages/visualizer/src/explorer/catalog.tsx +24 -1
- package/packages/visualizer/src/explorer/recording-verification.ts +58 -0
package/llms-full.txt
CHANGED
|
@@ -1493,6 +1493,18 @@ two ways without touching a check's result:
|
|
|
1493
1493
|
`--format json` prints the `verify-recordings-result` document. `--layer <id>` and `--limit <n>` narrow the work, since
|
|
1494
1494
|
it reads blobs. A store that cannot answer is `unreadable`, never verified. Test results are unchanged either way.
|
|
1495
1495
|
|
|
1496
|
+
- **In the viewer.** A publish token starts the same read-back on the server with
|
|
1497
|
+
`POST /api/reports/recordings/verification` (body optional: `{ "layers": ["<id prefix>"], "limit": 20 }`; the newest
|
|
1498
|
+
20 layers by default, at most 200, one read-back at a time; a reader token, a browser sign-in box or an anonymous
|
|
1499
|
+
caller is refused because it reads blobs). Anyone who can read the report then sees the result with
|
|
1500
|
+
`GET /api/reports/recordings/verification` (which reads no blob): per layer, its run, how many videos were read back, how
|
|
1501
|
+
many passed and each gap. The Runs tab and an opened run show `Recording unavailable: 1 of 3 stored videos failed the
|
|
1502
|
+
read-back (1 file gone from storage)` beside the green outcomes, or `3 stored videos verified`, and a run whose stored
|
|
1503
|
+
videos all failed offers no Watch action. A run whose layers were never read back shows nothing: unknown is not
|
|
1504
|
+
verified. The result lives in the server's memory only, so a restart empties it, and a layer that garbage collection
|
|
1505
|
+
removed drops out. An older server has no such route and the viewer then shows nothing. No status, slot or winner is
|
|
1506
|
+
affected; `capabilities.recordingVerification` in `GET /api/server` says a server offers it.
|
|
1507
|
+
|
|
1496
1508
|
Both are statements about stored bytes. Atlas does not check that a stored video plays in a given browser (a valid header
|
|
1497
1509
|
can still have an unsupported codec or be truncated), and acknowledging an upload is not playback. Videos given only by
|
|
1498
1510
|
`uri` are counted as referenced and not checked.
|
|
@@ -2015,6 +2027,14 @@ edit, and the server's time. Nothing about the layer, its results or the report'
|
|
|
2015
2027
|
See [Tagging artifacts and demos](rich-evidence.md#tagging-artifacts-and-demos) for the tag rules. Reviewer tags are
|
|
2016
2028
|
shown in the viewer's evidence; the tests API (`artifactTag`) searches the producers' tags.
|
|
2017
2029
|
|
|
2030
|
+
## Reading recordings back
|
|
2031
|
+
|
|
2032
|
+
`POST /api/reports/recordings/verification` (publish, `Authorization: Bearer`) reads the stored videos of the newest layers
|
|
2033
|
+
back and checks them; `GET` (read) returns the newest result per layer without reading any blob, so the viewer can mark a
|
|
2034
|
+
run whose recording was stored and then lost, apart from its test results. The result is in memory only and layers
|
|
2035
|
+
that were never read back are absent (unknown, never "fine"). See
|
|
2036
|
+
[Is the recording still there?](rich-evidence.md#is-the-recording-still-there) for what is checked and what is not.
|
|
2037
|
+
|
|
2018
2038
|
## Review collections
|
|
2019
2039
|
|
|
2020
2040
|
A team can publish a named, curated review of a few behaviors: recordings from several explicit executions, notes on
|
|
@@ -2194,6 +2214,10 @@ curl "$ATLAS_SERVER_URL/api/projects/$ATLAS_PROJECT/reports/tests?format=markdow
|
|
|
2194
2214
|
| `GET /api/projects/{project}/reports/artifact-tags` | read | Reviewer tags of attachments: every recorded edit. |
|
|
2195
2215
|
| `POST /api/reports/artifact-tags` | publish | Tag or untag one attachment after upload. |
|
|
2196
2216
|
| `POST /api/projects/{project}/reports/artifact-tags` | publish | Tag or untag one attachment after upload. |
|
|
2217
|
+
| `GET /api/reports/recordings/verification` | read | Which stored recordings the server last read back, and which were gone or damaged. |
|
|
2218
|
+
| `GET /api/projects/{project}/reports/recordings/verification` | read | Which stored recordings the server last read back, and which were gone or damaged. |
|
|
2219
|
+
| `POST /api/reports/recordings/verification` | publish | Read stored recordings back and check them (publish token). |
|
|
2220
|
+
| `POST /api/projects/{project}/reports/recordings/verification` | publish | Read stored recordings back and check them (publish token). |
|
|
2197
2221
|
| `GET /api/reports/review-collections` | read | Review collections: curated sets of recordings, newest first. |
|
|
2198
2222
|
| `GET /api/projects/{project}/reports/review-collections` | read | Review collections: curated sets of recordings, newest first. |
|
|
2199
2223
|
| `POST /api/reports/review-collections` | publish | Publish an immutable review collection (or a new revision of one). |
|
|
@@ -6086,6 +6110,58 @@ Scoped to one project. The same project may be selected on the unprefixed API us
|
|
|
6086
6110
|
|
|
6087
6111
|
Responses: `201` The recorded entry.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `403` The token is known but its role may not do this.; `413` Request too large.; `422` Not a tag edit: a bad target, an empty or over-long tag, or nothing to change.
|
|
6088
6112
|
|
|
6113
|
+
### Which stored recordings the server last read back, and which were gone or damaged
|
|
6114
|
+
|
|
6115
|
+
`GET /api/reports/recordings/verification` · role: read
|
|
6116
|
+
|
|
6117
|
+
The newest read-back the server did of stored videos, per layer: how many it checked, how many passed and the `gaps` (a blob that is gone, bytes of another size or hash than were published, or a file that does not begin like its container), each naming the check that holds the video. `run` is the key of the run the layer belongs to (a `RunSummary.key`), so a viewer can mark that run's recordings as stored-then-unavailable beside its test results.
|
|
6118
|
+
|
|
6119
|
+
This route never reads a blob: it returns what a publish token's `POST` found earlier. A layer that was never read back is absent, which means "not verified", not "fine"; the result is kept in the server's memory only, so a restart empties it. Only layers a store still holds are listed (one removed by garbage collection drops out). No status, slot or winner depends on it. It checks stored bytes, not browser playback. Narrow with `?run=` (a run key) and `?layer=` (an id prefix of at least 7 hex digits, repeatable).
|
|
6120
|
+
|
|
6121
|
+
- `run` (query): Only layers of this run (a key from `GET /api/reports/runs`).
|
|
6122
|
+
- `layer` (query): Only layers whose id starts with this prefix. Repeatable or comma-separated.
|
|
6123
|
+
- `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
6124
|
+
|
|
6125
|
+
Responses: `200` The held read-backs.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.
|
|
6126
|
+
|
|
6127
|
+
### Which stored recordings the server last read back, and which were gone or damaged
|
|
6128
|
+
|
|
6129
|
+
`GET /api/projects/{project}/reports/recordings/verification` · role: read
|
|
6130
|
+
|
|
6131
|
+
The newest read-back the server did of stored videos, per layer: how many it checked, how many passed and the `gaps` (a blob that is gone, bytes of another size or hash than were published, or a file that does not begin like its container), each naming the check that holds the video. `run` is the key of the run the layer belongs to (a `RunSummary.key`), so a viewer can mark that run's recordings as stored-then-unavailable beside its test results.
|
|
6132
|
+
|
|
6133
|
+
This route never reads a blob: it returns what a publish token's `POST` found earlier. A layer that was never read back is absent, which means "not verified", not "fine"; the result is kept in the server's memory only, so a restart empties it. Only layers a store still holds are listed (one removed by garbage collection drops out). No status, slot or winner depends on it. It checks stored bytes, not browser playback. Narrow with `?run=` (a run key) and `?layer=` (an id prefix of at least 7 hex digits, repeatable).
|
|
6134
|
+
|
|
6135
|
+
Scoped to one project. The same project may be selected on the unprefixed API using ?project= or X-Atlas-Project. Unknown projects return 404 on reads and are created by an authenticated upload.
|
|
6136
|
+
|
|
6137
|
+
- `project` (path, required): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
6138
|
+
- `run` (query): Only layers of this run (a key from `GET /api/reports/runs`).
|
|
6139
|
+
- `layer` (query): Only layers whose id starts with this prefix. Repeatable or comma-separated.
|
|
6140
|
+
|
|
6141
|
+
Responses: `200` The held read-backs.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.
|
|
6142
|
+
|
|
6143
|
+
### Read stored recordings back and check them (publish token)
|
|
6144
|
+
|
|
6145
|
+
`POST /api/reports/recordings/verification` · role: publish
|
|
6146
|
+
|
|
6147
|
+
Reads the videos of the newest layers (`limit`, default 20, at most 200; or the layers `layers` names) back from storage and checks that each is still stored, still has the size and SHA-256 published, and still begins like its media type's container. This reads blobs, so it needs a publish (or admin) token in a Bearer header; a reader-only token, a browser sign-in box and an anonymous reader are refused. One read-back runs at a time (`409` otherwise). It writes nothing to storage and changes no status. The answer is the same document `GET` returns, with `verified`: how many layers this pass read back.
|
|
6148
|
+
|
|
6149
|
+
- `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
6150
|
+
|
|
6151
|
+
Responses: `200` The read-backs now held.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `403` The token is known but its role may not do this.; `409` A read-back is already running.; `413` Request too large.; `422` Not a verification request.; `502` A store could not be read.; `503` The server is still reading its backends. Retry after the number of seconds in `Retry-After`.
|
|
6152
|
+
|
|
6153
|
+
### Read stored recordings back and check them (publish token)
|
|
6154
|
+
|
|
6155
|
+
`POST /api/projects/{project}/reports/recordings/verification` · role: publish
|
|
6156
|
+
|
|
6157
|
+
Reads the videos of the newest layers (`limit`, default 20, at most 200; or the layers `layers` names) back from storage and checks that each is still stored, still has the size and SHA-256 published, and still begins like its media type's container. This reads blobs, so it needs a publish (or admin) token in a Bearer header; a reader-only token, a browser sign-in box and an anonymous reader are refused. One read-back runs at a time (`409` otherwise). It writes nothing to storage and changes no status. The answer is the same document `GET` returns, with `verified`: how many layers this pass read back.
|
|
6158
|
+
|
|
6159
|
+
Scoped to one project. The same project may be selected on the unprefixed API using ?project= or X-Atlas-Project. Unknown projects return 404 on reads and are created by an authenticated upload.
|
|
6160
|
+
|
|
6161
|
+
- `project` (path, required): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
6162
|
+
|
|
6163
|
+
Responses: `200` The read-backs now held.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `403` The token is known but its role may not do this.; `409` A read-back is already running.; `413` Request too large.; `422` Not a verification request.; `502` A store could not be read.; `503` The server is still reading its backends. Retry after the number of seconds in `Retry-After`.
|
|
6164
|
+
|
|
6089
6165
|
### Review collections: curated sets of recordings, newest first
|
|
6090
6166
|
|
|
6091
6167
|
`GET /api/reports/review-collections` · role: read
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crvouga/atlas",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"description": "Atlas: specs of any kind, reported as layered evidence. Publish checks from any runner (JUnit, CTRF, a script), combine them across nodes, branches, environments and backends, gate CI on the result, and run statecharts against the real app.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.1.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Atlas server API",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.7.0",
|
|
6
6
|
"summary": "Take in test results from every environment, keep them, and show everyone the combined report, live.",
|
|
7
7
|
"description": "One Atlas node serves multiple projects. Clients select a project using ?project=<slug>, X-Atlas-Project, or /api/projects/<slug>/… . Each project has independent specs, layers, recordings, status, and event streams. Within each project, the newest completed result of every check wins its slot. Unqualified requests address the legacy default project.\n\n**Authentication.** With tokens configured, send `Authorization: Bearer <token>`. Roles: `read` < `critic` < `publish` < `admin`. A `critic` token may read, and publish critiques (layers of automated reviewers) and nothing else. Uploading needs a Bearer token; a browser's HTTP Basic sign-in (any user name, the token as the password) is accepted for reading only. A server with no tokens is open and listens on this machine only unless told otherwise.\n\n**Errors** are [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem documents (`application/problem+json`) with a `detail` that says what to do. `401` send a token, `403` that token may not, `409` upload what this depends on first or you are changing something immutable, `413` too large, `422` not valid, `503` still starting (retry after `Retry-After`).\n\n**Compatibility.** Within `/api` the API only grows: optional parameters, optional response fields, new events. Ignore what you do not know.\n\nGuides: `/llms.txt`. Source: https://github.com/crvouga/atlas.",
|
|
8
8
|
"license": {
|
|
@@ -4802,6 +4802,394 @@
|
|
|
4802
4802
|
}
|
|
4803
4803
|
}
|
|
4804
4804
|
},
|
|
4805
|
+
"/api/reports/recordings/verification": {
|
|
4806
|
+
"get": {
|
|
4807
|
+
"operationId": "getRecordingVerification",
|
|
4808
|
+
"tags": [
|
|
4809
|
+
"Report"
|
|
4810
|
+
],
|
|
4811
|
+
"summary": "Which stored recordings the server last read back, and which were gone or damaged",
|
|
4812
|
+
"description": "The newest read-back the server did of stored videos, per layer: how many it checked, how many passed and the `gaps` (a blob that is gone, bytes of another size or hash than were published, or a file that does not begin like its container), each naming the check that holds the video. `run` is the key of the run the layer belongs to (a `RunSummary.key`), so a viewer can mark that run's recordings as stored-then-unavailable beside its test results.\n\nThis route never reads a blob: it returns what a publish token's `POST` found earlier. A layer that was never read back is absent, which means \"not verified\", not \"fine\"; the result is kept in the server's memory only, so a restart empties it. Only layers a store still holds are listed (one removed by garbage collection drops out). No status, slot or winner depends on it. It checks stored bytes, not browser playback. Narrow with `?run=` (a run key) and `?layer=` (an id prefix of at least 7 hex digits, repeatable).\n\nRole needed: `read`.",
|
|
4813
|
+
"x-atlas-role": "read",
|
|
4814
|
+
"security": [
|
|
4815
|
+
{
|
|
4816
|
+
"bearer": []
|
|
4817
|
+
},
|
|
4818
|
+
{
|
|
4819
|
+
"basic": []
|
|
4820
|
+
}
|
|
4821
|
+
],
|
|
4822
|
+
"parameters": [
|
|
4823
|
+
{
|
|
4824
|
+
"required": false,
|
|
4825
|
+
"name": "run",
|
|
4826
|
+
"in": "query",
|
|
4827
|
+
"description": "Only layers of this run (a key from `GET /api/reports/runs`).",
|
|
4828
|
+
"schema": {
|
|
4829
|
+
"type": "string"
|
|
4830
|
+
}
|
|
4831
|
+
},
|
|
4832
|
+
{
|
|
4833
|
+
"required": false,
|
|
4834
|
+
"name": "layer",
|
|
4835
|
+
"in": "query",
|
|
4836
|
+
"description": "Only layers whose id starts with this prefix. Repeatable or comma-separated.",
|
|
4837
|
+
"schema": {
|
|
4838
|
+
"type": "string"
|
|
4839
|
+
}
|
|
4840
|
+
},
|
|
4841
|
+
{
|
|
4842
|
+
"required": false,
|
|
4843
|
+
"name": "project",
|
|
4844
|
+
"in": "query",
|
|
4845
|
+
"description": "Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.",
|
|
4846
|
+
"example": "todo-app",
|
|
4847
|
+
"schema": {
|
|
4848
|
+
"type": "string",
|
|
4849
|
+
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",
|
|
4850
|
+
"maxLength": 64
|
|
4851
|
+
}
|
|
4852
|
+
}
|
|
4853
|
+
],
|
|
4854
|
+
"responses": {
|
|
4855
|
+
"200": {
|
|
4856
|
+
"description": "The held read-backs.",
|
|
4857
|
+
"content": {
|
|
4858
|
+
"application/json": {
|
|
4859
|
+
"schema": {
|
|
4860
|
+
"$ref": "#/components/schemas/RecordingVerification"
|
|
4861
|
+
}
|
|
4862
|
+
}
|
|
4863
|
+
}
|
|
4864
|
+
},
|
|
4865
|
+
"401": {
|
|
4866
|
+
"description": "No token, or an unknown one. Send `Authorization: Bearer <token>`.",
|
|
4867
|
+
"content": {
|
|
4868
|
+
"application/problem+json": {
|
|
4869
|
+
"schema": {
|
|
4870
|
+
"$ref": "#/components/schemas/Problem"
|
|
4871
|
+
}
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
}
|
|
4875
|
+
}
|
|
4876
|
+
},
|
|
4877
|
+
"post": {
|
|
4878
|
+
"operationId": "verifyRecordings",
|
|
4879
|
+
"tags": [
|
|
4880
|
+
"Report"
|
|
4881
|
+
],
|
|
4882
|
+
"summary": "Read stored recordings back and check them (publish token)",
|
|
4883
|
+
"description": "Reads the videos of the newest layers (`limit`, default 20, at most 200; or the layers `layers` names) back from storage and checks that each is still stored, still has the size and SHA-256 published, and still begins like its media type's container. This reads blobs, so it needs a publish (or admin) token in a Bearer header; a reader-only token, a browser sign-in box and an anonymous reader are refused. One read-back runs at a time (`409` otherwise). It writes nothing to storage and changes no status. The answer is the same document `GET` returns, with `verified`: how many layers this pass read back.\n\nRole needed: `publish`.",
|
|
4884
|
+
"x-atlas-role": "publish",
|
|
4885
|
+
"security": [
|
|
4886
|
+
{
|
|
4887
|
+
"bearer": []
|
|
4888
|
+
}
|
|
4889
|
+
],
|
|
4890
|
+
"parameters": [
|
|
4891
|
+
{
|
|
4892
|
+
"required": false,
|
|
4893
|
+
"name": "project",
|
|
4894
|
+
"in": "query",
|
|
4895
|
+
"description": "Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.",
|
|
4896
|
+
"example": "todo-app",
|
|
4897
|
+
"schema": {
|
|
4898
|
+
"type": "string",
|
|
4899
|
+
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",
|
|
4900
|
+
"maxLength": 64
|
|
4901
|
+
}
|
|
4902
|
+
}
|
|
4903
|
+
],
|
|
4904
|
+
"requestBody": {
|
|
4905
|
+
"required": false,
|
|
4906
|
+
"description": "Optional `layers` (id prefixes) and `limit`. An empty body reads back the newest layers.",
|
|
4907
|
+
"content": {
|
|
4908
|
+
"application/json": {
|
|
4909
|
+
"schema": {
|
|
4910
|
+
"$ref": "#/components/schemas/RecordingVerifyRequest"
|
|
4911
|
+
}
|
|
4912
|
+
}
|
|
4913
|
+
}
|
|
4914
|
+
},
|
|
4915
|
+
"responses": {
|
|
4916
|
+
"200": {
|
|
4917
|
+
"description": "The read-backs now held.",
|
|
4918
|
+
"content": {
|
|
4919
|
+
"application/json": {
|
|
4920
|
+
"schema": {
|
|
4921
|
+
"$ref": "#/components/schemas/RecordingVerification"
|
|
4922
|
+
}
|
|
4923
|
+
}
|
|
4924
|
+
}
|
|
4925
|
+
},
|
|
4926
|
+
"401": {
|
|
4927
|
+
"description": "No token, or an unknown one. Send `Authorization: Bearer <token>`.",
|
|
4928
|
+
"content": {
|
|
4929
|
+
"application/problem+json": {
|
|
4930
|
+
"schema": {
|
|
4931
|
+
"$ref": "#/components/schemas/Problem"
|
|
4932
|
+
}
|
|
4933
|
+
}
|
|
4934
|
+
}
|
|
4935
|
+
},
|
|
4936
|
+
"403": {
|
|
4937
|
+
"description": "The token is known but its role may not do this.",
|
|
4938
|
+
"content": {
|
|
4939
|
+
"application/problem+json": {
|
|
4940
|
+
"schema": {
|
|
4941
|
+
"$ref": "#/components/schemas/Problem"
|
|
4942
|
+
}
|
|
4943
|
+
}
|
|
4944
|
+
}
|
|
4945
|
+
},
|
|
4946
|
+
"409": {
|
|
4947
|
+
"description": "A read-back is already running.",
|
|
4948
|
+
"content": {
|
|
4949
|
+
"application/problem+json": {
|
|
4950
|
+
"schema": {
|
|
4951
|
+
"$ref": "#/components/schemas/Problem"
|
|
4952
|
+
}
|
|
4953
|
+
}
|
|
4954
|
+
}
|
|
4955
|
+
},
|
|
4956
|
+
"413": {
|
|
4957
|
+
"description": "Request too large.",
|
|
4958
|
+
"content": {
|
|
4959
|
+
"application/problem+json": {
|
|
4960
|
+
"schema": {
|
|
4961
|
+
"$ref": "#/components/schemas/Problem"
|
|
4962
|
+
}
|
|
4963
|
+
}
|
|
4964
|
+
}
|
|
4965
|
+
},
|
|
4966
|
+
"422": {
|
|
4967
|
+
"description": "Not a verification request.",
|
|
4968
|
+
"content": {
|
|
4969
|
+
"application/problem+json": {
|
|
4970
|
+
"schema": {
|
|
4971
|
+
"$ref": "#/components/schemas/Problem"
|
|
4972
|
+
}
|
|
4973
|
+
}
|
|
4974
|
+
}
|
|
4975
|
+
},
|
|
4976
|
+
"502": {
|
|
4977
|
+
"description": "A store could not be read.",
|
|
4978
|
+
"content": {
|
|
4979
|
+
"application/problem+json": {
|
|
4980
|
+
"schema": {
|
|
4981
|
+
"$ref": "#/components/schemas/Problem"
|
|
4982
|
+
}
|
|
4983
|
+
}
|
|
4984
|
+
}
|
|
4985
|
+
},
|
|
4986
|
+
"503": {
|
|
4987
|
+
"description": "The server is still reading its backends. Retry after the number of seconds in `Retry-After`.",
|
|
4988
|
+
"content": {
|
|
4989
|
+
"application/problem+json": {
|
|
4990
|
+
"schema": {
|
|
4991
|
+
"$ref": "#/components/schemas/Problem"
|
|
4992
|
+
}
|
|
4993
|
+
}
|
|
4994
|
+
}
|
|
4995
|
+
}
|
|
4996
|
+
}
|
|
4997
|
+
}
|
|
4998
|
+
},
|
|
4999
|
+
"/api/projects/{project}/reports/recordings/verification": {
|
|
5000
|
+
"get": {
|
|
5001
|
+
"operationId": "getRecordingVerificationForProject",
|
|
5002
|
+
"tags": [
|
|
5003
|
+
"Report"
|
|
5004
|
+
],
|
|
5005
|
+
"summary": "Which stored recordings the server last read back, and which were gone or damaged",
|
|
5006
|
+
"description": "The newest read-back the server did of stored videos, per layer: how many it checked, how many passed and the `gaps` (a blob that is gone, bytes of another size or hash than were published, or a file that does not begin like its container), each naming the check that holds the video. `run` is the key of the run the layer belongs to (a `RunSummary.key`), so a viewer can mark that run's recordings as stored-then-unavailable beside its test results.\n\nThis route never reads a blob: it returns what a publish token's `POST` found earlier. A layer that was never read back is absent, which means \"not verified\", not \"fine\"; the result is kept in the server's memory only, so a restart empties it. Only layers a store still holds are listed (one removed by garbage collection drops out). No status, slot or winner depends on it. It checks stored bytes, not browser playback. Narrow with `?run=` (a run key) and `?layer=` (an id prefix of at least 7 hex digits, repeatable).\n\nScoped to one project. The same project may be selected on the unprefixed API using ?project= or X-Atlas-Project. Unknown projects return 404 on reads and are created by an authenticated upload.\n\nRole needed: `read`.",
|
|
5007
|
+
"x-atlas-role": "read",
|
|
5008
|
+
"security": [
|
|
5009
|
+
{
|
|
5010
|
+
"bearer": []
|
|
5011
|
+
},
|
|
5012
|
+
{
|
|
5013
|
+
"basic": []
|
|
5014
|
+
}
|
|
5015
|
+
],
|
|
5016
|
+
"parameters": [
|
|
5017
|
+
{
|
|
5018
|
+
"required": true,
|
|
5019
|
+
"name": "project",
|
|
5020
|
+
"in": "path",
|
|
5021
|
+
"description": "Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.",
|
|
5022
|
+
"example": "todo-app",
|
|
5023
|
+
"schema": {
|
|
5024
|
+
"type": "string",
|
|
5025
|
+
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",
|
|
5026
|
+
"maxLength": 64
|
|
5027
|
+
}
|
|
5028
|
+
},
|
|
5029
|
+
{
|
|
5030
|
+
"required": false,
|
|
5031
|
+
"name": "run",
|
|
5032
|
+
"in": "query",
|
|
5033
|
+
"description": "Only layers of this run (a key from `GET /api/reports/runs`).",
|
|
5034
|
+
"schema": {
|
|
5035
|
+
"type": "string"
|
|
5036
|
+
}
|
|
5037
|
+
},
|
|
5038
|
+
{
|
|
5039
|
+
"required": false,
|
|
5040
|
+
"name": "layer",
|
|
5041
|
+
"in": "query",
|
|
5042
|
+
"description": "Only layers whose id starts with this prefix. Repeatable or comma-separated.",
|
|
5043
|
+
"schema": {
|
|
5044
|
+
"type": "string"
|
|
5045
|
+
}
|
|
5046
|
+
}
|
|
5047
|
+
],
|
|
5048
|
+
"responses": {
|
|
5049
|
+
"200": {
|
|
5050
|
+
"description": "The held read-backs.",
|
|
5051
|
+
"content": {
|
|
5052
|
+
"application/json": {
|
|
5053
|
+
"schema": {
|
|
5054
|
+
"$ref": "#/components/schemas/RecordingVerification"
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
}
|
|
5058
|
+
},
|
|
5059
|
+
"401": {
|
|
5060
|
+
"description": "No token, or an unknown one. Send `Authorization: Bearer <token>`.",
|
|
5061
|
+
"content": {
|
|
5062
|
+
"application/problem+json": {
|
|
5063
|
+
"schema": {
|
|
5064
|
+
"$ref": "#/components/schemas/Problem"
|
|
5065
|
+
}
|
|
5066
|
+
}
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
}
|
|
5070
|
+
},
|
|
5071
|
+
"post": {
|
|
5072
|
+
"operationId": "verifyRecordingsForProject",
|
|
5073
|
+
"tags": [
|
|
5074
|
+
"Report"
|
|
5075
|
+
],
|
|
5076
|
+
"summary": "Read stored recordings back and check them (publish token)",
|
|
5077
|
+
"description": "Reads the videos of the newest layers (`limit`, default 20, at most 200; or the layers `layers` names) back from storage and checks that each is still stored, still has the size and SHA-256 published, and still begins like its media type's container. This reads blobs, so it needs a publish (or admin) token in a Bearer header; a reader-only token, a browser sign-in box and an anonymous reader are refused. One read-back runs at a time (`409` otherwise). It writes nothing to storage and changes no status. The answer is the same document `GET` returns, with `verified`: how many layers this pass read back.\n\nScoped to one project. The same project may be selected on the unprefixed API using ?project= or X-Atlas-Project. Unknown projects return 404 on reads and are created by an authenticated upload.\n\nRole needed: `publish`.",
|
|
5078
|
+
"x-atlas-role": "publish",
|
|
5079
|
+
"security": [
|
|
5080
|
+
{
|
|
5081
|
+
"bearer": []
|
|
5082
|
+
}
|
|
5083
|
+
],
|
|
5084
|
+
"parameters": [
|
|
5085
|
+
{
|
|
5086
|
+
"required": true,
|
|
5087
|
+
"name": "project",
|
|
5088
|
+
"in": "path",
|
|
5089
|
+
"description": "Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.",
|
|
5090
|
+
"example": "todo-app",
|
|
5091
|
+
"schema": {
|
|
5092
|
+
"type": "string",
|
|
5093
|
+
"pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",
|
|
5094
|
+
"maxLength": 64
|
|
5095
|
+
}
|
|
5096
|
+
}
|
|
5097
|
+
],
|
|
5098
|
+
"requestBody": {
|
|
5099
|
+
"required": false,
|
|
5100
|
+
"description": "Optional `layers` (id prefixes) and `limit`. An empty body reads back the newest layers.",
|
|
5101
|
+
"content": {
|
|
5102
|
+
"application/json": {
|
|
5103
|
+
"schema": {
|
|
5104
|
+
"$ref": "#/components/schemas/RecordingVerifyRequest"
|
|
5105
|
+
}
|
|
5106
|
+
}
|
|
5107
|
+
}
|
|
5108
|
+
},
|
|
5109
|
+
"responses": {
|
|
5110
|
+
"200": {
|
|
5111
|
+
"description": "The read-backs now held.",
|
|
5112
|
+
"content": {
|
|
5113
|
+
"application/json": {
|
|
5114
|
+
"schema": {
|
|
5115
|
+
"$ref": "#/components/schemas/RecordingVerification"
|
|
5116
|
+
}
|
|
5117
|
+
}
|
|
5118
|
+
}
|
|
5119
|
+
},
|
|
5120
|
+
"401": {
|
|
5121
|
+
"description": "No token, or an unknown one. Send `Authorization: Bearer <token>`.",
|
|
5122
|
+
"content": {
|
|
5123
|
+
"application/problem+json": {
|
|
5124
|
+
"schema": {
|
|
5125
|
+
"$ref": "#/components/schemas/Problem"
|
|
5126
|
+
}
|
|
5127
|
+
}
|
|
5128
|
+
}
|
|
5129
|
+
},
|
|
5130
|
+
"403": {
|
|
5131
|
+
"description": "The token is known but its role may not do this.",
|
|
5132
|
+
"content": {
|
|
5133
|
+
"application/problem+json": {
|
|
5134
|
+
"schema": {
|
|
5135
|
+
"$ref": "#/components/schemas/Problem"
|
|
5136
|
+
}
|
|
5137
|
+
}
|
|
5138
|
+
}
|
|
5139
|
+
},
|
|
5140
|
+
"409": {
|
|
5141
|
+
"description": "A read-back is already running.",
|
|
5142
|
+
"content": {
|
|
5143
|
+
"application/problem+json": {
|
|
5144
|
+
"schema": {
|
|
5145
|
+
"$ref": "#/components/schemas/Problem"
|
|
5146
|
+
}
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
5149
|
+
},
|
|
5150
|
+
"413": {
|
|
5151
|
+
"description": "Request too large.",
|
|
5152
|
+
"content": {
|
|
5153
|
+
"application/problem+json": {
|
|
5154
|
+
"schema": {
|
|
5155
|
+
"$ref": "#/components/schemas/Problem"
|
|
5156
|
+
}
|
|
5157
|
+
}
|
|
5158
|
+
}
|
|
5159
|
+
},
|
|
5160
|
+
"422": {
|
|
5161
|
+
"description": "Not a verification request.",
|
|
5162
|
+
"content": {
|
|
5163
|
+
"application/problem+json": {
|
|
5164
|
+
"schema": {
|
|
5165
|
+
"$ref": "#/components/schemas/Problem"
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
5168
|
+
}
|
|
5169
|
+
},
|
|
5170
|
+
"502": {
|
|
5171
|
+
"description": "A store could not be read.",
|
|
5172
|
+
"content": {
|
|
5173
|
+
"application/problem+json": {
|
|
5174
|
+
"schema": {
|
|
5175
|
+
"$ref": "#/components/schemas/Problem"
|
|
5176
|
+
}
|
|
5177
|
+
}
|
|
5178
|
+
}
|
|
5179
|
+
},
|
|
5180
|
+
"503": {
|
|
5181
|
+
"description": "The server is still reading its backends. Retry after the number of seconds in `Retry-After`.",
|
|
5182
|
+
"content": {
|
|
5183
|
+
"application/problem+json": {
|
|
5184
|
+
"schema": {
|
|
5185
|
+
"$ref": "#/components/schemas/Problem"
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5188
|
+
}
|
|
5189
|
+
}
|
|
5190
|
+
}
|
|
5191
|
+
}
|
|
5192
|
+
},
|
|
4805
5193
|
"/api/reports/review-collections": {
|
|
4806
5194
|
"get": {
|
|
4807
5195
|
"operationId": "listReviewCollections",
|
|
@@ -20225,6 +20613,144 @@
|
|
|
20225
20613
|
],
|
|
20226
20614
|
"additionalProperties": {}
|
|
20227
20615
|
},
|
|
20616
|
+
"RecordingVerification": {
|
|
20617
|
+
"type": "object",
|
|
20618
|
+
"properties": {
|
|
20619
|
+
"schemaVersion": {
|
|
20620
|
+
"type": "number",
|
|
20621
|
+
"const": 1
|
|
20622
|
+
},
|
|
20623
|
+
"layers": {
|
|
20624
|
+
"type": "array",
|
|
20625
|
+
"items": {
|
|
20626
|
+
"type": "object",
|
|
20627
|
+
"properties": {
|
|
20628
|
+
"layer": {
|
|
20629
|
+
"type": "string",
|
|
20630
|
+
"description": "The layer id"
|
|
20631
|
+
},
|
|
20632
|
+
"run": {
|
|
20633
|
+
"type": "string",
|
|
20634
|
+
"description": "The key of the run the layer belongs to (a `RunSummary.key`)"
|
|
20635
|
+
},
|
|
20636
|
+
"verifiedAtMs": {
|
|
20637
|
+
"type": "number",
|
|
20638
|
+
"description": "When the server read the videos back, epoch milliseconds"
|
|
20639
|
+
},
|
|
20640
|
+
"checked": {
|
|
20641
|
+
"type": "integer",
|
|
20642
|
+
"minimum": -9007199254740991,
|
|
20643
|
+
"maximum": 9007199254740991,
|
|
20644
|
+
"description": "Videos read back: those that name a bundle key"
|
|
20645
|
+
},
|
|
20646
|
+
"ok": {
|
|
20647
|
+
"type": "integer",
|
|
20648
|
+
"minimum": -9007199254740991,
|
|
20649
|
+
"maximum": 9007199254740991,
|
|
20650
|
+
"description": "Of those, the ones that passed every check"
|
|
20651
|
+
},
|
|
20652
|
+
"referenced": {
|
|
20653
|
+
"type": "integer",
|
|
20654
|
+
"minimum": -9007199254740991,
|
|
20655
|
+
"maximum": 9007199254740991,
|
|
20656
|
+
"description": "Videos given only by address, which Atlas cannot read back and so does not vouch for"
|
|
20657
|
+
},
|
|
20658
|
+
"gaps": {
|
|
20659
|
+
"type": "array",
|
|
20660
|
+
"items": {
|
|
20661
|
+
"type": "object",
|
|
20662
|
+
"properties": {
|
|
20663
|
+
"check": {
|
|
20664
|
+
"description": "The id of the check that holds the video, or null for a video attached to the run or a part",
|
|
20665
|
+
"type": [
|
|
20666
|
+
"string",
|
|
20667
|
+
"null"
|
|
20668
|
+
]
|
|
20669
|
+
},
|
|
20670
|
+
"name": {
|
|
20671
|
+
"type": "string",
|
|
20672
|
+
"description": "The attachment's name"
|
|
20673
|
+
},
|
|
20674
|
+
"bundleKey": {
|
|
20675
|
+
"type": "string",
|
|
20676
|
+
"description": "The key the layer names the file by"
|
|
20677
|
+
},
|
|
20678
|
+
"problem": {
|
|
20679
|
+
"type": "string",
|
|
20680
|
+
"description": "`not-in-bundle`, `blob-missing`, `unreadable`, `size-mismatch`, `checksum-mismatch` or `undecodable`. An open vocabulary: treat an unknown value as a gap"
|
|
20681
|
+
},
|
|
20682
|
+
"reason": {
|
|
20683
|
+
"type": "string",
|
|
20684
|
+
"description": "A short explanation without secrets or paths"
|
|
20685
|
+
}
|
|
20686
|
+
},
|
|
20687
|
+
"required": [
|
|
20688
|
+
"check",
|
|
20689
|
+
"name",
|
|
20690
|
+
"bundleKey",
|
|
20691
|
+
"problem",
|
|
20692
|
+
"reason"
|
|
20693
|
+
],
|
|
20694
|
+
"additionalProperties": {}
|
|
20695
|
+
},
|
|
20696
|
+
"description": "The videos that did not pass. Never a test result: statuses are unchanged"
|
|
20697
|
+
}
|
|
20698
|
+
},
|
|
20699
|
+
"required": [
|
|
20700
|
+
"layer",
|
|
20701
|
+
"run",
|
|
20702
|
+
"verifiedAtMs",
|
|
20703
|
+
"checked",
|
|
20704
|
+
"ok",
|
|
20705
|
+
"referenced",
|
|
20706
|
+
"gaps"
|
|
20707
|
+
],
|
|
20708
|
+
"additionalProperties": {}
|
|
20709
|
+
},
|
|
20710
|
+
"description": "Layers the server has read back and still holds, newest first"
|
|
20711
|
+
},
|
|
20712
|
+
"notices": {
|
|
20713
|
+
"type": "array",
|
|
20714
|
+
"items": {
|
|
20715
|
+
"type": "string"
|
|
20716
|
+
},
|
|
20717
|
+
"description": "Layers the last pass could not read at all: their recordings are unknown, not verified"
|
|
20718
|
+
},
|
|
20719
|
+
"verified": {
|
|
20720
|
+
"description": "POST only: how many layers this pass read back",
|
|
20721
|
+
"type": "integer",
|
|
20722
|
+
"minimum": -9007199254740991,
|
|
20723
|
+
"maximum": 9007199254740991
|
|
20724
|
+
}
|
|
20725
|
+
},
|
|
20726
|
+
"required": [
|
|
20727
|
+
"schemaVersion",
|
|
20728
|
+
"layers",
|
|
20729
|
+
"notices"
|
|
20730
|
+
],
|
|
20731
|
+
"additionalProperties": {}
|
|
20732
|
+
},
|
|
20733
|
+
"RecordingVerifyRequest": {
|
|
20734
|
+
"type": "object",
|
|
20735
|
+
"properties": {
|
|
20736
|
+
"layers": {
|
|
20737
|
+
"description": "Only layers whose id starts with one of these (at least 7 hex digits)",
|
|
20738
|
+
"maxItems": 50,
|
|
20739
|
+
"type": "array",
|
|
20740
|
+
"items": {
|
|
20741
|
+
"type": "string",
|
|
20742
|
+
"pattern": "^[0-9a-f]{7,64}$"
|
|
20743
|
+
}
|
|
20744
|
+
},
|
|
20745
|
+
"limit": {
|
|
20746
|
+
"description": "How many of the newest layers to read back. Default 20, at most 200",
|
|
20747
|
+
"type": "integer",
|
|
20748
|
+
"minimum": 1,
|
|
20749
|
+
"maximum": 200
|
|
20750
|
+
}
|
|
20751
|
+
},
|
|
20752
|
+
"additionalProperties": {}
|
|
20753
|
+
},
|
|
20228
20754
|
"TestCatalog": {
|
|
20229
20755
|
"type": "object",
|
|
20230
20756
|
"properties": {
|