oapi-profile-builder 2.2.0__tar.gz → 2.4.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.
- oapi_profile_builder-2.4.0/PKG-INFO +572 -0
- oapi_profile_builder-2.4.0/README.md +527 -0
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/pyproject.toml +1 -1
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/generate.py +211 -30
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/models.py +85 -8
- oapi_profile_builder-2.4.0/src/oapi_profile_builder.egg-info/PKG-INFO +572 -0
- oapi_profile_builder-2.2.0/PKG-INFO +0 -879
- oapi_profile_builder-2.2.0/README.md +0 -834
- oapi_profile_builder-2.2.0/src/oapi_profile_builder.egg-info/PKG-INFO +0 -879
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/LICENSE +0 -0
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/setup.cfg +0 -0
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/__init__.py +0 -0
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/cite.py +0 -0
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/cite_features.py +0 -0
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/cli.py +0 -0
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/compile.py +0 -0
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/server_validation.py +0 -0
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder.egg-info/SOURCES.txt +0 -0
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder.egg-info/dependency_links.txt +0 -0
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder.egg-info/entry_points.txt +0 -0
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder.egg-info/requires.txt +0 -0
- {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: oapi-profile-builder
|
|
3
|
+
Version: 2.4.0
|
|
4
|
+
Summary: Authoritative tooling for creating OGC API Service Profiles (EDR, Features)
|
|
5
|
+
Author-email: Shane Mill <shane.mill@noaa.gov>
|
|
6
|
+
License: Apache License
|
|
7
|
+
Version 2.0, January 2004
|
|
8
|
+
http://www.apache.org/licenses/
|
|
9
|
+
|
|
10
|
+
Copyright 2025 NOAA/NWS/Meteorological Development Laboratory
|
|
11
|
+
|
|
12
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
13
|
+
you may not use this file except in compliance with the License.
|
|
14
|
+
You may obtain a copy of the License at
|
|
15
|
+
|
|
16
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
17
|
+
|
|
18
|
+
Unless required by applicable law or agreed to in writing, software
|
|
19
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
+
See the License for the specific language governing permissions and
|
|
22
|
+
limitations under the License.
|
|
23
|
+
|
|
24
|
+
Project-URL: Homepage, https://github.com/ShaneMill1/OGC-API-Service-Profile-Builder
|
|
25
|
+
Project-URL: Issues, https://github.com/ShaneMill1/OGC-API-Service-Profile-Builder/issues
|
|
26
|
+
Keywords: ogc,edr,features,environmental-data-retrieval,profile,openapi,asyncapi
|
|
27
|
+
Classifier: Development Status :: 4 - Beta
|
|
28
|
+
Classifier: Intended Audience :: Science/Research
|
|
29
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
30
|
+
Classifier: Programming Language :: Python :: 3
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
34
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
35
|
+
Requires-Python: >=3.10
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
License-File: LICENSE
|
|
38
|
+
Requires-Dist: edr-pydantic>=0.3
|
|
39
|
+
Requires-Dist: pydantic>=2.0
|
|
40
|
+
Requires-Dist: pyyaml>=6.0
|
|
41
|
+
Requires-Dist: requests>=2.31
|
|
42
|
+
Provides-Extra: validate
|
|
43
|
+
Requires-Dist: schemathesis>=3.0; extra == "validate"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# OGC API Service Profile Builder
|
|
47
|
+
|
|
48
|
+
Generate OGC API - EDR Part 3 Service Profile artifacts from a YAML config — OpenAPI 3.1.0, AsyncAPI, AsciiDoc requirements, and conformance tests.
|
|
49
|
+
|
|
50
|
+
[](https://pypi.org/project/oapi-profile-builder/)
|
|
51
|
+
[](LICENSE)
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install oapi-profile-builder
|
|
59
|
+
|
|
60
|
+
# Copy an example config and edit it
|
|
61
|
+
cp examples/minimal_profile.yaml my_profile.yaml
|
|
62
|
+
|
|
63
|
+
# Validate and generate artifacts
|
|
64
|
+
oapi-profile-builder generate --config my_profile.yaml --output ./output
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
That's it. The `output/` directory will contain:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
output/
|
|
71
|
+
├── openapi.yaml # OpenAPI 3.1.0 — ready for Swagger UI, Redoc, schemathesis
|
|
72
|
+
├── profile_config.json # Round-trip serialized profile
|
|
73
|
+
├── document.adoc # Metanorma root document
|
|
74
|
+
├── sections/ # Abstract, Preface, Scope, Conformance, References, Terms
|
|
75
|
+
├── requirements/ # Individual REQ_*.adoc files
|
|
76
|
+
└── abstract_tests/ # Individual ATS_*.adoc files
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Example Profiles
|
|
82
|
+
|
|
83
|
+
Three working examples are included:
|
|
84
|
+
|
|
85
|
+
| File | What it shows |
|
|
86
|
+
|---|---|
|
|
87
|
+
| [`examples/minimal_profile.yaml`](examples/minimal_profile.yaml) | Smallest valid profile — one collection, one requirement |
|
|
88
|
+
| [`examples/insitu_observations_profile.yaml`](examples/insitu_observations_profile.yaml) | Full meteorological profile — 8 parameters with QUDT units, CF standard names, metocean extensions, CRS listing, temporal extent, custom dimensions, `parameter_schema` |
|
|
89
|
+
| [`examples/nwsviz_profile.yaml`](examples/nwsviz_profile.yaml) | Production profile — 13 collections, 3 OGC API Processes, PDF metadata |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## CLI Reference
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
oapi-profile-builder generate --config <file> --output <dir> [--pdf]
|
|
97
|
+
oapi-profile-builder validate --config <file>
|
|
98
|
+
oapi-profile-builder validate-server --config <file> --url <url> [--max-examples N] [--stateful]
|
|
99
|
+
oapi-profile-builder cite-test --url <url> [--report <dir>]
|
|
100
|
+
oapi-profile-builder cite-test-features --url <url> [--report <dir>]
|
|
101
|
+
oapi-profile-builder schema [--output <file>]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `generate`
|
|
105
|
+
|
|
106
|
+
Validates the profile config and writes all artifacts to the output directory.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
oapi-profile-builder generate --config my_profile.yaml --output ./output
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Add `--pdf` to also compile an OGC-compliant PDF via the `metanorma/metanorma` Docker image (Docker required):
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
oapi-profile-builder generate --config my_profile.yaml --output ./output --pdf
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### `validate`
|
|
119
|
+
|
|
120
|
+
Validates the config without writing any files. Useful in CI before generating.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
oapi-profile-builder validate --config my_profile.yaml
|
|
124
|
+
# Profile 'my_profile' is valid.
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### `validate-server`
|
|
128
|
+
|
|
129
|
+
Runs [schemathesis](https://schemathesis.io/) against a live server using the profile's generated OpenAPI. Requires `pip install oapi-profile-builder[validate]`.
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
oapi-profile-builder validate-server \
|
|
133
|
+
--config my_profile.yaml \
|
|
134
|
+
--url https://my-server.example.com \
|
|
135
|
+
--max-examples 5
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Supply real `instanceId` values in your config so schemathesis can exercise instance-level paths:
|
|
139
|
+
|
|
140
|
+
```yaml
|
|
141
|
+
collection_examples:
|
|
142
|
+
my_collection:
|
|
143
|
+
instanceId: "2025-04-02T00:00:00Z"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### `cite-test` / `cite-test-features`
|
|
147
|
+
|
|
148
|
+
Runs the official OGC CITE conformance test suites against a live server. Docker and Maven are required for `cite-test` (EDR); Docker only for `cite-test-features`.
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# OGC API - EDR Part 1 (builds ets-ogcapi-edr10 on first run, ~2 min)
|
|
152
|
+
oapi-profile-builder cite-test \
|
|
153
|
+
--url https://my-server.example.com \
|
|
154
|
+
--report ./cite_results
|
|
155
|
+
|
|
156
|
+
# OGC API - Features Part 1 (pulls pre-built image from Docker Hub)
|
|
157
|
+
oapi-profile-builder cite-test-features \
|
|
158
|
+
--url https://my-server.example.com \
|
|
159
|
+
--report ./cite_features_results
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Results:
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
OGC API - EDR CITE Results
|
|
166
|
+
Passed: 76/84
|
|
167
|
+
Failed: 0
|
|
168
|
+
Skipped: 8
|
|
169
|
+
|
|
170
|
+
✓ All CITE tests passed.
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## GitHub Actions
|
|
176
|
+
|
|
177
|
+
No local install needed. Add this to any workflow to generate profile artifacts from a config file:
|
|
178
|
+
|
|
179
|
+
```yaml
|
|
180
|
+
name: Generate Profile
|
|
181
|
+
|
|
182
|
+
on:
|
|
183
|
+
push:
|
|
184
|
+
paths: ['my_profile.yaml']
|
|
185
|
+
|
|
186
|
+
jobs:
|
|
187
|
+
generate:
|
|
188
|
+
runs-on: ubuntu-latest
|
|
189
|
+
steps:
|
|
190
|
+
- uses: actions/checkout@v4
|
|
191
|
+
|
|
192
|
+
- name: Generate profile artifacts
|
|
193
|
+
uses: ShaneMill1/OGC-API-Service-Profile-Builder@main
|
|
194
|
+
with:
|
|
195
|
+
config: my_profile.yaml
|
|
196
|
+
output: ./profile_output
|
|
197
|
+
|
|
198
|
+
- name: Upload artifacts
|
|
199
|
+
uses: actions/upload-artifact@v4
|
|
200
|
+
with:
|
|
201
|
+
name: profile-artifacts
|
|
202
|
+
path: ./profile_output/
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**To download the artifacts:** Actions tab → click the run → scroll to **Artifacts** at the bottom → download the zip.
|
|
206
|
+
|
|
207
|
+
### Action inputs
|
|
208
|
+
|
|
209
|
+
| Input | Default | Description |
|
|
210
|
+
|---|---|---|
|
|
211
|
+
| `config` | — | Path to the profile config YAML (required) |
|
|
212
|
+
| `output` | `./profile_output` | Output directory |
|
|
213
|
+
| `version` | `latest` | Package version to install |
|
|
214
|
+
| `pdf` | `false` | Compile PDF via Metanorma (Docker required on runner) |
|
|
215
|
+
| `cite-url` | — | Run OGC CITE tests against this server URL |
|
|
216
|
+
| `cite-type` | `edr` | `edr`, `features`, or `both` |
|
|
217
|
+
|
|
218
|
+
> **CITE + VPN:** The CITE test runner needs to reach the server from GitHub's runners. Servers behind a VPN require a [self-hosted runner](https://docs.github.com/en/actions/hosting-your-own-runners).
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Profile Config Reference
|
|
223
|
+
|
|
224
|
+
A profile config is a YAML file. The full JSON Schema is at [`profile.schema.json`](profile.schema.json).
|
|
225
|
+
|
|
226
|
+
### Minimal valid config
|
|
227
|
+
|
|
228
|
+
```yaml
|
|
229
|
+
name: my_profile # lowercase, a-z 0-9 _ only
|
|
230
|
+
title: My EDR Profile
|
|
231
|
+
version: "1.0"
|
|
232
|
+
description: Brief description of what this service profile provides.
|
|
233
|
+
keywords:
|
|
234
|
+
- my-parameter
|
|
235
|
+
- my-domain
|
|
236
|
+
|
|
237
|
+
required_conformance_classes:
|
|
238
|
+
- "http://www.opengis.net/spec/ogcapi-edr-1/1.0/conf/core"
|
|
239
|
+
|
|
240
|
+
extent_requirements:
|
|
241
|
+
minimum_bbox: [-180, -90, 180, 90]
|
|
242
|
+
allowed_crs:
|
|
243
|
+
- "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
|
|
244
|
+
|
|
245
|
+
output_formats:
|
|
246
|
+
- name: GeoJSON
|
|
247
|
+
media_type: application/geo+json
|
|
248
|
+
|
|
249
|
+
collections:
|
|
250
|
+
- id: my_collection
|
|
251
|
+
links:
|
|
252
|
+
- href: https://example.com/collections/my_collection
|
|
253
|
+
rel: self
|
|
254
|
+
type: application/json
|
|
255
|
+
extent:
|
|
256
|
+
spatial:
|
|
257
|
+
bbox: [[-180, -90, 180, 90]]
|
|
258
|
+
crs: "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
|
|
259
|
+
parameter_names:
|
|
260
|
+
temperature:
|
|
261
|
+
type: Parameter
|
|
262
|
+
observedProperty:
|
|
263
|
+
label: Air Temperature
|
|
264
|
+
unit:
|
|
265
|
+
label: Celsius
|
|
266
|
+
symbol: "°C"
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Top-level fields
|
|
270
|
+
|
|
271
|
+
| Field | Type | Required | Description |
|
|
272
|
+
|---|---|---|---|
|
|
273
|
+
| `name` | string | yes | Lowercase `a-z 0-9 _`. Drives OGC URIs and OpenAPI `operationId`s |
|
|
274
|
+
| `title` | string | yes | Human-readable profile title |
|
|
275
|
+
| `version` | string | no | Defaults to `"1.0"` |
|
|
276
|
+
| `description` | string | no | Human-readable description of the service profile. Surfaces in the OpenAPI `info.description` and the landing page response schema |
|
|
277
|
+
| `keywords` | list | no | Service-level keywords (e.g. query types, parameter names, domain terms). Surfaces in `info.x-keywords` and the landing page response schema. Distinct from `document_metadata.keywords`, which are for the OGC PDF header |
|
|
278
|
+
| `server_url` | string | no | Documentation only — not written to the profile OpenAPI |
|
|
279
|
+
| `allow_post_queries` | bool | no | When `true`, generates `POST` alongside `GET` for all EDR data query endpoints. Defaults to `false` |
|
|
280
|
+
| `collections` | list | yes | One or more EDR collections (see below) |
|
|
281
|
+
| `required_conformance_classes` | list | no | Conformance classes implementations must declare. Defaults to EDR Core |
|
|
282
|
+
| `extent_requirements` | object | no | Profile-level CRS/TRS/VRS constraints (see below) |
|
|
283
|
+
| `output_formats` | list | no | Format name → media type + schema ref mappings |
|
|
284
|
+
| `collection_id_pattern` | string | no | Regex all collection IDs must match |
|
|
285
|
+
| `parameter_name_pattern` | string | no | Regex all `parameter_names` keys must match |
|
|
286
|
+
| `parameter_schema` | object | no | JSON Schema for parameter objects — replaces the default schema in the generated OpenAPI (see below) |
|
|
287
|
+
| `processes` | list | no | OGC API Processes to expose in the OpenAPI |
|
|
288
|
+
| `requirements` | list | no | Normative requirements for the AsciiDoc/PDF |
|
|
289
|
+
| `abstract_tests` | list | no | Conformance tests — each must reference a valid requirement `id` |
|
|
290
|
+
| `pubsub` | object | no | OGC API - EDR Part 2 PubSub config — generates `asyncapi.yaml` |
|
|
291
|
+
| `collection_examples` | object | no | `{collectionId: {instanceId: "..."}}` — used by `validate-server` |
|
|
292
|
+
| `document_metadata` | object | no | Metanorma PDF header (doc number, editors, orgs, keywords) |
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
### `collections[]`
|
|
297
|
+
|
|
298
|
+
Uses the [edr-pydantic](https://github.com/KNMI/edr-pydantic) `Collection` model — the same schema an EDR server returns at `/collections/{id}`.
|
|
299
|
+
|
|
300
|
+
| Field | Required | Description |
|
|
301
|
+
|---|---|---|
|
|
302
|
+
| `id` | yes | Collection identifier |
|
|
303
|
+
| `title` | no | Human-readable name |
|
|
304
|
+
| `description` | no | Longer description |
|
|
305
|
+
| `links` | yes | At minimum a `self` link |
|
|
306
|
+
| `extent.spatial.bbox` | yes | `[[minLon, minLat, maxLon, maxLat]]` |
|
|
307
|
+
| `extent.spatial.crs` | yes | CRS URI — validated against `extent_requirements` |
|
|
308
|
+
| `extent.temporal` | no | `interval`, `values`, `trs` |
|
|
309
|
+
| `extent.vertical` | no | `interval`, `values`, `vrs` |
|
|
310
|
+
| `extent.custom` | no | Custom dimensions (e.g. `standard_name`, `level`) |
|
|
311
|
+
| `crs` | no | Full list of CRS values this collection supports |
|
|
312
|
+
| `output_formats` | no | Format names this collection supports (e.g. `[CoverageJSON, GeoJSON]`) |
|
|
313
|
+
| `data_queries` | no | EDR query types: `position`, `area`, `radius`, `cube`, `trajectory`, `corridor`, `locations`, `items`, `instances` |
|
|
314
|
+
| `parameter_names` | no | Map of parameter id → Parameter object. All parameters must have `unit` and `observedProperty` |
|
|
315
|
+
|
|
316
|
+
#### `data_queries` example
|
|
317
|
+
|
|
318
|
+
```yaml
|
|
319
|
+
data_queries:
|
|
320
|
+
position:
|
|
321
|
+
link:
|
|
322
|
+
href: https://example.com/collections/obs/position
|
|
323
|
+
rel: data
|
|
324
|
+
variables:
|
|
325
|
+
query_type: position
|
|
326
|
+
output_formats: [CoverageJSON]
|
|
327
|
+
radius:
|
|
328
|
+
link:
|
|
329
|
+
href: https://example.com/collections/obs/radius
|
|
330
|
+
rel: data
|
|
331
|
+
variables:
|
|
332
|
+
query_type: radius
|
|
333
|
+
output_formats: [CoverageJSON]
|
|
334
|
+
within_units: [m, km]
|
|
335
|
+
# crs_details: per-query CRS support, validated against extent_requirements
|
|
336
|
+
crs_details:
|
|
337
|
+
- crs: "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
|
|
338
|
+
- crs: "http://www.opengis.net/def/crs/EPSG/0/4326"
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
#### `parameter_names` example
|
|
342
|
+
|
|
343
|
+
```yaml
|
|
344
|
+
parameter_names:
|
|
345
|
+
air-temperature-2m:
|
|
346
|
+
type: Parameter
|
|
347
|
+
label: Air Temperature at 2m
|
|
348
|
+
description: Instantaneous air temperature at 2 metres above ground
|
|
349
|
+
observedProperty:
|
|
350
|
+
id: "https://vocab.nerc.ac.uk/standard_name/air_temperature"
|
|
351
|
+
label: Air Temperature
|
|
352
|
+
unit:
|
|
353
|
+
label: Kelvin
|
|
354
|
+
symbol:
|
|
355
|
+
value: K
|
|
356
|
+
type: "https://qudt.org/vocab/unit/K"
|
|
357
|
+
measurementType:
|
|
358
|
+
method: point
|
|
359
|
+
duration: PT0S
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
### `extent_requirements`
|
|
365
|
+
|
|
366
|
+
Constrains CRS, TRS, and VRS values across all collections. Validated at build time and embedded in the generated OpenAPI.
|
|
367
|
+
|
|
368
|
+
```yaml
|
|
369
|
+
extent_requirements:
|
|
370
|
+
minimum_bbox: [-180, -90, 180, 90]
|
|
371
|
+
|
|
372
|
+
# Option A: exact list
|
|
373
|
+
allowed_crs:
|
|
374
|
+
- "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
|
|
375
|
+
- "http://www.opengis.net/def/crs/EPSG/0/4326"
|
|
376
|
+
|
|
377
|
+
# Option B: regex (accepts any OGC or EPSG CRS)
|
|
378
|
+
# crs_pattern: "^http://www\\.opengis\\.net/def/crs/(OGC|EPSG)/.*$"
|
|
379
|
+
|
|
380
|
+
allowed_trs:
|
|
381
|
+
- "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian"
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Either `allowed_crs` or `crs_pattern` is required. The same enum/regex approach applies to `allowed_trs`/`trs_pattern` and `allowed_vrs`/`vrs_pattern`.
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
### `parameter_schema`
|
|
389
|
+
|
|
390
|
+
A JSON Schema fragment that replaces the default parameter schema in the generated OpenAPI. Use this to enforce field-level constraints — required fields, QUDT unit URIs, CF standard name URIs, ISO 8601 durations, and custom extension properties.
|
|
391
|
+
|
|
392
|
+
```yaml
|
|
393
|
+
parameter_schema:
|
|
394
|
+
type: object
|
|
395
|
+
required:
|
|
396
|
+
- type
|
|
397
|
+
- observedProperty
|
|
398
|
+
- measurementType
|
|
399
|
+
- label
|
|
400
|
+
- description
|
|
401
|
+
- unit
|
|
402
|
+
- "metocean:standard_name"
|
|
403
|
+
- "metocean:level"
|
|
404
|
+
properties:
|
|
405
|
+
unit:
|
|
406
|
+
type: object
|
|
407
|
+
properties:
|
|
408
|
+
symbol:
|
|
409
|
+
type: object
|
|
410
|
+
properties:
|
|
411
|
+
type:
|
|
412
|
+
type: string
|
|
413
|
+
pattern: "^https://qudt\\.org/vocab/unit/.*$"
|
|
414
|
+
observedProperty:
|
|
415
|
+
type: object
|
|
416
|
+
properties:
|
|
417
|
+
id:
|
|
418
|
+
type: string
|
|
419
|
+
pattern: "^https://vocab\\.nerc\\.ac\\.uk/standard_name/.*$"
|
|
420
|
+
measurementType:
|
|
421
|
+
type: object
|
|
422
|
+
properties:
|
|
423
|
+
duration:
|
|
424
|
+
type: string
|
|
425
|
+
pattern: "^P(\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+S)?)?$"
|
|
426
|
+
"metocean:standard_name":
|
|
427
|
+
type: string
|
|
428
|
+
"metocean:level":
|
|
429
|
+
type: number
|
|
430
|
+
additionalProperties: true
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
See [`examples/insitu_observations_profile.yaml`](examples/insitu_observations_profile.yaml) for a complete working example.
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
### `requirements[]` and `abstract_tests[]`
|
|
438
|
+
|
|
439
|
+
Requirements drive the AsciiDoc/PDF output. Abstract tests must reference a valid requirement `id`.
|
|
440
|
+
|
|
441
|
+
```yaml
|
|
442
|
+
requirements:
|
|
443
|
+
- id: position-coveragejson # lowercase, hyphens only
|
|
444
|
+
statement: The position query SHALL return CoverageJSON.
|
|
445
|
+
parts:
|
|
446
|
+
- The service SHALL provide a /collections/{id}/position endpoint.
|
|
447
|
+
- The response Content-Type SHALL be application/prs.coverage+json.
|
|
448
|
+
|
|
449
|
+
abstract_tests:
|
|
450
|
+
- id: position-coveragejson # must equal requirement_id
|
|
451
|
+
requirement_id: position-coveragejson
|
|
452
|
+
steps:
|
|
453
|
+
- Send GET /collections/{id}/position?coords=POINT(lon lat).
|
|
454
|
+
- Verify Content-Type is application/prs.coverage+json.
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
### `processes[]`
|
|
460
|
+
|
|
461
|
+
Adds OGC API Processes paths to the generated OpenAPI.
|
|
462
|
+
|
|
463
|
+
```yaml
|
|
464
|
+
processes:
|
|
465
|
+
- id: compute-difference
|
|
466
|
+
title: Compute Dataset Difference
|
|
467
|
+
description: Calculates the difference between two datasets.
|
|
468
|
+
output_content:
|
|
469
|
+
application/zip:
|
|
470
|
+
schema:
|
|
471
|
+
type: object
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
Generates: `/processes/compute-difference`, `/processes/compute-difference/execution`, `/jobs`, `/jobs/{jobId}`, `/jobs/{jobId}/results`.
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
### `pubsub`
|
|
479
|
+
|
|
480
|
+
When present, generates `asyncapi.yaml` alongside `openapi.yaml`.
|
|
481
|
+
|
|
482
|
+
```yaml
|
|
483
|
+
pubsub:
|
|
484
|
+
broker_host: my-broker.example.com
|
|
485
|
+
broker_port: 5672
|
|
486
|
+
protocol: amqp # amqp | mqtt | kafka
|
|
487
|
+
collections:
|
|
488
|
+
- my_collection
|
|
489
|
+
filters:
|
|
490
|
+
- name: station
|
|
491
|
+
description: Filter by station ID
|
|
492
|
+
type: string
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
### `document_metadata`
|
|
498
|
+
|
|
499
|
+
Required only when compiling a PDF with `--pdf`.
|
|
500
|
+
|
|
501
|
+
```yaml
|
|
502
|
+
document_metadata:
|
|
503
|
+
doc_number: "25-myprofile"
|
|
504
|
+
doc_subtype: implementation # implementation | best-practice | engineering-report
|
|
505
|
+
editors:
|
|
506
|
+
- Jane Smith
|
|
507
|
+
submitting_orgs:
|
|
508
|
+
- My Organization
|
|
509
|
+
keywords:
|
|
510
|
+
- ogcdoc
|
|
511
|
+
- OGC API
|
|
512
|
+
- EDR
|
|
513
|
+
copyright_year: 2026
|
|
514
|
+
external_id: http://www.opengis.net/doc/dp/my-profile/1.0
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
519
|
+
## Validation Rules
|
|
520
|
+
|
|
521
|
+
The tool enforces these rules at build time. Violations produce clear error messages.
|
|
522
|
+
|
|
523
|
+
| Rule | Detail |
|
|
524
|
+
|---|---|
|
|
525
|
+
| `name` format | Must match `^[a-z0-9_]+$` |
|
|
526
|
+
| No duplicate collection IDs | Across the whole profile |
|
|
527
|
+
| `extent_requirements` requires CRS spec | Either `allowed_crs` or `crs_pattern` must be set |
|
|
528
|
+
| Collection CRS validated | Each `extent.spatial.crs` checked against `allowed_crs`/`crs_pattern` |
|
|
529
|
+
| Collection TRS validated | Each `extent.temporal.trs` checked against `allowed_trs`/`trs_pattern` |
|
|
530
|
+
| `crs_details` validated | Each `data_queries.*.variables.crs_details[].crs` checked against CRS constraints |
|
|
531
|
+
| Parameters need `unit` + `observedProperty` | Required by OGC API - EDR Part 3 |
|
|
532
|
+
| `parameter_name_pattern` enforced | All `parameter_names` keys must match if set |
|
|
533
|
+
| `collection_id_pattern` enforced | All collection IDs must match if set |
|
|
534
|
+
| Abstract test IDs match requirements | `requirement_id` must reference an existing requirement |
|
|
535
|
+
| Requirement IDs | Must match `^[a-z0-9][a-z0-9\-]*$`, no trailing hyphen |
|
|
536
|
+
|
|
537
|
+
---
|
|
538
|
+
|
|
539
|
+
## Programmatic Use
|
|
540
|
+
|
|
541
|
+
```python
|
|
542
|
+
from oapi_profile_builder.models import ServiceProfile
|
|
543
|
+
from oapi_profile_builder.generate import generate
|
|
544
|
+
from pathlib import Path
|
|
545
|
+
import yaml
|
|
546
|
+
|
|
547
|
+
with open("my_profile.yaml") as f:
|
|
548
|
+
config = yaml.safe_load(f)
|
|
549
|
+
|
|
550
|
+
profile = ServiceProfile.model_validate(config) # validates everything
|
|
551
|
+
generate(profile, Path("./output"))
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
## Standards
|
|
557
|
+
|
|
558
|
+
- [OGC API - EDR Part 1: Core](https://docs.ogc.org/is/19-086r6/19-086r6.html)
|
|
559
|
+
- [OGC API - EDR Part 2: PubSub](https://docs.ogc.org/DRAFTS/21-009.html)
|
|
560
|
+
- [OGC API - EDR Part 3: Service Profiles (draft)](https://github.com/opengeospatial/ogcapi-environmental-data-retrieval)
|
|
561
|
+
- [OGC API - Processes Part 1](https://docs.ogc.org/is/18-062r2/18-062r2.html)
|
|
562
|
+
- OpenAPI 3.1.0 / AsyncAPI 3.0
|
|
563
|
+
- Metanorma/AsciiDoc
|
|
564
|
+
|
|
565
|
+
## License
|
|
566
|
+
|
|
567
|
+
Apache 2.0 — see [LICENSE](LICENSE).
|
|
568
|
+
|
|
569
|
+
## Contact
|
|
570
|
+
|
|
571
|
+
Shane Mill · NOAA/NWS/MDL · shane.mill@noaa.gov
|
|
572
|
+
Issues: https://github.com/ShaneMill1/OGC-API-Service-Profile-Builder/issues
|