|
1 | | -"""The MAP 0.2 core artifacts and JSON Schema validation for MailSchema Registry contributions.""" |
| 1 | +"""Mail Action Protocol 0.3 artifacts, byte for byte as mailschema.org publishes them. |
2 | 2 |
|
3 | | -import json |
4 | | -from importlib.resources import files |
5 | | -from typing import Any |
6 | | - |
7 | | -from jsonschema import Draft202012Validator, FormatChecker |
8 | | - |
9 | | -__version__ = "0.2.1" |
10 | | - |
11 | | -MAP_PROFILE = "https://mailschema.org/profiles/map/0.2" |
12 | | -"""The MAP profile whose core artifacts this package carries.""" |
13 | | - |
14 | | - |
15 | | -def _bundled(name: str) -> dict[str, Any]: |
16 | | - return json.loads(files(__package__).joinpath(name).read_text(encoding="utf-8")) |
17 | | - |
18 | | - |
19 | | -def get_map_schema() -> dict[str, Any]: |
20 | | - """Return a fresh copy of the MAP 0.2 core schema, as the profile record binds it by SHA-256.""" |
21 | | - return _bundled("map-0.2.schema.json") |
22 | | - |
23 | | - |
24 | | -def get_map_context() -> dict[str, Any]: |
25 | | - """Return a fresh copy of the MAP 0.2 JSON-LD context.""" |
26 | | - return _bundled("map-0.2.jsonld") |
27 | | - |
28 | | - |
29 | | -def get_contract_format_schema() -> dict[str, Any]: |
30 | | - """Return a fresh copy of the type contract format every MAP 0.2 contract follows.""" |
31 | | - return _bundled("type-contract-0.2.schema.json") |
| 3 | +The profile record, the JSON-LD context, and the core, type contract and implementation record |
| 4 | +schemas. The profile record binds the context and the first two schemas by SHA-256. |
| 5 | +""" |
32 | 6 |
|
| 7 | +from importlib.resources import files |
33 | 8 |
|
34 | | -def get_forms_schema() -> dict[str, Any]: |
35 | | - """Return a fresh copy of the form fields block contracts pin.""" |
36 | | - return _bundled("forms-0.1.schema.json") |
37 | | - |
38 | | - |
39 | | -def get_contribution_schema() -> dict[str, Any]: |
40 | | - """Return a fresh copy of the bundled Registry contribution schema.""" |
41 | | - return _bundled("contribution.schema.json") |
42 | | - |
43 | | - |
44 | | -def get_record_schema() -> dict[str, Any]: |
45 | | - """Return the schema for expanded Registry records.""" |
46 | | - schema = get_contribution_schema() |
47 | | - return {"$schema": schema["$schema"], "$defs": schema["$defs"], "$ref": "#/$defs/record"} |
48 | | - |
49 | | - |
50 | | -_schema = get_contribution_schema() |
51 | | -_validator = Draft202012Validator(_schema, format_checker=FormatChecker()) |
52 | | -_variants = { |
53 | | - variant["properties"]["kind"]["const"]: Draft202012Validator( |
54 | | - {"$schema": _schema["$schema"], "$defs": _schema["$defs"], **variant}, |
55 | | - format_checker=FormatChecker(), |
56 | | - ) |
57 | | - for variant in _schema["oneOf"] |
58 | | -} |
59 | | -_record_validator = Draft202012Validator(get_record_schema(), format_checker=FormatChecker()) |
60 | | - |
61 | | - |
62 | | -def _errors(validator: Draft202012Validator, value: Any) -> list[str]: |
63 | | - result = [] |
64 | | - for error in validator.iter_errors(value): |
65 | | - path = "/" + "/".join(str(part).replace("~", "~0").replace("/", "~1") for part in error.absolute_path) |
66 | | - result.append(f"{path} {error.message}") |
67 | | - return result |
68 | | - |
69 | | - |
70 | | -def contribution_errors(value: Any) -> list[str]: |
71 | | - """Return structural errors; an empty list means the schema checks passed.""" |
72 | | - kind = value.get("kind") if isinstance(value, dict) else None |
73 | | - validator = _variants.get(kind, _validator) if isinstance(kind, str) else _validator |
74 | | - return _errors(validator, value) |
75 | | - |
| 9 | +__version__ = "0.3.0" |
76 | 10 |
|
77 | | -def record_errors(value: Any) -> list[str]: |
78 | | - """Return structural errors for a Registry record.""" |
79 | | - return _errors(_record_validator, value) |
| 11 | +PROFILE = "https://mailschema.org/profiles/map/0.3" |
| 12 | +"""The MAP profile these artifacts define.""" |
80 | 13 |
|
| 14 | +CONTEXT = "https://mailschema.org/contexts/map-0.3.jsonld" |
| 15 | +"""The JSON-LD context every MAP 0.3 description names.""" |
81 | 16 |
|
82 | | -def validate_contribution(value: Any) -> None: |
83 | | - """Raise ValueError if a contribution does not match the schema.""" |
84 | | - errors = contribution_errors(value) |
85 | | - if errors: |
86 | | - raise ValueError("Invalid contribution:\n" + "\n".join(errors)) |
| 17 | +ARTIFACTS = ( |
| 18 | + "profile.json", |
| 19 | + "context.jsonld", |
| 20 | + "core.schema.json", |
| 21 | + "contract.schema.json", |
| 22 | + "implementation.schema.json", |
| 23 | +) |
| 24 | +"""The bundled files, by name.""" |
87 | 25 |
|
88 | 26 |
|
89 | | -def validate_record(value: Any) -> None: |
90 | | - """Raise ValueError if a Registry record does not match the schema.""" |
91 | | - errors = record_errors(value) |
92 | | - if errors: |
93 | | - raise ValueError("Invalid type record:\n" + "\n".join(errors)) |
| 27 | +def artifact(name: str) -> bytes: |
| 28 | + """Return the exact bytes of a bundled artifact.""" |
| 29 | + if name not in ARTIFACTS: |
| 30 | + raise KeyError(f"mailschema: unknown artifact {name!r}") |
| 31 | + return files(__package__).joinpath(name).read_bytes() |
0 commit comments