-
Notifications
You must be signed in to change notification settings - Fork 51
Expand file tree
/
Copy pathbaseinfo.py
More file actions
119 lines (103 loc) · 6.17 KB
/
baseinfo.py
File metadata and controls
119 lines (103 loc) · 6.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# pylint: disable=no-self-argument,no-name-in-module
import re
from typing import Dict, List, Optional, Union
from pydantic import AnyHttpUrl, BaseModel, Field, root_validator, validator
from optimade.models.jsonapi import Link, Resource
from optimade.models.utils import SemanticVersion, StrictField
__all__ = ("AvailableApiVersion", "BaseInfoAttributes", "BaseInfoResource")
class AvailableApiVersion(BaseModel):
"""A JSON object containing information about an available API version"""
url: AnyHttpUrl = StrictField(
...,
description="A string specifying a versioned base URL that MUST adhere to the rules in section Base URL",
pattern=r".+/v[0-1](\.[0-9]+)*/?$",
)
version: SemanticVersion = StrictField(
...,
description="""A string containing the full version number of the API served at that versioned base URL.
The version number string MUST NOT be prefixed by, e.g., 'v'.
Examples: `1.0.0`, `1.0.0-rc.2`.""",
)
@validator("url")
def url_must_be_versioned_base_url(cls, v):
"""The URL must be a valid versioned Base URL"""
if not re.match(r".+/v[0-1](\.[0-9]+)*/?$", v):
raise ValueError(f"url MUST be a versioned base URL. It is: {v}")
return v
@root_validator(pre=False, skip_on_failure=True)
def crosscheck_url_and_version(cls, values):
"""Check that URL version and API version are compatible."""
url_version = (
values["url"]
.split("/")[-2 if values["url"].endswith("/") else -1]
.replace("v", "")
)
# as with version urls, we need to split any release tags or build metadata out of these URLs
url_version = tuple(
int(val) for val in url_version.split("-")[0].split("+")[0].split(".")
)
api_version = tuple(
int(val) for val in values["version"].split("-")[0].split("+")[0].split(".")
)
if any(a != b for a, b in zip(url_version, api_version)):
raise ValueError(
f"API version {api_version} is not compatible with url version {url_version}."
)
return values
class BaseInfoAttributes(BaseModel):
"""Attributes for Base URL Info endpoint"""
api_version: SemanticVersion = StrictField(
...,
description="""Presently used full version of the OPTIMADE API.
The version number string MUST NOT be prefixed by, e.g., "v".
Examples: `1.0.0`, `1.0.0-rc.2`.""",
)
available_api_versions: list[AvailableApiVersion] = StrictField(
...,
description="A list of dictionaries of available API versions at other base URLs",
)
formats: list[str] = StrictField(
default=["json"], description="List of available output formats."
)
available_endpoints: list[str] = StrictField(
...,
description="List of available endpoints (i.e., the string to be appended to the versioned base URL).",
)
entry_types_by_format: dict[str, list[str]] = StrictField(
..., description="Available entry endpoints as a function of output formats."
)
license: Union[Link, AnyHttpUrl] = StrictField(
...,
description="""A [JSON API links object](http://jsonapi.org/format/1.0/#document-links) giving a URL to a web page containing a human-readable text describing the license (or licensing options if there are multiple) covering all the data and metadata provided by this database.
Clients are advised not to try automated parsing of this link or its content, but rather rely on the field `available_licenses` instead.""",
)
available_licenses: Optional[List[str]] = StrictField(
...,
description="""List of [SPDX license identifiers](https://spdx.org/licenses/) specifying a set of alternative licenses under which the client is granted access to all the data and metadata in this database.
If the data and metadata is available under multiple alternative licenses, identifiers of these multiple licenses SHOULD be provided to let clients know under which conditions the data and metadata can be used.
Inclusion of a license identifier in the list is a commitment of the database that the rights are in place to grant clients access to all the data and metadata according to the terms of either of these licenses (at the choice of the client).
If the licensing information provided via the field `license` omits licensing options specified in `available_licenses`, or if it otherwise contradicts them, a client MUST still be allowed to interpret the inclusion of a license in `available_licenses` as a full commitment from the database that the data and metadata is available, without exceptions, under the respective licenses.
If the database cannot make that commitment, e.g., if only part of the data is available under a license, the corresponding license identifier MUST NOT appear in `available_licenses` (but, rather, the field `license` is to be used to clarify the licensing situation.)
An empty list indicates that none of the SPDX licenses apply for the entirety of the database and that the licensing situation is clarified in human readable form in the field `license`.""",
)
is_index: Optional[bool] = StrictField(
default=False,
description="If true, this is an index meta-database base URL (see section Index Meta-Database). "
"If this member is not provided, the client MUST assume this is not an index meta-database base URL "
"(i.e., the default is for `is_index` to be `false`).",
)
@validator("entry_types_by_format", check_fields=False)
def formats_and_endpoints_must_be_valid(cls, v, values):
for format_, endpoints in v.items():
if format_ not in values["formats"]:
raise ValueError(f"'{format_}' must be listed in formats to be valid")
for endpoint in endpoints:
if endpoint not in values["available_endpoints"]:
raise ValueError(
f"'{endpoint}' must be listed in available_endpoints to be valid"
)
return v
class BaseInfoResource(Resource):
id: str = Field("/", regex="^/$")
type: str = Field("info", regex="^info$")
attributes: BaseInfoAttributes = Field(...)