openapi-sourcetools 0.12.0 → 0.13

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 830a477c8647ad883e5a6619767f19cfae7c2d9b9baa100b0e10bdd911ef0fc1
4
- data.tar.gz: 9e4a89d44c344c0099f826bf08c459b2eaa448e4d8b76b982e7ee8b7146199b2
3
+ metadata.gz: edfaf16a37a66c6f64cddb2580cd5a85c6daf21937a403358ea03d28b35a47d6
4
+ data.tar.gz: 6e81dcc098183b9547fbc9dd956fb0f11e2ccf556dbf7d8baaf73821bfe9334e
5
5
  SHA512:
6
- metadata.gz: eef01e1c3095d747213533d8ceeb4d0ba4107970b6541632b92b9ffaa0a0202d583ec6934f2af4af3dbf0dff4d0344e053bbf9201d8128cd474406fe11a8b308
7
- data.tar.gz: 92fcbc4c9128e213ee4d67d7eebe69c5de9f5ef2576a0efdeae33b2f512a786d586e0eebe903fc932b43f59475ecbd780d925263adbd9f5039b113292db8ae97
6
+ metadata.gz: 13c4fd8d5a67c9af5072a8d7e29bee3bec6a82e0a7e9a39284e492871cfb7f9dfca736293d2218df5d91efa2fc200ec0c90b0e8887e0991793dec11a714a692f
7
+ data.tar.gz: 22ff4d3552d478a72d1e7b5c5d413e4de0c912479da1469ebbb3afa3311e23bd3cb769c69ec40547448e63cd4fbde038383cfef1122fb4a3a8de5297d14d1134
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2026 Ismo Kärkkäinen
5
+ # Licensed under Universal Permissive License. See LICENSE.txt.
6
+
7
+ require_relative '../lib/openapi/sourcetools/apiobjects'
8
+ require_relative '../lib/openapi/sourcetools/common'
9
+ require 'optparse'
10
+ require 'yaml'
11
+
12
+
13
+ def main
14
+ input_name = nil
15
+ output_name = nil
16
+ path = %w[components mediaTypes]
17
+ components = OpenAPISourceTools::ApiObjects::Components.new(path, 'MediaType')
18
+ replacer = OpenAPISourceTools::ApiObjects::ValueSubValueReplacer.new('content', components)
19
+
20
+ parser = OptionParser.new do |opts|
21
+ opts.summary_indent = ' '
22
+ opts.summary_width = 26
23
+ opts.banner = 'Usage: openapi-addmediatypes [options]'
24
+ opts.separator ''
25
+ opts.separator 'Options:'
26
+ opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
27
+ input_name = f
28
+ end
29
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout.') do |f|
30
+ output_name = f
31
+ end
32
+ components.add_options(opts)
33
+ opts.on('-h', '--help', 'Print this help and exit.') do
34
+ $stdout.puts %(#{opts}
35
+
36
+ Loads API document in OpenAPI format and moves media types under components
37
+ and replaces the original with reference.
38
+
39
+ #{components.help}
40
+ )
41
+ exit 0
42
+ end
43
+ end
44
+ parser.order!
45
+
46
+ doc = OpenAPISourceTools::Common.load_source(input_name)
47
+ return 2 if doc.nil?
48
+
49
+ components.items = doc.dig(*path) || {}
50
+ replacer.replace(doc.dig('components', 'headers') || {})
51
+ replacer.replace(doc.dig('components', 'responses') || {})
52
+ replacer.replace(doc.dig('components', 'parameters') || {})
53
+ replacer.replace(doc.fetch('paths', {}))
54
+ OpenAPISourceTools::Common.bury(doc, path, components.items) unless components.items.empty?
55
+
56
+ OpenAPISourceTools::Common.dump_result(output_name, doc, 3)
57
+ end
58
+
59
+ exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2026 Ismo Kärkkäinen
5
+ # Licensed under Universal Permissive License. See LICENSE.txt.
6
+
7
+ require_relative '../lib/openapi/sourcetools/apiobjects'
8
+ require_relative '../lib/openapi/sourcetools/common'
9
+ require 'optparse'
10
+ require 'yaml'
11
+
12
+
13
+ def main
14
+ input_name = nil
15
+ output_name = nil
16
+ path = %w[components requestBodies]
17
+ components = OpenAPISourceTools::ApiObjects::Components.new(path, 'RequestBody')
18
+ replacer = OpenAPISourceTools::ApiObjects::ValueReplacer.new('requestBody', components)
19
+
20
+ parser = OptionParser.new do |opts|
21
+ opts.summary_indent = ' '
22
+ opts.summary_width = 26
23
+ opts.banner = 'Usage: openapi-addrequestbodies [options]'
24
+ opts.separator ''
25
+ opts.separator 'Options:'
26
+ opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
27
+ input_name = f
28
+ end
29
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout.') do |f|
30
+ output_name = f
31
+ end
32
+ components.add_options(opts)
33
+ opts.on('-h', '--help', 'Print this help and exit.') do
34
+ $stdout.puts %(#{opts}
35
+
36
+ Loads API document in OpenAPI format and moves request body under components
37
+ and replaces the original with reference.
38
+
39
+ #{components.help}
40
+ )
41
+ exit 0
42
+ end
43
+ end
44
+ parser.order!
45
+
46
+ doc = OpenAPISourceTools::Common.load_source(input_name)
47
+ return 2 if doc.nil?
48
+
49
+ components.items = doc.dig(*path) || {}
50
+ replacer.replace(doc.dig('components', 'pathItems') || {})
51
+ replacer.replace(doc.fetch('paths', {}))
52
+ OpenAPISourceTools::Common.bury(doc, path, components.items) unless components.items.empty?
53
+
54
+ OpenAPISourceTools::Common.dump_result(output_name, doc, 3)
55
+ end
56
+
57
+ exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
@@ -16,6 +16,14 @@ def remove_subitem(obj, path)
16
16
  parent.delete(path.last) || {}
