1
1
#!/usr/bin/env python3
2
2
from __future__ import annotations
3
3
4
+ import argparse
4
5
import logging
6
+ import os
5
7
import re
6
8
from builtins import isinstance
7
9
from collections import deque
10
12
from urllib .parse import urlparse
11
13
12
14
from jsonpointer import resolve_pointer
13
- from ogc .na .util import is_url , load_yaml
15
+ from ogc .na .util import is_url , load_yaml , dump_yaml
14
16
15
17
from ogc .bblocks .util import load_file_cached , PathOrUrl
16
18
@@ -302,9 +304,15 @@ def schema_to_oas30(schema_fn: Path, schema_url: str, bbr: BuildingBlockRegister
302
304
}
303
305
304
306
305
- def oas31_to_oas30 (document : dict , document_location : PathOrUrl | str , bbr : BuildingBlockRegister | None ,
307
+ def oas31_to_oas30 (document : dict , document_location : PathOrUrl | str , bbr : BuildingBlockRegister | None = None ,
306
308
x_defs_path = '/x-defs' , target_version = '3.0.3' ):
307
309
310
+ if not isinstance (document , dict ):
311
+ if isinstance (document , Path ) or not is_url (document ):
312
+ document = load_yaml (filename = document )
313
+ else :
314
+ document = load_yaml (url = document )
315
+
308
316
# == 1. Bundle
309
317
if x_defs_path [0 ] != '/' :
310
318
x_defs_path = '/' + x_defs_path
@@ -430,6 +438,12 @@ def fn(subschema: dict[str, Any], parent_is_properties=False, property=None, **k
430
438
def process_path_item_object (o : dict ):
431
439
if not o :
432
440
return
441
+
442
+ ref = o .get ('$ref' )
443
+ if ref :
444
+ process_path_item_object (resolve_pointer (root_schema , ref [1 :]))
445
+ return
446
+
433
447
for op_key in OAS_OPERATION_KEYS :
434
448
operation = o .get (op_key )
435
449
if operation :
@@ -442,25 +456,58 @@ def process_path_item_object(o: dict):
442
456
def process_operation_object (o : dict ):
443
457
if not o :
444
458
return
445
- for parameter in o .get ('parameters' , []):
446
- resolve_parameter (parameter )
447
- process_schema_object (parameter )
448
- process_content_object (parameter )
459
+
460
+ ref = o .get ('$ref' )
461
+ if ref :
462
+ process_operation_object (resolve_pointer (root_schema , ref [1 :]))
463
+ return
464
+
465
+ parameters = o .get ('parameters' )
466
+ if isinstance (parameters , dict ): # $ref!
467
+ parameters = resolve_pointer (root_schema , parameters ['$ref' ][1 :])
468
+ if parameters :
469
+ for parameter in parameters :
470
+ resolve_parameter (parameter )
471
+ process_schema_object (parameter )
472
+ process_content_object (parameter )
449
473
450
474
process_content_object (o .get ('requestBody' ))
451
475
452
- for response in o .get ('responses' , {}).values ():
453
- process_content_object (response )
454
- for header in response .get ('headers' , {}).values ():
455
- process_schema_object (header )
476
+ responses = o .get ('responses' )
477
+ if responses :
478
+ ref = responses .get ('$ref' )
479
+ if ref :
480
+ responses = resolve_pointer (root_schema , ref [1 :])
481
+ if responses :
482
+ for response in o .get ('responses' , {}).values ():
483
+ process_content_object (response )
484
+ headers = response .get ('headers' )
485
+ if headers :
486
+ ref = responses .get ('$ref' )
487
+ if ref :
488
+ headers = resolve_pointer (root_schema , ref [1 :])
489
+ if headers :
490
+ for header in headers .values ():
491
+ process_schema_object (header )
456
492
457
493
for callback in o .get ('callbacks' , {}).values ():
458
494
process_operation_object (callback )
459
495
460
496
def process_content_object (o : dict ):
461
497
if not o :
462
498
return
499
+
500
+ ref = o .get ('$ref' )
501
+ if ref :
502
+ process_content_object (resolve_pointer (root_schema , ref [1 :]))
503
+ return
504
+
463
505
content = o .get ('content' )
506
+ if content :
507
+ ref = content .get ('$ref' )
508
+ if ref :
509
+ content = resolve_pointer (root_schema , ref [1 :])
510
+
464
511
if content :
465
512
for schema_object in content .values ():
466
513
# remove description because 3.0 doesn't support it
@@ -515,3 +562,40 @@ def process_document():
515
562
root_schema ['openapi' ] = target_version
516
563
517
564
return root_schema
565
+
566
+
567
+ def _main ():
568
+ parser = argparse .ArgumentParser (
569
+ description = 'Downcompiles OpenApi 3.1 documents to 3.0' ,
570
+ )
571
+
572
+ parser .add_argument (
573
+ 'document' ,
574
+ help = 'Document to downcompile' ,
575
+ )
576
+
577
+ parser .add_argument (
578
+ '--url' ,
579
+ help = 'Canonical URL of document' ,
580
+ )
581
+
582
+ parser .add_argument (
583
+ '-o' ,
584
+ '--output' ,
585
+ help = 'Output file'
586
+ )
587
+
588
+ args = parser .parse_args ()
589
+
590
+ document_location = PathOrUrl (args .url or args .document )
591
+
592
+ result = oas31_to_oas30 (args .document , document_location )
593
+
594
+ if not args .output :
595
+ print (dump_yaml (result ))
596
+ else :
597
+ dump_yaml (result , args .output )
598
+
599
+
600
+ if __name__ == '__main__' :
601
+ _main ()
0 commit comments