Skip to content

Commit 8d700de

Browse files
committed
Updated schemas as per new spec sheet
1 parent d8eada2 commit 8d700de

7 files changed

Lines changed: 92 additions & 67 deletions

File tree

mod_api/schemas/auth.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
1-
"""Request/response schemas for token endpoints."""
1+
"""Request/response schemas for the token endpoints."""
22

33
from marshmallow import RAISE, Schema, fields, validate
44

55
from mod_api.models.api_token import VALID_SCOPES
66

77

88
class TokenCreateRequestSchema(Schema):
9-
"""Schema for POST /auth/tokens request body."""
9+
"""Validates POST /auth/tokens bodies."""
1010

1111
email = fields.Email(required=True)
1212
password = fields.String(
1313
required=True,
14-
validate=validate.Length(min=5, max=128),
14+
validate=validate.Length(min=8, max=128),
1515
)
1616
token_name = fields.String(
1717
required=True,
1818
validate=[
19-
2019
validate.Length(min=1, max=50),
2120
validate.Regexp(
2221
r'^[a-zA-Z0-9_\-]+$',
@@ -25,8 +24,8 @@ class TokenCreateRequestSchema(Schema):
2524
],
2625
)
2726
expires_in_days = fields.Integer(
28-
load_default=30,
29-
validate=validate.Range(min=1, max=90),
27+
load_default=7,
28+
validate=validate.Range(min=1, max=30),
3029
)
3130
scopes = fields.List(
3231
fields.String(validate=validate.OneOf(VALID_SCOPES)),
@@ -35,14 +34,31 @@ class TokenCreateRequestSchema(Schema):
3534
)
3635

3736
class Meta:
38-
unknown = RAISE # Reject unknown fields
37+
unknown = RAISE
3938

4039

4140
class AuthTokenSchema(Schema):
42-
"""Schema for serializing the created token response."""
41+
"""The one-time response returned when a token is created."""
4342

4443
token = fields.String(required=True)
4544
token_type = fields.String(dump_default='bearer')
4645
token_name = fields.String(required=True)
4746
scopes = fields.List(fields.String(), required=True)
4847
expires_at = fields.DateTime(required=True)
48+
49+
50+
class ApiTokenItemSchema(Schema):
51+
"""Token metadata for list responses — never includes the plaintext value."""
52+
53+
id = fields.Integer(required=True)
54+
user_id = fields.Integer(required=True)
55+
token_name = fields.String(required=True)
56+
token_prefix = fields.String(required=True)
57+
scopes = fields.Method('get_scopes')
58+
created_at = fields.DateTime(required=True)
59+
expires_at = fields.DateTime(required=True)
60+
is_revoked = fields.Boolean(required=True)
61+
revoked_at = fields.DateTime(allow_none=True)
62+
63+
def get_scopes(self, obj):
64+
return obj.scopes

mod_api/schemas/common.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
"""Common schemas: ErrorResponse, pagination wrappers."""
1+
"""Shared schemas: ErrorResponse and pagination wrappers."""
22

33
from marshmallow import Schema, fields
44

55

66
class ErrorResponseSchema(Schema):
7-
"""Standard error response body."""
7+
"""Standard JSON error body returned by all error responses."""
88
code = fields.String(required=True)
99
message = fields.String(required=True)
1010
details = fields.Dict(keys=fields.String(), required=True, load_default={})

mod_api/schemas/errors.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
"""Schemas for error items, error summaries, and log lines."""
1+
"""Schemas for error items, error summary buckets, and log lines."""
22

33
from marshmallow import Schema, fields, validate
44

55

66
class ErrorItemSchema(Schema):
7-
"""A single error derived from run results."""
7+
"""A single error derived from run results or infra progress."""
88
error_id = fields.String(required=True)
99
run_id = fields.Integer(required=True)
1010
sample_id = fields.Integer(allow_none=True)

mod_api/schemas/results.py

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@
44

55

66
class OutputFileContentSchema(Schema):
7-
"""File content blob (expected or actual output)."""
7+
"""File content blob returned for expected or actual output."""
8+
run_id = fields.Integer(allow_none=True)
9+
sample_id = fields.Integer(required=True)
10+
regression_id = fields.Integer(required=True)
11+
output_id = fields.Integer(required=True)
812
filename = fields.String(required=True)
9-
content = fields.String(required=True)
13+
content_type = fields.String(required=True)
1014
encoding = fields.String(required=True, validate=validate.OneOf(['utf-8', 'base64']))
15+
content = fields.String(required=True)
1116
sha256 = fields.String(allow_none=True)
1217
storage_status = fields.String(
1318
required=True,
@@ -17,49 +22,61 @@ class OutputFileContentSchema(Schema):
1722

1823
class DiffHunkLineSchema(Schema):
1924
"""One line inside a diff hunk."""
20-
type = fields.String(required=True, validate=validate.OneOf(['add', 'delete', 'context']))
21-
content = fields.String(required=True)
25+
kind = fields.String(required=True, validate=validate.OneOf(['context', 'added', 'removed']))
26+
expected_line = fields.Integer(allow_none=True)
27+
actual_line = fields.Integer(allow_none=True)
28+
text = fields.String(required=True)
2229

2330

2431
class DiffHunkSchema(Schema):
25-
"""A contiguous block of changes in a diff."""
26-
header = fields.String(required=True)
32+
"""A contiguous block of changes."""
33+
expected_start = fields.Integer(required=True)
34+
actual_start = fields.Integer(required=True)
2735
lines = fields.List(fields.Nested(DiffHunkLineSchema), required=True)
2836

2937

3038
class DiffSchema(Schema):
3139
"""Structured diff between expected and actual output."""
40+
run_id = fields.Integer(required=True)
41+
sample_id = fields.Integer(required=True)
42+
regression_id = fields.Integer(required=True)
43+
output_id = fields.Integer(required=True)
3244
status = fields.String(required=True, validate=validate.OneOf([
3345
'identical', 'different', 'missing_actual', 'missing_expected',
3446
]))
35-
expected_sha256 = fields.String(allow_none=True)
36-
actual_sha256 = fields.String(allow_none=True)
37-
stats = fields.Dict(required=True)
47+
summary = fields.Dict(required=True)
3848
hunks = fields.List(fields.Nested(DiffHunkSchema), required=True)
3949

4050

4151
class BaselineApprovalRequestSchema(Schema):
4252
"""POST /runs/{id}/samples/{sid}/baseline-approval body."""
43-
reason = fields.String(
53+
regression_id = fields.Integer(
4454
required=True,
45-
validate=validate.Length(min=10, max=1000),
55+
validate=validate.Range(min=1),
4656
)
4757
output_id = fields.Integer(
48-
load_default=None,
58+
required=True,
4959
validate=validate.Range(min=1),
5060
)
51-
apply_to_variants = fields.Boolean(load_default=False)
61+
reason = fields.String(
62+
required=True,
63+
validate=validate.Length(min=10, max=500),
64+
)
65+
remove_variants = fields.Boolean(
66+
load_default=False,
67+
)
5268

5369
class Meta:
5470
unknown = RAISE
5571

5672

5773
class BaselineApprovalSchema(Schema):
58-
"""Response after submitting a baseline approval request."""
74+
"""Response after a baseline approval is applied."""
5975
approval_id = fields.String(required=True)
60-
status = fields.String(required=True, validate=validate.OneOf([
61-
'pending_review', 'approved', 'rejected',
62-
]))
76+
status = fields.String(required=True, validate=validate.OneOf(['approved']))
77+
run_id = fields.Integer(required=True)
78+
sample_id = fields.Integer(required=True)
79+
regression_id = fields.Integer(required=True)
80+
output_id = fields.Integer(required=True)
6381
requested_by = fields.String(required=True)
64-
reason = fields.String(required=True)
6582
created_at = fields.DateTime(required=True)

mod_api/schemas/runs.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Schemas for runs, run summaries, progress events, and run actions."""
1+
"""Schemas for runs, summaries, progress events, and run actions."""
22

33
from marshmallow import RAISE, Schema, fields, validate
44

@@ -8,41 +8,44 @@ class ProgressEventSchema(Schema):
88
timestamp = fields.DateTime(required=True)
99
status = fields.String(required=True)
1010
message = fields.String(required=True)
11+
step = fields.Integer(allow_none=True)
1112

1213

1314
class RunSchema(Schema):
14-
"""Full run representation."""
15+
"""Full run details."""
1516
run_id = fields.Integer(required=True)
1617
status = fields.String(required=True, validate=validate.OneOf([
1718
'queued', 'running', 'pass', 'fail', 'canceled', 'error', 'incomplete',
1819
]))
1920
platform = fields.String(required=True, validate=validate.OneOf(['linux', 'windows']))
20-
test_type = fields.String(required=True, validate=validate.OneOf(['commit', 'pr']))
21+
test_type = fields.String(validate=validate.OneOf(['commit', 'pr']))
2122
repository = fields.String(required=True)
22-
branch = fields.String(required=True)
23+
branch = fields.String(allow_none=True)
2324
commit_sha = fields.String(required=True)
2425
pr_number = fields.Integer(allow_none=True, load_default=None)
25-
created_at = fields.DateTime(allow_none=True)
26+
created_at = fields.DateTime(required=True)
2627
queued_at = fields.DateTime(allow_none=True)
2728
started_at = fields.DateTime(allow_none=True)
2829
completed_at = fields.DateTime(allow_none=True)
2930
github_link = fields.String(allow_none=True)
3031

3132

3233
class RunSummarySchema(Schema):
33-
"""Aggregated pass/fail counts for a run."""
34+
"""Pass/fail/skip aggregate counts for a run."""
3435
run_id = fields.Integer(required=True)
3536
status = fields.String(required=True)
3637
total_samples = fields.Integer(required=True)
3738
pass_count = fields.Integer(required=True)
3839
fail_count = fields.Integer(required=True)
39-
skip_count = fields.Integer(required=True)
40+
skipped_count = fields.Integer(required=True)
4041
missing_output_count = fields.Integer(required=True)
41-
runtime_ms = fields.Integer(allow_none=True)
42+
error_count = fields.Integer(load_default=0)
43+
duration_ms = fields.Integer(allow_none=True)
44+
triggered_by = fields.String(allow_none=True)
4245

4346

4447
class RunConfigSchema(Schema):
45-
"""The configuration used to launch a run."""
48+
"""The test matrix and configuration for a run."""
4649
run_id = fields.Integer(required=True)
4750
platform = fields.String(required=True)
4851
branch = fields.String(required=True)
@@ -83,6 +86,11 @@ class RunCreateRequestSchema(Schema):
8386
),
8487
],
8588
)
89+
pull_request = fields.Integer(
90+
load_default=None,
91+
allow_none=True,
92+
validate=validate.Range(min=1),
93+
)
8694
regression_test_ids = fields.List(
8795
fields.Integer(validate=validate.Range(min=1)),
8896
load_default=None,
@@ -94,7 +102,7 @@ class Meta:
94102

95103

96104
class RunActionResultSchema(Schema):
97-
"""Response for cancel/retry actions."""
105+
"""Response for cancel and similar run actions."""
98106
run_id = fields.Integer(required=True)
99107
new_run_id = fields.Integer(allow_none=True)
100108
action = fields.String(required=True)

mod_api/schemas/samples.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Schemas for samples, run sample results, history entries, and regression tests."""
1+
"""Schemas for samples, per-run sample results, history, and regression tests."""
22

33
from marshmallow import Schema, fields, validate
44

@@ -13,7 +13,7 @@ class OutputFileSchema(Schema):
1313

1414

1515
class RunSampleSchema(Schema):
16-
"""A sample's result within a specific run."""
16+
"""A regression test's result within a specific run."""
1717
regression_test_id = fields.Integer(required=True)
1818
sample_id = fields.Integer(allow_none=True)
1919
sample_name = fields.String(allow_none=True)
@@ -29,7 +29,7 @@ class RunSampleSchema(Schema):
2929

3030

3131
class SampleSchema(Schema):
32-
"""A media sample from the sample catalog."""
32+
"""A media sample from the catalog."""
3333
sample_id = fields.Integer(required=True)
3434
sha = fields.String(required=True)
3535
extension = fields.String(required=True)

mod_api/schemas/system.py

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
"""System schemas for health, queue, branches, environments, and artifacts."""
1+
"""Schemas for health checks, queue jobs, and run artifacts."""
22

33
from marshmallow import Schema, fields, validate
44

55

66
class DependencyHealthSchema(Schema):
7-
"""Schema for a single system dependency status."""
7+
"""Status of a single system dependency (DB, GCS, local storage)."""
88
name = fields.String(required=True)
99
status = fields.String(required=True, validate=validate.OneOf(['ok', 'degraded', 'down']))
1010
message = fields.String(allow_none=True)
1111

1212

1313
class SystemHealthSchema(Schema):
14-
"""Schema for the overall system health response."""
14+
"""Overall system health response."""
1515
status = fields.String(
1616
required=True,
1717
validate=validate.OneOf(['ok', 'degraded', 'down']),
@@ -21,41 +21,25 @@ class SystemHealthSchema(Schema):
2121

2222

2323
class QueueJobSchema(Schema):
24-
"""Schema for a single job in the queue."""
24+
"""A single queued or running job."""
2525
run_id = fields.Integer(required=True)
2626
status = fields.String(required=True, validate=validate.OneOf(['queued', 'running']))
2727
platform = fields.String(required=True, validate=validate.OneOf(['linux', 'windows']))
28-
queued_at = fields.DateTime(required=True)
28+
queued_at = fields.DateTime(allow_none=True)
2929
started_at = fields.DateTime(allow_none=True)
3030
position = fields.Integer(allow_none=True)
3131

3232

33-
class BranchSchema(Schema):
34-
"""Schema for a tracked branch."""
35-
repository = fields.String(required=True)
36-
name = fields.String(required=True)
37-
head_sha = fields.String(allow_none=True)
38-
active = fields.Boolean(required=True)
39-
40-
41-
class EnvironmentSchema(Schema):
42-
"""Schema for a test environment."""
43-
environment_id = fields.String(required=True)
44-
platform = fields.String(required=True, validate=validate.OneOf(['linux', 'windows']))
45-
active = fields.Boolean(required=True)
46-
runner_label = fields.String(allow_none=True)
47-
average_duration_ms = fields.Integer(allow_none=True)
48-
49-
5033
class ArtifactSchema(Schema):
51-
"""Schema for a run artifact."""
34+
"""A downloadable artifact tied to a run."""
5235
artifact_id = fields.String(required=True)
5336
run_id = fields.Integer(required=True)
5437
sample_id = fields.Integer(allow_none=True)
5538
type = fields.String(
5639
required=True,
5740
validate=validate.OneOf([
58-
'build_log', 'sample_output', 'expected_output', 'diff', 'media_info', 'binary',
41+
'build_log', 'sample_output', 'expected_output', 'actual_output',
42+
'diff', 'media_info', 'binary', 'coredump', 'combined_stdout',
5943
]),
6044
)
6145
filename = fields.String(required=True)

0 commit comments

Comments
 (0)