17
17
  end
18
18
 
19
+ def replace_child_inlines(obj, components)
20
+ return true unless obj.is_a?(Hash)
21
+ obj.keys.sort!.each do |name|
22
+ return false unless replace_inlines(obj[name], components)
23
+ end
24
+ true
25
+ end
26
+
19
27
  def replace_inlines(obj, components, top_level_name = nil)
20
28
  if obj.is_a?(Array)
21
29
  obj.each do |o|
@@ -29,22 +37,30 @@ def replace_inlines(obj, components, top_level_name = nil)
29
37
  return true
30
38
  end
31
39
  # Is inlined, process parts recursively.
32
- %w[allOf anyOf oneOf items prefixItems additionalProperties unevaluatedItems
33
- unevaluatedProperties if then else not
34
- ].each do |prop_name|
35
- return false unless replace_inlines(obj[prop_name], components)
40
+ looks_like_type = false
41
+ # Array-valued.
42
+ OpenAPISourceTools::Schemas::ARRAY_VALUED.each do |prop_name|
43
+ value = obj[prop_name]
44
+ next unless value.is_a?(Array)
45
+ return false unless replace_inlines(value, components)
46
+ looks_like_type = true
47
+ end
48
+ # Other-valued things.
49
+ OpenAPISourceTools::Schemas::OTHER_VALUED.each do |prop_name|
50
+ value = obj[prop_name]
51
+ next if value.nil? || value.is_a?(Array)
52
+ return false unless replace_inlines(value, components)
53
+ looks_like_type = true
36
54
  end
37
- %w[properties patternProperties dependentSchemas].each do |prop_name|
55
+ # Keys of these have object values that should be processed.
56
+ OpenAPISourceTools::Schemas::KEY2SCHEMA.each do |prop_name|
38
57
  props = obj[prop_name]
39
- next if props.nil?
40
- props.keys.sort!.each do |name|
41
- return false unless replace_inlines(props[name], components)
42
- end
58
+ next if props.nil? || !props.is_a?(Hash)
59
+ return false unless replace_child_inlines(props, components)
60
+ looks_like_type = true
43
61
  end
