oapi-profile-builder 3.2.0__tar.gz → 3.2.1__tar.gz
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.
- {oapi_profile_builder-3.2.0/src/oapi_profile_builder.egg-info → oapi_profile_builder-3.2.1}/PKG-INFO +1 -1
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/pyproject.toml +1 -1
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder/server_validation.py +91 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1/src/oapi_profile_builder.egg-info}/PKG-INFO +1 -1
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/LICENSE +0 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/README.md +0 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/setup.cfg +0 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder/__init__.py +0 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder/cite.py +0 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder/cite_features.py +0 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder/cli.py +0 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder/compile.py +0 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder/cover.py +0 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder/generate.py +0 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder/models.py +0 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder.egg-info/SOURCES.txt +0 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder.egg-info/dependency_links.txt +0 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder.egg-info/entry_points.txt +0 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder.egg-info/requires.txt +0 -0
- {oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "oapi-profile-builder"
|
|
7
|
-
version = "3.2.
|
|
7
|
+
version = "3.2.1"
|
|
8
8
|
description = "Authoritative tooling for creating OGC API Service Profiles (EDR, Features)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -39,6 +39,7 @@ import subprocess
|
|
|
39
39
|
import sys
|
|
40
40
|
import tempfile
|
|
41
41
|
from pathlib import Path
|
|
42
|
+
from urllib.parse import urljoin, urlsplit
|
|
42
43
|
|
|
43
44
|
import yaml
|
|
44
45
|
|
|
@@ -60,6 +61,72 @@ VALID_CHECKS = {
|
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
|
|
64
|
+
def _discover_service_desc_path(base_url: str) -> str | None:
|
|
65
|
+
"""
|
|
66
|
+
Fetch the landing page and resolve where the live server actually serves
|
|
67
|
+
its OpenAPI document.
|
|
68
|
+
|
|
69
|
+
OGC API standards do not fix the OpenAPI document path. It is discoverable
|
|
70
|
+
via the landing page link with rel="service-desc" (machine-readable). This
|
|
71
|
+
returns the path relative to ``base_url`` (e.g. "/openapi.json") suitable
|
|
72
|
+
for use as an OpenAPI path template, or None if it cannot be resolved to a
|
|
73
|
+
path on the same host as ``base_url``.
|
|
74
|
+
"""
|
|
75
|
+
try:
|
|
76
|
+
import requests
|
|
77
|
+
except ImportError:
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
# Normalise to a base that urljoin treats as a directory.
|
|
81
|
+
base = base_url if base_url.endswith("/") else base_url + "/"
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
resp = requests.get(
|
|
85
|
+
base,
|
|
86
|
+
headers={"Accept": "application/json"},
|
|
87
|
+
timeout=10,
|
|
88
|
+
)
|
|
89
|
+
resp.raise_for_status()
|
|
90
|
+
landing = resp.json()
|
|
91
|
+
except Exception:
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
if not isinstance(landing, dict):
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
links = landing.get("links")
|
|
98
|
+
if not isinstance(links, list):
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
href = None
|
|
102
|
+
for link in links:
|
|
103
|
+
if isinstance(link, dict) and link.get("rel") == "service-desc" and link.get("href"):
|
|
104
|
+
href = link["href"]
|
|
105
|
+
break
|
|
106
|
+
if not href:
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
# Resolve relative hrefs against the landing page URL.
|
|
110
|
+
absolute = urljoin(base, href)
|
|
111
|
+
|
|
112
|
+
base_parts = urlsplit(base)
|
|
113
|
+
href_parts = urlsplit(absolute)
|
|
114
|
+
|
|
115
|
+
# schemathesis appends the OpenAPI path template to --url, so the spec can
|
|
116
|
+
# only describe resources on the same origin as the validated base URL.
|
|
117
|
+
if (href_parts.scheme, href_parts.netloc) != (base_parts.scheme, base_parts.netloc):
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
# Return the href path relative to the base URL's path.
|
|
121
|
+
base_path = base_parts.path # ends with "/"
|
|
122
|
+
href_path = href_parts.path or "/"
|
|
123
|
+
if href_path.startswith(base_path):
|
|
124
|
+
rel = href_path[len(base_path):]
|
|
125
|
+
else:
|
|
126
|
+
rel = href_path.lstrip("/")
|
|
127
|
+
return "/" + rel
|
|
128
|
+
|
|
129
|
+
|
|
63
130
|
def validate_server(
|
|
64
131
|
spec: dict,
|
|
65
132
|
url: str,
|
|
@@ -88,6 +155,27 @@ def validate_server(
|
|
|
88
155
|
)
|
|
89
156
|
sys.exit(1)
|
|
90
157
|
|
|
158
|
+
# The OpenAPI document path is discoverable, not fixed by OGC API. The spec
|
|
159
|
+
# ships a placeholder "/openapi" path; remap it to wherever the live server
|
|
160
|
+
# actually advertises its service-desc link, or drop it if we cannot resolve
|
|
161
|
+
# a same-origin path (testing a path the standard never mandated would be a
|
|
162
|
+
# false failure).
|
|
163
|
+
drop_openapi_path = False
|
|
164
|
+
paths = spec.get("paths")
|
|
165
|
+
if isinstance(paths, dict) and "/openapi" in paths:
|
|
166
|
+
discovered = _discover_service_desc_path(url)
|
|
167
|
+
if discovered and discovered != "/openapi":
|
|
168
|
+
if discovered in paths:
|
|
169
|
+
# Server serves its spec at a path the profile already describes.
|
|
170
|
+
paths.pop("/openapi")
|
|
171
|
+
else:
|
|
172
|
+
paths[discovered] = paths.pop("/openapi")
|
|
173
|
+
print(f"Discovered service-desc document at: {discovered}")
|
|
174
|
+
elif not discovered:
|
|
175
|
+
drop_openapi_path = True
|
|
176
|
+
print("Could not resolve service-desc from landing page; "
|
|
177
|
+
"excluding /openapi from validation.")
|
|
178
|
+
|
|
91
179
|
with tempfile.NamedTemporaryFile(
|
|
92
180
|
mode="w", suffix=".yaml", delete=False, encoding="utf-8"
|
|
93
181
|
) as tmp:
|
|
@@ -108,6 +196,9 @@ def validate_server(
|
|
|
108
196
|
if exclude_pattern:
|
|
109
197
|
cmd += ["--exclude-path-regex", exclude_pattern.pattern]
|
|
110
198
|
|
|
199
|
+
if drop_openapi_path:
|
|
200
|
+
cmd += ["--exclude-path-regex", r"^/openapi$"]
|
|
201
|
+
|
|
111
202
|
# Exclude POST /execution and job resource paths unless stateful testing is enabled
|
|
112
203
|
if not stateful:
|
|
113
204
|
cmd += ["--exclude-path-regex", r"(/execution$|/jobs/[^/]+)"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder/cite_features.py
RENAMED
|
File without changes
|
|
File without changes
|
{oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder/compile.py
RENAMED
|
File without changes
|
|
File without changes
|
{oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder/generate.py
RENAMED
|
File without changes
|
{oapi_profile_builder-3.2.0 → oapi_profile_builder-3.2.1}/src/oapi_profile_builder/models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|