|
3 | 3 | import glob |
4 | 4 | import collections |
5 | 5 | import jsonschema |
6 | | -from urllib.parse import urlparse |
7 | | -from urllib.request import urlopen |
| 6 | +from urllib.parse import urlparse, urlsplit, urlunsplit |
| 7 | +from urllib.request import urlopen, url2pathname |
8 | 8 | import yaml |
9 | 9 | import yaml.resolver |
10 | 10 | from functools import reduce, lru_cache |
|
13 | 13 | import re |
14 | 14 |
|
15 | 15 | OPENAPI_SCHEMA_URL = 'https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/schemas/v3.0/schema.json' |
| 16 | +URI_SCHEMES = ['file', 'http', 'https'] |
16 | 17 |
|
17 | 18 |
|
18 | 19 | class AlertLogicOpenApiValidationException(Exception): |
@@ -62,7 +63,6 @@ class OpenAPIKeyWord: |
62 | 63 | COMPONENTS = "components" |
63 | 64 | SCHEMAS = "schemas" |
64 | 65 | PROPERTIES = "properties" |
65 | | - REQUIRED = "required" |
66 | 66 | CONTENT = "content" |
67 | 67 | DEFAULT = "default" |
68 | 68 | ENCODING = "encoding" |
@@ -127,25 +127,24 @@ def load_service_spec(service_name, apis_dir=None, version=None): |
127 | 127 | version = version > new_version and version or new_version |
128 | 128 | else: |
129 | 129 | version = version[:1] != "v" and version or version[1:] |
130 | | - service_spec_file = f"file:///{pjoin(service_api_dir, service_name)}.v{version}.yaml" |
131 | | - return load_spec(service_spec_file) |
| 130 | + service_spec_file_path = f"{pjoin(service_api_dir, service_name)}.v{version}.yaml" |
| 131 | + return load_spec(service_spec_file_path) |
132 | 132 |
|
133 | 133 |
|
134 | | -def load_spec(uri): |
| 134 | +def load_spec(uri_or_path): |
135 | 135 | """Loads spec out of RFC3986 URI, resolves refs, normalizes""" |
136 | | - return normalize_spec(get_spec(uri), uri) |
| 136 | + uri_or_path = __normalize_uri(uri_or_path) |
| 137 | + return normalize_spec(uri_or_path, get_spec(uri_or_path)) |
137 | 138 |
|
138 | 139 |
|
139 | | -def normalize_spec(spec, uri): |
| 140 | +def normalize_spec(uri_or_path, spec): |
140 | 141 | """Resolves refs, normalizes""" |
141 | | - return __normalize_spec(__resolve_refs(uri, spec)) |
| 142 | + uri_or_path = __normalize_uri(uri_or_path) |
| 143 | + return __normalize_spec(__resolve_refs(__base_uri(uri_or_path), spec)) |
142 | 144 |
|
143 | 145 |
|
144 | 146 | def get_spec(uri): |
145 | 147 | """Loads spec out of RFC3986 URI, yaml's Reader detects encoding automatically""" |
146 | | - parsed = urlparse(uri) |
147 | | - if not parsed.scheme: |
148 | | - uri = f"file://{uri}" |
149 | 148 | with urlopen(uri) as stream: |
150 | 149 | try: |
151 | 150 | return yaml.load(stream, _YamlOrderedLoader) |
@@ -210,22 +209,45 @@ def al_specific_validations(spec): |
210 | 209 | checks = [validate_operation_ids(spec), validate_path_parameters(spec)] |
211 | 210 | all(checks) |
212 | 211 |
|
213 | | - obj = normalize_spec(spec, uri) |
| 212 | + obj = normalize_spec(uri, spec) |
214 | 213 | jsonschema.validate(obj, schema) |
215 | 214 | return al_specific_validations(spec) |
216 | 215 |
|
217 | 216 |
|
| 217 | +def make_file_uri(path): |
| 218 | + return make_uri('file', '', path, '', '') |
| 219 | + |
| 220 | + |
| 221 | +def make_uri(scheme, netloc, url, query, fragment): |
| 222 | + return urlunsplit((scheme, netloc, url, query, fragment)) |
| 223 | + |
| 224 | + |
218 | 225 | # Private functions |
219 | 226 |
|
| 227 | +def __normalize_uri(uri_or_path): |
| 228 | + parsed = urlparse(uri_or_path) |
| 229 | + if parsed.scheme not in URI_SCHEMES: |
| 230 | + return make_file_uri(uri_or_path) |
| 231 | + else: |
| 232 | + return uri_or_path |
| 233 | + |
| 234 | + |
| 235 | +def __base_uri(uri): |
| 236 | + (scheme, netloc, path, query, fragment) = urlsplit(uri) |
| 237 | + path = os.path.dirname(url2pathname(path)) + '/' |
| 238 | + return urlunsplit((scheme, netloc, path, query, fragment)) |
| 239 | + |
| 240 | + |
220 | 241 | def __list_flatten(l): |
221 | 242 | return [item for sublist in l for item in sublist] |
222 | 243 |
|
223 | 244 |
|
224 | | -def __resolve_refs(file_uri, spec): |
| 245 | +def __resolve_refs(file_base_uri, spec): |
225 | 246 | def spec_ref_handler(uri): |
226 | 247 | return __resolve_refs(uri, get_spec(uri)) |
| 248 | + |
227 | 249 | handlers = {'': spec_ref_handler, 'file': spec_ref_handler, 'http': spec_ref_handler, 'https': spec_ref_handler} |
228 | | - resolver = jsonschema.RefResolver(file_uri, spec, handlers=handlers) |
| 250 | + resolver = jsonschema.RefResolver(file_base_uri, spec, handlers=handlers) |
229 | 251 |
|
230 | 252 | def _do_resolve(node): |
231 | 253 | if isinstance(node, collections.abc.Mapping) and OpenAPIKeyWord.REF in node: |
|
0 commit comments