oapi-profile-builder 2.0.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,17 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2025 NOAA/NWS/Meteorological Development Laboratory
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
@@ -0,0 +1,612 @@
1
+ Metadata-Version: 2.4
2
+ Name: oapi-profile-builder
3
+ Version: 2.0.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
+ Authoritative tooling for creating OGC API Service Profiles (EDR, Features), built on Pydantic and [edr-pydantic](https://github.com/KNMI/edr-pydantic).
49
+
50
+ ## Overview
51
+
52
+ Profile structure is defined as Pydantic models (`src/oapi_profile_builder/models.py`). Instantiating a `ServiceProfile` validates the entire profile — cross-model validators catch referential errors — before any files are written.
53
+
54
+ Collections use `edr-pydantic`'s authoritative `Collection` model directly, meaning a profile config is simultaneously a valid EDR collection descriptor and a Part 3 profile definition.
55
+
56
+ ## Installation
57
+
58
+ ```bash
59
+ pip install oapi-profile-builder
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Workflow
65
+
66
+ ### 1. Author a Profile Config
67
+
68
+ A profile config is a YAML or JSON file. Start with the minimal example:
69
+
70
+ ```bash
71
+ cp examples/minimal_profile.yaml my_profile.yaml
72
+ ```
73
+
74
+ The minimal valid config:
75
+
76
+ ```yaml
77
+ name: my_profile
78
+ title: My EDR Profile
79
+
80
+ # OGC API - EDR Part 3 compliance fields (recommended)
81
+ required_conformance_classes:
82
+ - "http://www.opengis.net/spec/ogcapi-edr-1/1.0/conf/core"
83
+
84
+ extent_requirements:
85
+ minimum_bbox: [-180, -90, 180, 90]
86
+ allowed_crs:
87
+ - "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
88
+
89
+ output_formats:
90
+ - name: GeoJSON
91
+ media_type: application/geo+json
92
+ schema_ref: https://geojson.org/schema/FeatureCollection.json
93
+
94
+ collections:
95
+ - id: my_collection
96
+ links:
97
+ - href: https://example.com/collections/my_collection
98
+ rel: self
99
+ type: application/json
100
+ extent:
101
+ spatial:
102
+ bbox:
103
+ - [-180, -90, 180, 90]
104
+ crs: "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
105
+ parameter_names:
106
+ temp:
107
+ type: Parameter
108
+ observedProperty:
109
+ label: Temperature
110
+ unit: # REQUIRED per OGC API - EDR Part 3
111
+ label: Celsius
112
+ symbol: C
113
+ ```
114
+
115
+ See [`examples/minimal_profile.yaml`](examples/minimal_profile.yaml) for a complete working example and [`examples/nwsviz_profile.yaml`](examples/nwsviz_profile.yaml) for a full profile with 13 collections, 3 processes, requirements, abstract tests, and document metadata.
116
+
117
+ ### 2. Generate Profile Artifacts
118
+
119
+ ```bash
120
+ oapi-profile-builder generate \
121
+ --config my_profile.yaml \
122
+ --output ./my_profile
123
+ ```
124
+
125
+ Produces:
126
+
127
+ ```
128
+ my_profile/
129
+ ├── openapi.yaml
130
+ ├── profile_config.json
131
+ ├── document.adoc # Metanorma root document
132
+ ├── sections/
133
+ │ ├── 00-abstract.adoc
134
+ │ ├── 01-preface.adoc
135
+ │ ├── 02-scope.adoc
136
+ │ ├── 03-conformance.adoc
137
+ │ ├── 04-references.adoc
138
+ │ ├── 05-terms.adoc
139
+ │ ├── 06-requirements.adoc
140
+ │ └── 07-abstract-tests.adoc
141
+ ├── requirements/
142
+ │ ├── requirements_class_core.adoc
143
+ │ └── core/REQ_<id>.adoc
144
+ └── abstract_tests/
145
+ ├── ATS_class_core.adoc
146
+ └── core/ATS_<id>.adoc
147
+ ```
148
+
149
+ Validate a config without generating output:
150
+
151
+ ```bash
152
+ oapi-profile-builder validate --config my_profile.yaml
153
+ ```
154
+
155
+ ### 3. Compile OGC PDF
156
+
157
+ Requires Docker. Shells out to the official `metanorma/metanorma` image — no Ruby or LaTeX install needed.
158
+
159
+ ```bash
160
+ oapi-profile-builder generate \
161
+ --config my_profile.yaml \
162
+ --output ./my_profile \
163
+ --pdf
164
+ ```
165
+
166
+ The `document_metadata` block in the profile config drives the Metanorma document header:
167
+
168
+ ```yaml
169
+ document_metadata:
170
+ doc_number: "24-nwsviz"
171
+ doc_subtype: implementation
172
+ copyright_year: 2026
173
+ editors:
174
+ - Shane Mill
175
+ submitting_orgs:
176
+ - NOAA/NWS/MDL
177
+ keywords:
178
+ - ogcdoc
179
+ - OGC API
180
+ - EDR
181
+ - NWSViz
182
+ - service profile
183
+ external_id: http://www.opengis.net/doc/dp/ogcapi-edr-nwsviz/1.0
184
+ ```
185
+
186
+ Produces `my_profile/document.pdf` — a fully compliant OGC `draft-standard` PDF with Abstract, Preface, Scope, Conformance, References, Terms, Requirements class, and normative Abstract Test Suite annex.
187
+
188
+ ### 4. Validate Against a Live Server
189
+
190
+ ```bash
191
+ oapi-profile-builder validate-server \
192
+ --config my_profile.yaml \
193
+ --url https://edr-api-desi-c.mdl.nws.noaa.gov \
194
+ --max-examples 3
195
+ ```
196
+
197
+ Results:
198
+
199
+ ```
200
+ Operations: 100 selected / 106 total
201
+ Tested: 47
202
+ Test cases: 1002 generated, 1002 passed
203
+
204
+ No issues found in 49.51s
205
+ ```
206
+
207
+ Use `--stateful` to additionally test job lifecycle endpoints (`/jobs/{jobId}`, `DELETE /jobs/{jobId}`) via POST `/execution` chaining.
208
+
209
+ Add `collection_examples` to your config to supply real `instanceId` values for schemathesis path parameters:
210
+
211
+ ```yaml
212
+ collection_examples:
213
+ my_collection:
214
+ instanceId: "2025-04-02T00:00:00Z"
215
+ ```
216
+
217
+ ### 5. OGC CITE Conformance Testing
218
+
219
+ #### EDR Conformance Testing
220
+
221
+ Run the official OGC API - EDR Part 1 conformance test suite (ets-ogcapi-edr10):
222
+
223
+ ```bash
224
+ oapi-profile-builder cite-test \
225
+ --url https://edr-api-desi-c.mdl.nws.noaa.gov \
226
+ --report ./cite_results
227
+ ```
228
+
229
+ Results:
230
+
231
+ ```
232
+ OGC API - EDR CITE Results
233
+ Passed: 76/84
234
+ Failed: 0
235
+ Skipped: 8
236
+
237
+ ✓ All CITE tests passed.
238
+ ```
239
+
240
+ The tool automatically:
241
+ - Clones and builds ets-ogcapi-edr10 from GitHub on first run
242
+ - Caches Docker image (`ogccite/ets-ogcapi-edr10:local`) for subsequent runs
243
+ - Runs TestNG tests via `docker exec`
244
+ - Supports localhost testing with `--network host` mode
245
+ - Generates JSON report with detailed test results
246
+
247
+ The skipped tests are optional features not implemented by the server.
248
+
249
+ #### Features Conformance Testing
250
+
251
+ Run the official OGC API - Features Part 1 conformance test suite (ets-ogcapi-features10):
252
+
253
+ ```bash
254
+ oapi-profile-builder cite-test-features \
255
+ --url https://api.example.com \
256
+ --report ./cite_features_results
257
+ ```
258
+
259
+ Results:
260
+
261
+ ```
262
+ OGC API - Features CITE Results
263
+ Passed: 639/712
264
+ Failed: 18
265
+ Skipped: 55
266
+
267
+ ✗ 18 test(s) failed.
268
+ ```
269
+
270
+ The tool automatically:
271
+ - Pulls pre-built Docker image (`ogccite/ets-ogcapi-features10:latest`) from Docker Hub
272
+ - Runs TestNG tests via `docker exec`
273
+ - Supports localhost testing with `--network host` mode
274
+ - Generates JSON report with detailed test results
275
+
276
+ The skipped tests are optional features not implemented by the server.
277
+
278
+ ---
279
+
280
+ ## Config Reference
281
+
282
+ ### Top-level fields
283
+
284
+ | Field | Type | Required | Description |
285
+ |---|---|---|---|
286
+ | `name` | `string` | yes | Lowercase identifier using only `a-z`, `0-9`, `_`. Used in OGC URIs and OpenAPI `operationId`s. e.g. `water_gauge` |
287
+ | `title` | `string` | yes | Human-readable profile title |
288
+ | `version` | `string` | no | Profile version. Defaults to `1.0` |
289
+ | `server_url` | `string` | no | Base URL of the live server (for documentation only - not used in profile OpenAPI per OGC API - EDR Part 3) |
290
+ | `collections` | `list` | yes | One or more EDR collections (see below) |
291
+ | `processes` | `list` | no | OGC API Processes to include in the OpenAPI (see below) |
292
+ | `requirements` | `list` | no | Normative requirements (see below) |
293
+ | `abstract_tests` | `list` | no | Conformance tests — each must reference a valid requirement `id` (see below) |
294
+ | `pubsub` | `object` | no | OGC API - EDR Part 2 PubSub configuration (see below) |
295
+ | `collection_examples` | `object` | no | Map of collection id → example parameter values (e.g. `instanceId`) for server validation |
296
+ | `document_metadata` | `object` | no | Metanorma document header fields for PDF compilation (see below) |
297
+ | `required_conformance_classes` | `list[string]` | no | Conformance classes that implementations must declare. Defaults to EDR Core |
298
+ | `extent_requirements` | `object` | no | Profile-level extent restrictions (see below) |
299
+ | `output_formats` | `list` | no | Profile-level output format definitions with schema references (see below) |
300
+ | `collection_id_pattern` | `string` | no | Regex pattern for valid collection IDs |
301
+
302
+ ---
303
+
304
+ ### `collections[]`
305
+
306
+ Each collection uses the [edr-pydantic](https://github.com/KNMI/edr-pydantic) `Collection` schema — the same model an EDR server returns at `/collections/{id}`. Key fields:
307
+
308
+ | Field | Type | Required | Description |
309
+ |---|---|---|---|
310
+ | `id` | `string` | yes | Collection identifier |
311
+ | `title` | `string` | no | Human-readable collection name |
312
+ | `description` | `string` | no | Collection description |
313
+ | `links` | `list` | yes | At minimum a `self` link |
314
+ | `extent.spatial.bbox` | `list` | yes | Bounding box as `[[minLon, minLat, maxLon, maxLat]]` |
315
+ | `extent.spatial.crs` | `string` | yes | CRS URI, typically `http://www.opengis.net/def/crs/OGC/1.3/CRS84` |
316
+ | `data_queries` | `object` | no | Which EDR query types this collection supports |
317
+ | `output_formats` | `list` | no | Supported output format labels e.g. `GeoJSON`, `CoverageJSON` |
318
+ | `parameter_names` | `object` | no | Map of parameter id → `Parameter` object |
319
+
320
+ #### `data_queries`
321
+
322
+ Supported keys: `items` · `position` · `area` · `radius` · `cube` · `trajectory` · `corridor` · `locations` · `instances`
323
+
324
+ ```yaml
325
+ data_queries:
326
+ position:
327
+ link:
328
+ href: https://example.com/collections/water_gauge/position
329
+ rel: data
330
+ variables:
331
+ query_type: position
332
+ output_formats:
333
+ - CoverageJSON
334
+ items:
335
+ link:
336
+ href: https://example.com/collections/water_gauge/items
337
+ rel: data
338
+ variables:
339
+ query_type: items
340
+ output_formats:
341
+ - GeoJSON
342
+ ```
343
+
344
+ #### `parameter_names`
345
+
346
+ **Note:** Per OGC API - EDR Part 3 requirements, all parameters must specify `unit` and `observedProperty`. The tool validates this automatically.
347
+
348
+ ```yaml
349
+ parameter_names:
350
+ gauge_height:
351
+ type: Parameter
352
+ observedProperty:
353
+ label: Gauge Height
354
+ unit:
355
+ label: feet
356
+ symbol: ft
357
+ ```
358
+
359
+ ---
360
+
361
+ ### `extent_requirements`
362
+
363
+ Profile-level extent restrictions per OGC API - EDR Part 3 REQ_extent.
364
+
365
+ | Field | Type | Required | Description |
366
+ |---|---|---|---|
367
+ | `minimum_bbox` | `list[float]` | yes | Minimum spatial bounds `[minLon, minLat, maxLon, maxLat]` |
368
+ | `allowed_crs` | `list[string]` | no | Enumerated list of valid CRS values |
369
+ | `crs_pattern` | `string` | no | Regular expression defining valid CRS string patterns |
370
+ | `allowed_trs` | `list[string]` | no | Enumerated list of valid TRS values |
371
+ | `trs_pattern` | `string` | no | Regular expression defining valid TRS string patterns |
372
+ | `allowed_vrs` | `list[string]` | no | Enumerated list of valid VRS values |
373
+ | `vrs_pattern` | `string` | no | Regular expression defining valid VRS string patterns |
374
+
375
+ **Note:** Either `allowed_crs` or `crs_pattern` must be specified.
376
+
377
+ ```yaml
378
+ extent_requirements:
379
+ minimum_bbox: [-180, -90, 180, 90]
380
+ allowed_crs:
381
+ - "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
382
+ - "http://www.opengis.net/def/crs/EPSG/0/4326"
383
+ allowed_trs:
384
+ - "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian"
385
+ ```
386
+
387
+ ---
388
+
389
+ ### `output_formats[]`
390
+
391
+ Profile-level output format definitions with schema references per OGC API - EDR Part 3 REQ_output-format.
392
+
393
+ | Field | Type | Required | Description |
394
+ |---|---|---|---|
395
+ | `name` | `string` | yes | Format name (e.g., GeoJSON, CoverageJSON) |
396
+ | `media_type` | `string` | yes | MIME type (e.g., application/geo+json) |
397
+ | `schema_ref` | `string` | no | URL to schema definition |
398
+
399
+ ```yaml
400
+ output_formats:
401
+ - name: GeoJSON
402
+ media_type: application/geo+json
403
+ schema_ref: https://geojson.org/schema/FeatureCollection.json
404
+ - name: CoverageJSON
405
+ media_type: application/prs.coverage+json
406
+ schema_ref: https://schemas.opengis.net/ogcapi/edr/1.0/openapi/schemas/coverageJSON.yaml
407
+ ```
408
+
409
+ ---
410
+
411
+ ### `required_conformance_classes[]`
412
+
413
+ Conformance classes that implementations must declare at `/conformance` per OGC API - EDR Part 3 REQ_api.
414
+
415
+ Defaults to:
416
+ ```yaml
417
+ required_conformance_classes:
418
+ - "http://www.opengis.net/spec/ogcapi-edr-1/1.0/conf/core"
419
+ ```
420
+
421
+ Add additional conformance classes as needed:
422
+ ```yaml
423
+ required_conformance_classes:
424
+ - "http://www.opengis.net/spec/ogcapi-edr-1/1.0/conf/core"
425
+ - "http://www.opengis.net/spec/ogcapi-edr-1/1.0/conf/oas30"
426
+ - "http://www.opengis.net/spec/ogcapi-edr-2/1.0/conf/pubsub"
427
+ ```
428
+
429
+ ---
430
+
431
+ ### `processes[]`
432
+
433
+ OGC API Processes to expose in the generated OpenAPI. Each entry produces `/processes/{id}` and `/processes/{id}/execution` paths, plus `/processes`, `/jobs`, `/jobs/{jobId}`, and `/jobs/{jobId}/results`.
434
+
435
+ | Field | Type | Required | Description |
436
+ |---|---|---|---|
437
+ | `id` | `string` | yes | Process identifier e.g. `edr-zarr-difference` |
438
+ | `title` | `string` | no | Human-readable process name |
439
+ | `description` | `string` | no | Process description |
440
+ | `output_content` | `object` | no | OpenAPI content map for the 200 response. Defaults to `application/json` |
441
+
442
+ ```yaml
443
+ processes:
444
+ - id: edr-zarr-difference
445
+ title: EDR Zarr Dataset Difference
446
+ description: Calculates the difference between two EDR Zarr datasets.
447
+ output_content:
448
+ application/zip:
449
+ schema:
450
+ type: object
451
+ ```
452
+
453
+ ---
454
+
455
+ ### `requirements[]`
456
+
457
+ | Field | Type | Required | Description |
458
+ |---|---|---|---|
459
+ | `id` | `string` | yes | Lowercase, hyphen-separated. Must match `^[a-z0-9][a-z0-9\-]*$` |
460
+ | `statement` | `string` | yes | One-sentence normative statement |
461
+ | `parts` | `list[string]` | yes | One or more SHALL/MUST clauses |
462
+
463
+ ```yaml
464
+ requirements:
465
+ - id: position-coveragejson
466
+ statement: The position query SHALL return CoverageJSON.
467
+ parts:
468
+ - The service SHALL provide a /collections/{id}/position endpoint.
469
+ - The response Content-Type SHALL be application/prs.coverage+json.
470
+ ```
471
+
472
+ ---
473
+
474
+ ### `abstract_tests[]`
475
+
476
+ Every `requirement_id` must match an existing requirement `id` — the model validator will reject the profile if not.
477
+
478
+ | Field | Type | Required | Description |
479
+ |---|---|---|---|
480
+ | `id` | `string` | yes | Must equal `requirement_id` |
481
+ | `requirement_id` | `string` | yes | The `id` of the requirement this test validates |
482
+ | `steps` | `list[string]` | yes | Ordered test steps |
483
+
484
+ ```yaml
485
+ abstract_tests:
486
+ - id: position-coveragejson
487
+ requirement_id: position-coveragejson
488
+ steps:
489
+ - Send GET request to /collections/{id}/position?coords=POINT(lon lat).
490
+ - Verify the response Content-Type is application/prs.coverage+json.
491
+ ```
492
+
493
+ ---
494
+
495
+ ### `pubsub`
496
+
497
+ When present, an `asyncapi.yaml` is generated.
498
+
499
+ | Field | Type | Default | Description |
500
+ |---|---|---|---|
501
+ | `broker_host` | `string` | `localhost` | Message broker hostname |
502
+ | `broker_port` | `integer` | `5672` | Broker port (1–65535) |
503
+ | `protocol` | `string` | `amqp` | One of `amqp`, `mqtt`, `kafka` |
504
+ | `filters` | `list` | `[]` | Subscription filters |
505
+
506
+ Each filter: `name` (required), `description` (required), `type` (one of `string`, `number`, `array`, `boolean`, default `string`).
507
+
508
+ ---
509
+
510
+ ### `document_metadata`
511
+
512
+ Controls the Metanorma document header when compiling a PDF with `--pdf`.
513
+
514
+ | Field | Type | Required | Description |
515
+ |---|---|---|---|
516
+ | `doc_number` | `string` | yes | OGC document number e.g. `24-nwsviz` |
517
+ | `doc_subtype` | `string` | yes | One of `implementation`, `best-practice`, `engineering-report` |
518
+ | `editors` | `list[string]` | yes | Editor names |
519
+ | `submitting_orgs` | `list[string]` | yes | Submitting organization names |
520
+ | `keywords` | `list[string]` | no | Document keywords |
521
+ | `copyright_year` | `integer` | no | Defaults to current year |
522
+ | `external_id` | `string` | no | OGC external document URI |
523
+
524
+ ---
525
+
526
+ ## OGC API - EDR Part 3 Compliance
527
+
528
+ This tool implements the requirements of OGC API - EDR Part 3: Service Profiles (draft standard):
529
+
530
+ ### Key Compliance Features
531
+
532
+ 1. **Profile OpenAPI Document** (REQ_publishing)
533
+ - Generated OpenAPI has empty `servers` array (profile is implementation-independent)
534
+ - Landing page schema requires `profile` link relation
535
+ - Profile URI advertised in `x-ogc-profile` info field
536
+
537
+ 2. **Conformance Classes** (REQ_api)
538
+ - `/conformance` endpoint schema specifies required conformance classes
539
+ - Defaults to EDR Core, customizable via `required_conformance_classes`
540
+
541
+ 3. **Parameter Names** (REQ_parameter-names)
542
+ - Validates that all parameters specify `unit` and `observedProperty`
543
+ - Automatically enforced during profile validation
544
+
545
+ 4. **Extent Requirements** (REQ_extent)
546
+ - Profile-level `extent_requirements` specify minimum bounds
547
+ - CRS/TRS/VRS restrictions via enumerated lists or regex patterns
548
+
549
+ 5. **Output Formats** (REQ_output-format)
550
+ - Profile-level `output_formats` with schema references
551
+ - Links to JSON Schema, XML Schema, or format specifications
552
+
553
+ 6. **Pub/Sub** (REQ_pubsub)
554
+ - Automatically adds Part 2 conformance requirement when `pubsub` is present
555
+ - AsyncAPI document specifies channels and payloads
556
+
557
+ ### Profile Types
558
+
559
+ The tool supports both:
560
+ - **Class 1 Profiles**: Restrictive profiles that constrain EDR Core
561
+ - **Class 2 Profiles**: Extended profiles that add custom requirements (e.g., processes)
562
+
563
+ Both remain compliant with EDR Core - extensions are optional, not mandatory.
564
+
565
+ ---
566
+
567
+ ## Programmatic Use
568
+
569
+ ```python
570
+ from oapi_profile_builder.models import ServiceProfile
571
+ from oapi_profile_builder.generate import generate
572
+ from pathlib import Path
573
+
574
+ profile = ServiceProfile.model_validate(config_dict)
575
+ generate(profile, Path("./output"))
576
+ ```
577
+
578
+ ## Repository Structure
579
+
580
+ ```
581
+ ├── src/
582
+ │ └── oapi_profile_builder/
583
+ │ ├── models.py # Authoritative Pydantic schema
584
+ │ ├── generate.py # Validated model → OpenAPI, AsyncAPI, AsciiDoc
585
+ │ ├── compile.py # PDF compilation via metanorma/metanorma Docker image
586
+ │ ├── cite.py # OGC CITE test suite orchestration
587
+ │ └── cli.py # CLI entry point
588
+ ├── examples/
589
+ │ ├── water_gauge.yaml # Minimal example profile config
590
+ │ └── nwsviz_profile.yaml # Full NWSViz profile: 13 collections, 3 processes, PDF metadata
591
+ ├── profile.schema.json # Machine-readable JSON Schema for profile configs
592
+ └── pyproject.toml
593
+ ```
594
+
595
+ ## Standards
596
+
597
+ - OGC API - EDR Part 1: Core
598
+ - OGC API - EDR Part 2: PubSub
599
+ - OGC API - EDR Part 3: Service Profiles (draft)
600
+ - OGC API - Processes Part 1
601
+ - OpenAPI 3.0 / AsyncAPI 3.0
602
+ - Metanorma/AsciiDoc documentation format
603
+
604
+ ## License
605
+
606
+ MIT — See [LICENSE](LICENSE) for details.
607
+
608
+ ## Contact
609
+
610
+ - **Author**: Shane Mill (NOAA/NWS/MDL)
611
+ - **Email**: shane.mill@noaa.gov
612
+ - **Issues**: https://github.com/ShaneMill1/OGC-API-Service-Profile-Builder/issues