ogcapi-registry 0.1.0__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.
@@ -0,0 +1,280 @@
1
+ Metadata-Version: 2.3
2
+ Name: ogcapi-registry
3
+ Version: 0.1.0
4
+ Summary: Add your description here
5
+ Author: Claude, Francesco Bartoli
6
+ Author-email: Claude <noreply@anthropic.com>, Francesco Bartoli <xbartolone@gmail.com>
7
+ Requires-Dist: httpx>=0.28.1
8
+ Requires-Dist: openapi-pydantic>=0.5.1
9
+ Requires-Dist: pydantic>=2.12.5
10
+ Requires-Dist: pyyaml>=6.0.3
11
+ Requires-Python: >=3.11
12
+ Description-Content-Type: text/markdown
13
+
14
+ # OGC API Registry Validator
15
+
16
+ A Python library for validating OpenAPI documents published by OGC API servers against OGC specification requirements.
17
+
18
+ ## Why This Library?
19
+
20
+ OGC API standards (Features, Tiles, Processes, Records, etc.) define specific requirements for the OpenAPI documents that servers must publish. These requirements include:
21
+
22
+ - **Required endpoints** (e.g., `/collections`, `/conformance`)
23
+ - **Required HTTP operations** (GET, POST, etc.)
24
+ - **Conformance class declarations**
25
+ - **Response schemas and content types**
26
+
27
+ This library performs **static validation** of these OpenAPI documents, helping you verify that your OGC API server's published specification meets the standard requirements before deployment.
28
+
29
+ ## Features
30
+
31
+ - **OGC API Validation**: Validate against OGC API - Features, Tiles, Processes, Records, Coverages, EDR, Maps, Styles, Routes
32
+ - **Conformance-Based Detection**: Automatically detect API type from conformance classes
33
+ - **Error Severity Levels**: Distinguish between critical errors, warnings, and informational messages
34
+ - **Extensible Strategy Pattern**: Add custom validation strategies for new specifications
35
+ - **Async Support**: Full async/await support for high-performance validation
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ pip install ogcapi-registry
41
+ ```
42
+
43
+ Or with uv:
44
+
45
+ ```bash
46
+ uv add ogcapi-registry
47
+ ```
48
+
49
+ ## Quick Start
50
+
51
+ ### Validating an OGC API Server
52
+
53
+ ```python
54
+ from ogcapi_registry import (
55
+ OpenAPIClient,
56
+ validate_ogc_api,
57
+ parse_conformance_classes,
58
+ )
59
+
60
+ # Fetch the OpenAPI document from your server
61
+ client = OpenAPIClient()
62
+ openapi_doc, metadata = client.fetch("https://demo.pygeoapi.io/master/openapi")
63
+
64
+ # Parse conformance classes (usually from /conformance endpoint)
65
+ conformance_uris = [
66
+ "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/core",
67
+ "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/geojson",
68
+ "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/oas30",
69
+ ]
70
+ conformance_classes = parse_conformance_classes(conformance_uris)
71
+
72
+ # Validate against OGC API requirements
73
+ result = validate_ogc_api(openapi_doc, conformance_classes)
74
+
75
+ if result.is_valid:
76
+ print("OpenAPI document is fully compliant!")
77
+ else:
78
+ # Check error severity
79
+ for error in result.critical_errors:
80
+ print(f"CRITICAL: {error['message']}")
81
+
82
+ for warning in result.warnings:
83
+ print(f"WARNING: {warning['message']}")
84
+ ```
85
+
86
+ ### Understanding Error Severity
87
+
88
+ The library classifies validation errors by severity:
89
+
90
+ ```python
91
+ from ogcapi_registry import ErrorSeverity
92
+
93
+ # Critical errors: Must be fixed for OGC compliance
94
+ result.critical_errors # Missing required endpoints, wrong HTTP methods
95
+
96
+ # Warnings: Should be fixed but API may still function
97
+ result.warnings # Missing recommended features like CRS support
98
+
99
+ # Info: Optional improvements
100
+ result.info_errors # Missing optional features like filtering
101
+
102
+ # Check if API is compliant (no critical errors)
103
+ if result.is_compliant:
104
+ print("API meets minimum OGC requirements")
105
+ ```
106
+
107
+ ### Automatic API Type Detection
108
+
109
+ ```python
110
+ from ogcapi_registry import detect_api_types, get_primary_api_type
111
+
112
+ # Detect all API types from conformance classes
113
+ api_types = detect_api_types(conformance_classes)
114
+ # e.g., {OGCAPIType.FEATURES, OGCAPIType.TILES}
115
+
116
+ # Get the primary (most specific) API type
117
+ primary = get_primary_api_type(conformance_classes)
118
+ # e.g., OGCAPIType.FEATURES
119
+ ```
120
+
121
+ ## Application Example: CI/CD Validation
122
+
123
+ Integrate OGC API validation into your deployment pipeline to catch specification errors before they reach production:
124
+
125
+ ```python
126
+ #!/usr/bin/env python
127
+ """validate_ogc_server.py - CI/CD validation script"""
128
+
129
+ import sys
130
+ import httpx
131
+ from ogcapi_registry import (
132
+ OpenAPIClient,
133
+ validate_ogc_api,
134
+ parse_conformance_classes,
135
+ )
136
+
137
+
138
+ def validate_server(base_url: str) -> bool:
139
+ """Validate an OGC API server's OpenAPI document."""
140
+
141
+ # 1. Fetch conformance classes
142
+ response = httpx.get(f"{base_url}/conformance")
143
+ response.raise_for_status()
144
+ conformance_uris = response.json().get("conformsTo", [])
145
+
146
+ if not conformance_uris:
147
+ print("ERROR: No conformance classes declared")
148
+ return False
149
+
150
+ conformance_classes = parse_conformance_classes(conformance_uris)
151
+ print(f"Found {len(conformance_classes)} conformance classes")
152
+
153
+ # 2. Fetch and validate OpenAPI document
154
+ client = OpenAPIClient()
155
+ openapi_doc, metadata = client.fetch(f"{base_url}/openapi")
156
+
157
+ print(f"Validating OpenAPI {openapi_doc.get('openapi', 'unknown')}")
158
+
159
+ # 3. Run OGC API validation
160
+ result = validate_ogc_api(openapi_doc, conformance_classes)
161
+
162
+ # 4. Report results
163
+ print(f"\n{'='*50}")
164
+ print(f"Validation Result: {'PASSED' if result.is_valid else 'FAILED'}")
165
+ print(f"{'='*50}")
166
+
167
+ if result.critical_errors:
168
+ print(f"\nCritical Errors ({len(result.critical_errors)}):")
169
+ for err in result.critical_errors:
170
+ print(f" - {err['path']}: {err['message']}")
171
+
172
+ if result.warnings:
173
+ print(f"\nWarnings ({len(result.warnings)}):")
174
+ for warn in result.warnings:
175
+ print(f" - {warn['path']}: {warn['message']}")
176
+
177
+ summary = result.get_summary()
178
+ print(f"\nSummary: {summary['critical']} critical, "
179
+ f"{summary['warning']} warnings, {summary['info']} info")
180
+
181
+ # Return True only if no critical errors
182
+ return result.is_compliant
183
+
184
+
185
+ if __name__ == "__main__":
186
+ server_url = sys.argv[1] if len(sys.argv) > 1 else "https://demo.pygeoapi.io/master"
187
+
188
+ success = validate_server(server_url)
189
+ sys.exit(0 if success else 1)
190
+ ```
191
+
192
+ Use in GitHub Actions:
193
+
194
+ ```yaml
195
+ - name: Validate OGC API Compliance
196
+ run: python validate_ogc_server.py ${{ env.SERVER_URL }}
197
+ ```
198
+
199
+ ## Supported OGC API Standards
200
+
201
+ | Standard | Strategy Class | Key Validations |
202
+ |----------|---------------|-----------------|
203
+ | OGC API - Common | `CommonStrategy` | Landing page, conformance, API definition |
204
+ | OGC API - Features | `FeaturesStrategy` | Collections, items, bbox, CRS |
205
+ | OGC API - Tiles | `TilesStrategy` | Tile matrix sets, tile endpoints |
206
+ | OGC API - Processes | `ProcessesStrategy` | Process list, execution, jobs |
207
+ | OGC API - Records | `RecordsStrategy` | Catalog, record queries |
208
+ | OGC API - Coverages | `CoveragesStrategy` | Coverage data access |
209
+ | OGC API - EDR | `EDRStrategy` | Environmental data queries |
210
+ | OGC API - Maps | `MapsStrategy` | Map rendering endpoints |
211
+ | OGC API - Styles | `StylesStrategy` | Style management |
212
+ | OGC API - Routes | `RoutesStrategy` | Routing endpoints |
213
+
214
+ ## Advanced Usage
215
+
216
+ ### Custom Validation Strategy
217
+
218
+ ```python
219
+ from ogcapi_registry import ValidationStrategy, OGCAPIType, ValidationResult
220
+
221
+ class MyCustomStrategy(ValidationStrategy):
222
+ """Custom validation for organization-specific requirements."""
223
+
224
+ api_type = OGCAPIType.FEATURES
225
+
226
+ def validate(self, document, conformance_classes):
227
+ errors = []
228
+
229
+ # Add custom validation logic
230
+ if "/health" not in document.get("paths", {}):
231
+ errors.append({
232
+ "path": "paths",
233
+ "message": "Missing /health endpoint (org requirement)",
234
+ "severity": "warning",
235
+ })
236
+
237
+ return ValidationResult.success() if not errors else ValidationResult.failure(errors)
238
+ ```
239
+
240
+ ### Async Validation
241
+
242
+ ```python
243
+ import asyncio
244
+ from ogcapi_registry import AsyncOpenAPIClient, validate_ogc_api
245
+
246
+ async def validate_multiple_servers(urls: list[str]):
247
+ client = AsyncOpenAPIClient()
248
+
249
+ async def validate_one(url):
250
+ doc, _ = await client.fetch(f"{url}/openapi")
251
+ # ... fetch conformance and validate
252
+ return url, result
253
+
254
+ results = await asyncio.gather(*[validate_one(url) for url in urls])
255
+ return dict(results)
256
+ ```
257
+
258
+ ## Documentation
259
+
260
+ Full documentation: [https://francbartoli.github.io/ogcapi-registry/](https://francbartoli.github.io/ogcapi-registry/)
261
+
262
+ ## Development
263
+
264
+ ```bash
265
+ # Clone and install
266
+ git clone https://github.com/francbartoli/ogcapi-registry.git
267
+ cd ogcapi-registry
268
+ uv sync --all-extras --dev
269
+
270
+ # Run tests
271
+ uv run pytest -v
272
+
273
+ # Run linting
274
+ uv run ruff check src/ tests/
275
+ uv run mypy src/ogcapi_registry
276
+ ```
277
+
278
+ ## License
279
+
280
+ MIT
@@ -0,0 +1,267 @@
1
+ # OGC API Registry Validator
2
+
3
+ A Python library for validating OpenAPI documents published by OGC API servers against OGC specification requirements.
4
+
5
+ ## Why This Library?
6
+
7
+ OGC API standards (Features, Tiles, Processes, Records, etc.) define specific requirements for the OpenAPI documents that servers must publish. These requirements include:
8
+
9
+ - **Required endpoints** (e.g., `/collections`, `/conformance`)
10
+ - **Required HTTP operations** (GET, POST, etc.)
11
+ - **Conformance class declarations**
12
+ - **Response schemas and content types**
13
+
14
+ This library performs **static validation** of these OpenAPI documents, helping you verify that your OGC API server's published specification meets the standard requirements before deployment.
15
+
16
+ ## Features
17
+
18
+ - **OGC API Validation**: Validate against OGC API - Features, Tiles, Processes, Records, Coverages, EDR, Maps, Styles, Routes
19
+ - **Conformance-Based Detection**: Automatically detect API type from conformance classes
20
+ - **Error Severity Levels**: Distinguish between critical errors, warnings, and informational messages
21
+ - **Extensible Strategy Pattern**: Add custom validation strategies for new specifications
22
+ - **Async Support**: Full async/await support for high-performance validation
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ pip install ogcapi-registry
28
+ ```
29
+
30
+ Or with uv:
31
+
32
+ ```bash
33
+ uv add ogcapi-registry
34
+ ```
35
+
36
+ ## Quick Start
37
+
38
+ ### Validating an OGC API Server
39
+
40
+ ```python
41
+ from ogcapi_registry import (
42
+ OpenAPIClient,
43
+ validate_ogc_api,
44
+ parse_conformance_classes,
45
+ )
46
+
47
+ # Fetch the OpenAPI document from your server
48
+ client = OpenAPIClient()
49
+ openapi_doc, metadata = client.fetch("https://demo.pygeoapi.io/master/openapi")
50
+
51
+ # Parse conformance classes (usually from /conformance endpoint)
52
+ conformance_uris = [
53
+ "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/core",
54
+ "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/geojson",
55
+ "http://www.opengis.net/spec/ogcapi-features-1/1.0/conf/oas30",
56
+ ]
57
+ conformance_classes = parse_conformance_classes(conformance_uris)
58
+
59
+ # Validate against OGC API requirements
60
+ result = validate_ogc_api(openapi_doc, conformance_classes)
61
+
62
+ if result.is_valid:
63
+ print("OpenAPI document is fully compliant!")
64
+ else:
65
+ # Check error severity
66
+ for error in result.critical_errors:
67
+ print(f"CRITICAL: {error['message']}")
68
+
69
+ for warning in result.warnings:
70
+ print(f"WARNING: {warning['message']}")
71
+ ```
72
+
73
+ ### Understanding Error Severity
74
+
75
+ The library classifies validation errors by severity:
76
+
77
+ ```python
78
+ from ogcapi_registry import ErrorSeverity
79
+
80
+ # Critical errors: Must be fixed for OGC compliance
81
+ result.critical_errors # Missing required endpoints, wrong HTTP methods
82
+
83
+ # Warnings: Should be fixed but API may still function
84
+ result.warnings # Missing recommended features like CRS support
85
+
86
+ # Info: Optional improvements
87
+ result.info_errors # Missing optional features like filtering
88
+
89
+ # Check if API is compliant (no critical errors)
90
+ if result.is_compliant:
91
+ print("API meets minimum OGC requirements")
92
+ ```
93
+
94
+ ### Automatic API Type Detection
95
+
96
+ ```python
97
+ from ogcapi_registry import detect_api_types, get_primary_api_type
98
+
99
+ # Detect all API types from conformance classes
100
+ api_types = detect_api_types(conformance_classes)
101
+ # e.g., {OGCAPIType.FEATURES, OGCAPIType.TILES}
102
+
103
+ # Get the primary (most specific) API type
104
+ primary = get_primary_api_type(conformance_classes)
105
+ # e.g., OGCAPIType.FEATURES
106
+ ```
107
+
108
+ ## Application Example: CI/CD Validation
109
+
110
+ Integrate OGC API validation into your deployment pipeline to catch specification errors before they reach production:
111
+
112
+ ```python
113
+ #!/usr/bin/env python
114
+ """validate_ogc_server.py - CI/CD validation script"""
115
+
116
+ import sys
117
+ import httpx
118
+ from ogcapi_registry import (
119
+ OpenAPIClient,
120
+ validate_ogc_api,
121
+ parse_conformance_classes,
122
+ )
123
+
124
+
125
+ def validate_server(base_url: str) -> bool:
126
+ """Validate an OGC API server's OpenAPI document."""
127
+
128
+ # 1. Fetch conformance classes
129
+ response = httpx.get(f"{base_url}/conformance")
130
+ response.raise_for_status()
131
+ conformance_uris = response.json().get("conformsTo", [])
132
+
133
+ if not conformance_uris:
134
+ print("ERROR: No conformance classes declared")
135
+ return False
136
+
137
+ conformance_classes = parse_conformance_classes(conformance_uris)
138
+ print(f"Found {len(conformance_classes)} conformance classes")
139
+
140
+ # 2. Fetch and validate OpenAPI document
141
+ client = OpenAPIClient()
142
+ openapi_doc, metadata = client.fetch(f"{base_url}/openapi")
143
+
144
+ print(f"Validating OpenAPI {openapi_doc.get('openapi', 'unknown')}")
145
+
146
+ # 3. Run OGC API validation
147
+ result = validate_ogc_api(openapi_doc, conformance_classes)
148
+
149
+ # 4. Report results
150
+ print(f"\n{'='*50}")
151
+ print(f"Validation Result: {'PASSED' if result.is_valid else 'FAILED'}")
152
+ print(f"{'='*50}")
153
+
154
+ if result.critical_errors:
155
+ print(f"\nCritical Errors ({len(result.critical_errors)}):")
156
+ for err in result.critical_errors:
157
+ print(f" - {err['path']}: {err['message']}")
158
+
159
+ if result.warnings:
160
+ print(f"\nWarnings ({len(result.warnings)}):")
161
+ for warn in result.warnings:
162
+ print(f" - {warn['path']}: {warn['message']}")
163
+
164
+ summary = result.get_summary()
165
+ print(f"\nSummary: {summary['critical']} critical, "
166
+ f"{summary['warning']} warnings, {summary['info']} info")
167
+
168
+ # Return True only if no critical errors
169
+ return result.is_compliant
170
+
171
+
172
+ if __name__ == "__main__":
173
+ server_url = sys.argv[1] if len(sys.argv) > 1 else "https://demo.pygeoapi.io/master"
174
+
175
+ success = validate_server(server_url)
176
+ sys.exit(0 if success else 1)
177
+ ```
178
+
179
+ Use in GitHub Actions:
180
+
181
+ ```yaml
182
+ - name: Validate OGC API Compliance
183
+ run: python validate_ogc_server.py ${{ env.SERVER_URL }}
184
+ ```
185
+
186
+ ## Supported OGC API Standards
187
+
188
+ | Standard | Strategy Class | Key Validations |
189
+ |----------|---------------|-----------------|
190
+ | OGC API - Common | `CommonStrategy` | Landing page, conformance, API definition |
191
+ | OGC API - Features | `FeaturesStrategy` | Collections, items, bbox, CRS |
192
+ | OGC API - Tiles | `TilesStrategy` | Tile matrix sets, tile endpoints |
193
+ | OGC API - Processes | `ProcessesStrategy` | Process list, execution, jobs |
194
+ | OGC API - Records | `RecordsStrategy` | Catalog, record queries |
195
+ | OGC API - Coverages | `CoveragesStrategy` | Coverage data access |
196
+ | OGC API - EDR | `EDRStrategy` | Environmental data queries |
197
+ | OGC API - Maps | `MapsStrategy` | Map rendering endpoints |
198
+ | OGC API - Styles | `StylesStrategy` | Style management |
199
+ | OGC API - Routes | `RoutesStrategy` | Routing endpoints |
200
+
201
+ ## Advanced Usage
202
+
203
+ ### Custom Validation Strategy
204
+
205
+ ```python
206
+ from ogcapi_registry import ValidationStrategy, OGCAPIType, ValidationResult
207
+
208
+ class MyCustomStrategy(ValidationStrategy):
209
+ """Custom validation for organization-specific requirements."""
210
+
211
+ api_type = OGCAPIType.FEATURES
212
+
213
+ def validate(self, document, conformance_classes):
214
+ errors = []
215
+
216
+ # Add custom validation logic
217
+ if "/health" not in document.get("paths", {}):
218
+ errors.append({
219
+ "path": "paths",
220
+ "message": "Missing /health endpoint (org requirement)",
221
+ "severity": "warning",
222
+ })
223
+
224
+ return ValidationResult.success() if not errors else ValidationResult.failure(errors)
225
+ ```
226
+
227
+ ### Async Validation
228
+
229
+ ```python
230
+ import asyncio
231
+ from ogcapi_registry import AsyncOpenAPIClient, validate_ogc_api
232
+
233
+ async def validate_multiple_servers(urls: list[str]):
234
+ client = AsyncOpenAPIClient()
235
+
236
+ async def validate_one(url):
237
+ doc, _ = await client.fetch(f"{url}/openapi")
238
+ # ... fetch conformance and validate
239
+ return url, result
240
+
241
+ results = await asyncio.gather(*[validate_one(url) for url in urls])
242
+ return dict(results)
243
+ ```
244
+
245
+ ## Documentation
246
+
247
+ Full documentation: [https://francbartoli.github.io/ogcapi-registry/](https://francbartoli.github.io/ogcapi-registry/)
248
+
249
+ ## Development
250
+
251
+ ```bash
252
+ # Clone and install
253
+ git clone https://github.com/francbartoli/ogcapi-registry.git
254
+ cd ogcapi-registry
255
+ uv sync --all-extras --dev
256
+
257
+ # Run tests
258
+ uv run pytest -v
259
+
260
+ # Run linting
261
+ uv run ruff check src/ tests/
262
+ uv run mypy src/ogcapi_registry
263
+ ```
264
+
265
+ ## License
266
+
267
+ MIT
@@ -0,0 +1,45 @@
1
+ [project]
2
+ name = "ogcapi-registry"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Claude", email = "noreply@anthropic.com" },
8
+ { name = "Francesco Bartoli", email = "xbartolone@gmail.com" }
9
+ ]
10
+ requires-python = ">=3.11"
11
+ dependencies = [
12
+ "httpx>=0.28.1",
13
+ "openapi-pydantic>=0.5.1",
14
+ "pydantic>=2.12.5",
15
+ "pyyaml>=6.0.3",
16
+ ]
17
+
18
+ [build-system]
19
+ requires = ["uv_build>=0.8.17,<0.9.0"]
20
+ build-backend = "uv_build"
21
+
22
+ [dependency-groups]
23
+ dev = [
24
+ "mkdocs>=1.6.1",
25
+ "mkdocs-material>=9.7.0",
26
+ "mypy>=1.19.0",
27
+ "pytest>=9.0.1",
28
+ "pytest-asyncio>=1.3.0",
29
+ "pytest-cov>=6.0.0",
30
+ "pytest-httpx>=0.36.0",
31
+ "ruff>=0.14.8",
32
+ "types-pyyaml>=6.0.12.20250915",
33
+ ]
34
+
35
+ [tool.mypy]
36
+ python_version = "3.11"
37
+ warn_unused_ignores = true
38
+ plugins = ["pydantic.mypy"]
39
+ # Ignore missing imports for packages without type stubs
40
+ ignore_missing_imports = true
41
+
42
+ [tool.pydantic-mypy]
43
+ init_forbid_extra = true
44
+ init_typed = true
45
+ warn_required_dynamic_aliases = true