44
- looks_like_type = false
45
62
  # required is omitted since it is in schema and in parameters.
46
- %w[type items prefixItems properties patternProperties additionalProperties
47
- unevaluatedItems unevaluatedProperties enum const minimum maximum multipleOf
63
+ %w[type enum const minimum maximum multipleOf
48
64
  exclusiveMinimum exclusiveMaximum maxLength minLength pattern maxItems minItems
49
65
  uniqueItems maxContains minContains maxProperties minProperties propertyNames
50
66
  dependentRequired format contentEncoding contentMediaType contentSchema
@@ -56,8 +72,8 @@ def replace_inlines(obj, components, top_level_name = nil)
56
72
  end
57
73
  unless looks_like_type
58
74
  # Some kind of intermediate-level object.
59
- (obj.keys.sort! { |a, b| a.to_s <=> b.to_s }).each do |key|
60
- next if key == 'securitySchemes'
75
+ obj.keys.sort! { |a, b| a.to_s <=> b.to_s }.each do |key|
76
+ next if key == 'securitySchemes' || OpenAPISourceTools::Schemas::IGNORED.include?(key) || OpenAPISourceTools::Schemas::HANDLED.include?(key)
61
77
  return false unless replace_inlines(obj[key], components)
62
78
  end
63
79
  return true
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2024-2026 Ismo Kärkkäinen
5
+ # Licensed under Universal Permissive License. See LICENSE.txt.
6
+
7
+ require_relative '../lib/openapi/sourcetools/common'
8
+ require_relative '../lib/openapi/sourcetools/config'
9
+ require_relative '../lib/openapi/sourcetools/gen'
10
+ require 'optparse'
11
+ include OpenAPISourceTools
12
+
13
+
14
+ def main
15
+ prefix = nil
16
+ keys = nil
17
+ read = false
18
+
19
+ parser = OptionParser.new do |opts|
20
+ opts.summary_indent = ' '
21
+ opts.summary_width = 26
22
+ opts.banner = 'Usage: openapi-generate-checkconfig [options]'
23
+ opts.separator ''
24
+ opts.separator 'Options:'
25
+ opts.on('-c', '--config NAME', 'Use config prefix NAME. (config:NAME)') do |c|
26
+ prefix = c
27
+ end
28
+ opts.on('-s', '--separator STRING', 'Use separator STRING. (separator:STRING)') do |s|
29
+ Gen.separator = s unless s.empty?
30
+ end
31
+ opts.on('-r', '--[no-]read', "Show if file is read by read_contents, default #{Common.yesno(read)}.") do |b|
32
+ read = b
33
+ end
34
+ opts.on('-k', '--keys KEYS', 'Show all values of KEYS.') do |ks|
35
+ keys = ks.split('/').reject(&:empty?) unless ks.empty?
36
+ keys = nil if !keys.nil? && keys.empty?
37
+ end
38
+ opts.on('-h', '--help', 'Print this help and exit.') do
39
+ $stdout.puts %(#{opts}
40
+ Displays configurations files that are found using specific prefix and an
41
+ optional separator character or string.
42
+
43
+ If KEYS, separated by "/", is given, the value of that key path is displayed.
44
+ That and read check require the configuration files to be YAML/JSON.
45
+ )
46
+ exit 0
47
+ end
48
+ end
49
+ parser.order!
50
+
51
+ return Common.aargh('Config must be given.', 1) if prefix.nil?
52
+ cfgs = ConfigLoader.find_files(name_prefix: prefix)
53
+ ConfigLoader.read_contents(cfgs) if read || !keys.nil?
54
+ root_dir, _p = ConfigLoader.prepare_prefix(prefix, Gen.wd)
55
+ drop = root_dir.size + 1
56
+ cfgs.each do |cfg|
57
+ ks = cfg.keys.join('/')
58
+ info = "#{cfg.root}: #{ks.empty? ? '(root)' : ks}, #{cfg.path[drop..]}"
59
+ ki = nil
60
+ unless keys.nil? || cfg.content.nil?
61
+ v = cfg.content.dig(*keys)
62
+ ki = v.to_s unless v.nil?
63
+ end
64
+ ri = read ? !cfg.content.nil? : nil
65
+ next if (read || !keys.nil?) && ki.nil? && ri.nil?
66
+ puts info
67
+ puts " #{ki}" unless ki.nil?
68
+ puts " File is #{ri ? '' : 'not '}read." unless ri.nil?
69
+ end
70
+ 0
71
+ end
72
+
73
+ exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2026 Ismo Kärkkäinen
5
+ # Licensed under Universal Permissive License. See LICENSE.txt.
6
+
7
+ require_relative '../lib/openapi/sourcetools/apiobjects'
8
+ require_relative '../lib/openapi/sourcetools/common'
9
+ require 'optparse'
10
+ include OpenAPISourceTools
11
+
12
+
13
+ def mixture?(schema)
14
+ has_logic = false
15
+ has_other = false
16
+ schema.each_key do |key|
17
+ if OpenAPISourceTools::ApiObjects::LOGIC_KEYS.member?(key)
18
+ has_logic = true
19
+ return true if has_other
20
+ elsif !OpenAPISourceTools::ApiObjects::DOCUMENT_KEYS.include?(key)
21
+ has_other = true
22
+ return true if has_logic
23
+ end
24
+ end
25
+ has_logic && has_other
26
+ end
27
+
28
+ def split(name, schema, added_schemas, schema_path)
29
+ logic = {}
30
+ other = {}
31
+ schema.each do |key, value|
32
+ if OpenAPISourceTools::ApiObjects::LOGIC_KEYS.member?(key)
33
+ logic[key] = value
34
+ else
35
+ other[key] = value
36
+ end
37
+ end
38
+ logic_name = "#{name}Logic"
39
+ other_name = "#{name}Other"
40
+ added_schemas[logic_name] = logic
41
+ added_schemas[other_name] = other
42
+ schema.clear
43
+ schema['allOf'] = [
44
+ { '$ref' => "#{schema_path}/#{logic_name}" },
45
+ { '$ref' => "#{schema_path}/#{other_name}" }
46
+ ]
47
+ end
48
+
49
+ def main
50
+ input_name = nil
51
+ output_name = nil
52
+ schema_path = '#/components/schemas'
53
+
54
+ parser = OptionParser.new do |opts|
55
+ opts.summary_indent = ' '
56
+ opts.summary_width = 26
57
+ opts.banner = 'Usage: openapi-logicsplit [options]'
58
+ opts.separator ''
59
+ opts.separator 'Options:'
60
+ opts.on('-i', '--input FILE', 'Read API spec from FILE, not stdin.') do |f|
61
+ input_name = f
62
+ end
63
+ opts.on('-o', '--output FILE', 'Output to FILE, not stdout.') do |f|
64
+ output_name = f
65
+ end
66
+ opts.on('-h', '--help', 'Print this help and exit.') do
67
+ $stdout.puts %(#{opts}
68
+
69
+ Loads API document in OpenAPI format and splits each schema with a mixture of
70
+ logic keywords and other keywords so that the schema has only allOf with
71
+ references to logic and other schema. The aim is to simplify code generation.
72
+
73
+ The openapi-addschemas must have been run. Processes "#{schema_path}".
74
+ )
75
+ return 0
76
+ end
77
+ end
78
+ parser.order!
79
+
80
+ doc = Common.load_source(input_name)
81
+ return 2 if doc.nil?
82
+
83
+ path = schema_path.split('/').reject { |x| x == '#' || x.empty? }
84
+ schemas = doc.dig(*path)
85
+ added_schemas = {}
86
+ unless schemas.nil?
87
+ schemas.keys.sort.each do |name|
88
+ s = schemas[name]
89
+ split(name, s, added_schemas, schema_path) if mixture?(s)
90
+ end
91
+ schemas.merge!(added_schemas)
92
+ end
93
+ Common.dump_result(output_name, doc, 3)
94
+ end
95
+
96
+ exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)
data/bin/openapi-order CHANGED
@@ -53,6 +53,7 @@ def default_order_data(order_key, regexp_prefix)
53
53
  server: *ServerObject
54
54
  parameters:
55
55
  #{order_key}: &ParameterObject_order
56
+ - required: '>' # True before false.
56
57
  - name
57
58
  - in
58
59
  '*': &ParameterObject
@@ -213,9 +214,10 @@ def default_order_data(order_key, regexp_prefix)
213
214
  - parameters
214
215
  - '*'
215
216
  - additionalOperations
217
+ - re:^x-openapi-sourcetools.*$
216
218
  '*':
217
219
  #{order_key}: &OperationObject
218
- - '$ref'
220
+ - '\$ref'
219
221
  - operationId
220
222
  - summary
221
223
  - description
@@ -348,7 +350,7 @@ end
348
350
 
349
351
  # This needs to extract all path/order pairs, store and sort.
350
352
 
351
- def extract_orders(path, order, collection, order_key, regexp_prefix)
353
+ def extract_orders(path, order, collection, order_key, regexp_prefix, visited)
352
354
  return true unless order.is_a?(Hash)
353
355
  order.each do |key, value|
354
356
  if key == order_key
@@ -356,9 +358,13 @@ def extract_orders(path, order, collection, order_key, regexp_prefix)
356
358
  OpenAPISourceTools::Common.aargh("Not an array: #{path.join('.')}.#{order_key} =\n#{YAML.dump(value)}")
357
359
  return false
358
360
  end
361
+ return true if visited.member?(value.object_id)
362
+ visited.add(value.object_id)
359
363
  value.each_with_index do |v, k|
360
- next if v.is_a?(String)
361
- OpenAPISourceTools::Common.aargh("Not a string: #{path}.#{order_key}[#{k}] =\n#{YAML.dump(v)}")
364
+ ov = OpenAPISourceTools::Ordering::Order.ordervalue(v)
365
+ value[k] = ov
366
+ next unless ov.nil?
367
+ OpenAPISourceTools::Common.aargh("Not valid order value: #{path}.#{order_key}[#{k}] =\n#{YAML.dump(v)}")
362
368
  return false
363
369
  end
364
370
  key_path = OpenAPISourceTools::Ordering::KeyPath.new(path, regexp_prefix)
@@ -367,7 +373,7 @@ def extract_orders(path, order, collection, order_key, regexp_prefix)
367
373
  else
368
374
  key = key.to_s unless key.is_a?(String)
369
375
  path.push(key)
370
- return false unless extract_orders(path, value, collection, order_key, regexp_prefix)
376
+ return false unless extract_orders(path, value, collection, order_key, regexp_prefix, visited)
371
377
  path.pop
372
378
  end
373
379
  end
@@ -472,14 +478,17 @@ def main
472
478
  regexp_prefix = p
473
479
  end
474
480
  opts.on('-h', '--help', 'Print this help and exit.') do
475
- $stdout.puts %(#{opts}
481
+ $stdout.puts <<EOF
482
+ #{opts}
476
483
  Loads YAML document and applies ordering to object keys, and arrays with
477
484
  objects, when an order matches the key path to object.
478
485
 
479
486
  Can be used on generic YAML or JSON files. Default order is for OpenAPI
480
487
  documents.
481
488
 
482
- The order instructions key "#{order_key}" value must be an array of strings.
489
+ The order instructions key "#{order_key}" value must be an array of strings
490
+ or single-item map from string to "<" or ">" to indicate ascending (default)
491
+ or descending order. Hence any 'key' is same as 'key: "<"'.
483
492
  The values can be any combination of the following:
484
493
  - Literal key name.
485
494
  - Regular expression starting with "#{regexp_prefix}".
@@ -492,13 +501,13 @@ position are sorted within that position.
492
501
  The path to the "#{order_key}" can have any combination of the following:
493
502
  - Literal key name.
494
503
  - Regular expression starting with "#{regexp_prefix}".
495
- - "*" that matches one key of any value.
496
- - "**" that matches one or more keys of any value.
504
+ - "*" that matches one key of any content.
505
+ - "**" that matches one or more keys of any content.
497
506
  The path is used to determine if a value in the document should be ordered
498
507
  using the instructions. At top level the path is empty and applies to the root
499
508
  of the document. Long paths are considered to be more specific than short
500
509
  paths, because they supposedly contain more literal keys or limiting regular
501
- expressions so thay are intended for a specific case. Of paths of same length
510
+ expressions so they are intended for a specific case. Of paths of same length
502
511
  one with "**" is considered less specific than one without.
503
512
 
504
513
  The path to a value in the document is matched against paths to "#{order_key}"
@@ -538,7 +547,7 @@ so provide it if you want to keep it. Useful for readability.
538
547
 
539
548
  When reading an order file, --key and --regexp are over-ridden by root-level
540
549
  object "#{metadata}" keys "#{metadata_key}" and "#{metadata_regexp}" values.
541
- )
550
+ EOF
542
551
  exit 0
543
552
  end
544
553
  end
@@ -561,7 +570,7 @@ object "#{metadata}" keys "#{metadata_key}" and "#{metadata_regexp}" values.
561
570
  order_key = md.fetch(metadata_key, order_key)
562
571
  regexp_prefix = md.fetch(metadata_regexp, regexp_prefix)
563
572
  path_orders = []
564
- return 4 unless extract_orders([], order, path_orders, order_key, regexp_prefix)
573
+ return 4 unless extract_orders([], order, path_orders, order_key, regexp_prefix, Set.new)
565
574
  path_orders.sort!.reverse! # Longest key pattern sequence first.
566
575
  order_document(path_orders, [], doc)
567
576
  doc = formats[format].call(doc, line_width)
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2026 Ismo Kärkkäinen
5
+ # Licensed under Universal Permissive License. See LICENSE.txt.
6
+
7
+ require_relative '../lib/openapi/sourcetools/common'
8
+ require_relative '../lib/openapi/sourcetools/schemaio'
9
+ require 'optparse'
10
+ include OpenAPISourceTools
11
+
12
+
13
+ # Functions related to saving the output.
14
+
15
+ def to_json(doc, _line_width)
16
+ JSON.dump(doc)
17
+ end
18
+
19
+ def to_pretty_json(doc, _line_width)
20
+ JSON.pretty_generate(doc)
21
+ end
22
+
23
+ def to_yaml(doc, line_width)
24
+ YAML.dump(doc, line_width: line_width)
25
+ end
26
+
27
+ def main
28
+ input_file = nil
29
+ schema_file = nil
30
+ output_file = nil
31
+ name = nil
32
+ refs = true
33
+ format = 'yaml'
34
+ formats = {
35
+ 'yaml' => method(:to_yaml),
36
+ 'json' => method(:to_json),
37
+ 'pretty_json' => method(:to_pretty_json)
38
+ }
39
+ line_width = 80
40
+ doc_paths = :openapi
41
+ schema_paths = :jsonschema
42
+ drops = []
43
+
44
+ parser = OptionParser.new do |opts|
45
+ opts.summary_indent = ' '
46
+ opts.summary_width = 26
47
+ opts.banner = 'Usage: openapi-schema-in [options]'
48
+ opts.separator ''
49
+ opts.separator 'Options:'
50
+ opts.on('-i', '--input FILE', 'Read input from FILE, not stdin.') do |f|
51
+ input_file = f
52
+ end
53
+ opts.on('-s', '--schema FILE', 'Read schema from FILE, not stdin.') do |f|
54
+ schema_file = f
55
+ end
56
+ opts.on('-o', '--output FILE', 'Write result to FILE, not stdout.') do |f|
57
+ output_file = f
58
+ end
59
+ opts.on('-n', '--name NAME', 'Use schema NAME.') do |n|
60
+ name = n
61
+ end
62
+ opts.on('--[no-]refs', "Include referred schemas (default #{Common.yesno(refs)}).") do |r|
63
+ refs = r
64
+ end
65
+ opts.on('-f', '--format FORMAT', "Document output format: #{formats.keys.sort!.join(', ')}.") do |fmt|
66
+ format = fmt
67
+ end
68
+ opts.on('-w', '--width WIDTH', "Set YAML line width, default: #{line_width}") do |w|
69
+ line_width = w.to_i
70
+ end
71
+ opts.on('--inputpaths DATA', 'Input path data.') do |data|
72
+ doc_paths = data
73
+ end
74
+ opts.on('--schemapaths DATA', 'Schema path data.') do |data|
75
+ schema_paths = data
76
+ end
77
+ opts.on('-d', '--drop KEY', 'Drop KEY from chosen schema.') do |k|
78
+ drops.push(k)
79
+ end
80
+ opts.on('-h', '--help', 'Print this help and exit.') do
81
+ $stdout.puts <<EOF
82
+ #{opts}
83
+ Takes schema with name and places it into document.
84
+
85
+ The document and schema paths DATA is either a file name, internal data name,
86
+ or a JSON-encoded object containing paths data. Internal data names are:
87
+ #{SchemaIO::BUILTINS.keys.sort!.join(', ')}
88
+
89
+ Depending on input, schema NAME is used to find and store the schema in the
90
+ location indicated by DATA for input and output.
91
+
92
+ DATA values default to input and output being OpenAPI format, schema file is
93
+ JSON schema with the named schema at root.
94
+
95
+ Dropping keys can be used to remove for example "$schema" and "$id" keys from
96
+ the chosen schema, if present.
97
+ EOF
98
+ return 0
99
+ end
100
+ end
101
+ parser.order!
102
+
103
+ drops.sort!.uniq! unless drops.size < 2
104
+
105
+ return Common.aargh('Cannot read both document and schema from stdin.', 1) if input_file.nil? && schema_file.nil?
106
+ return Common.aargh('Schema name must be given.', 1) if name.nil?
107
+
108
+ return Common.aargh("Unknown format: #{format}", 1) unless formats.key?(format)
109
+
110
+ dp = SchemaIO.key_data(doc_paths)
111
+ return Common.aargh("Not file or internal format name or format as JSON object: #{doc_paths}", 2) if dp.nil?
112
+ sp = SchemaIO.key_data(schema_paths)
113
+ return Common.aargh("Not file or internal format name or format as JSON object: #{schema_paths}", 2) if sp.nil?
114
+
115
+ return Common.aargh(%(Name "#{name}" has document path separator "#{dp.separator}"), 1) if name.include?(dp.separator)
116
+ return Common.aargh(%(Name "#{name}" has schema path separator "#{sp.separator}"), 1) if name.include?(sp.separator)
117
+
118
+ doc = Common.load_source(input_file)
119
+ return 2 if doc.nil?
120
+ return Common.aargh('Document is not an object.', 2) unless doc.is_a?(Hash)
121
+ schema = Common.load_source(schema_file)
122
+ return 2 if schema.nil?
123
+ return Common.aargh('Schema is not an object.', 2) unless schema.is_a?(Hash)
124
+
125
+ tgt = SchemaIO::SchemaAccess.new(dp, doc)
126
+ src = SchemaIO::SchemaAccess.new(sp, schema)
127
+
128
+ desired = src.find_named_schema(name)
129
+ return Common.aargh("Schema not found: #{name}", 1) if desired.nil?
130
+ drops.each { |k| desired.delete(k) }
131
+ tgt.put_named_schema(name, desired)
132
+
133
+ if refs
134
+ refd = src.find_referenced_schemas(name, desired)
135
+ refd.each { |n, s| tgt.put_referenced_schema(n, s) }
136
+ end
137
+ SchemaIO.alter_refs(tgt.document, src, tgt) # After referenced schemas have been found.
138
+
139
+ out = formats[format].call(tgt.document, line_width)
140
+ Common.dump_result(output_file, out, 3)
141
+ end
142
+
143
+ exit(main) if File.basename(__FILE__) == File.basename($PROGRAM_NAME)