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.
Files changed (22) hide show
  1. oapi_profile_builder-2.4.0/PKG-INFO +572 -0
  2. oapi_profile_builder-2.4.0/README.md +527 -0
  3. {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/pyproject.toml +1 -1
  4. {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/generate.py +211 -30
  5. {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/models.py +85 -8
  6. oapi_profile_builder-2.4.0/src/oapi_profile_builder.egg-info/PKG-INFO +572 -0
  7. oapi_profile_builder-2.2.0/PKG-INFO +0 -879
  8. oapi_profile_builder-2.2.0/README.md +0 -834
  9. oapi_profile_builder-2.2.0/src/oapi_profile_builder.egg-info/PKG-INFO +0 -879
  10. {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/LICENSE +0 -0
  11. {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/setup.cfg +0 -0
  12. {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/__init__.py +0 -0
  13. {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/cite.py +0 -0
  14. {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/cite_features.py +0 -0
  15. {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/cli.py +0 -0
  16. {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/compile.py +0 -0
  17. {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder/server_validation.py +0 -0
  18. {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder.egg-info/SOURCES.txt +0 -0
  19. {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder.egg-info/dependency_links.txt +0 -0
  20. {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder.egg-info/entry_points.txt +0 -0
  21. {oapi_profile_builder-2.2.0 → oapi_profile_builder-2.4.0}/src/oapi_profile_builder.egg-info/requires.txt +0 -0
  22. {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
+ [![PyPI](https://img.shields.io/pypi/v/oapi-profile-builder)](https://pypi.org/project/oapi-profile-builder/)
51
+ [![License](https://img.shields.io/badge/license-Apache-blue)](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