tomograph 2.5.3 → 3.1.1
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 +5 -5
- data/.github/workflows/ruby.yml +33 -0
- data/.rubocop.yml +1 -1
- data/.ruby-version +1 -1
- data/.tool-versions +1 -1
- data/CHANGELOG.md +36 -0
- data/README.md +296 -175
- data/exe/tomograph +15 -48
- data/lib/tomograph/api_blueprint/crafter/yaml.rb +5 -9
- data/lib/tomograph/api_blueprint/crafter/yaml/action.rb +2 -2
- data/lib/tomograph/api_blueprint/drafter_4/yaml.rb +5 -9
- data/lib/tomograph/api_blueprint/drafter_4/yaml/action.rb +2 -2
- data/lib/tomograph/api_blueprint/json_schema.rb +2 -2
- data/lib/tomograph/openapi/openapi2.rb +91 -0
- data/lib/tomograph/openapi/openapi3.rb +118 -0
- data/lib/tomograph/tomogram.rb +12 -14
- data/lib/tomograph/tomogram/action.rb +4 -4
- data/lib/tomograph/version.rb +1 -1
- data/tomograph.gemspec +10 -10
- metadata +44 -59
- data/.travis.yml +0 -5
- data/lib/tomograph/api_blueprint/drafter_3/yaml.rb +0 -128
- data/lib/tomograph/api_blueprint/drafter_3/yaml/action.rb +0 -65
data/exe/tomograph
CHANGED
@@ -12,11 +12,10 @@ include Methadone::CLILogging
|
|
12
12
|
|
13
13
|
version Tomograph::VERSION
|
14
14
|
description 'Converts API Blueprint to JSON Schema'
|
15
|
-
on('-d DRAFTER_VERSION', '--drafter', 'Choose drafter version: crafter,
|
16
|
-
on('-f INPUT_FORMAT', '--format', 'Force input format: "apib" or "yaml". Default: detect by file extension.')
|
15
|
+
on('-d DRAFTER_VERSION', '--drafter', 'Choose drafter version: crafter or 4, or OpenAPI version: openapi2 or openapi3. Default: use drafter v.4.')
|
17
16
|
on('--exclude-description', 'Exclude "description" keys.')
|
18
17
|
on('--split', 'Split output into files by method.')
|
19
|
-
arg :input, 'path/to/doc.
|
18
|
+
arg :input, 'path/to/doc.yaml (API Elements)'
|
20
19
|
arg :output, 'path/to/doc.json or path/to/dir if --split is used.'
|
21
20
|
|
22
21
|
def prune!(obj, unwanted_key)
|
@@ -28,36 +27,18 @@ def prune!(obj, unwanted_key)
|
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
31
|
-
def guess_format(opt_format, input)
|
32
|
-
case opt_format && opt_format.downcase
|
33
|
-
when 'apib'
|
34
|
-
:apib
|
35
|
-
when 'yaml'
|
36
|
-
:yaml
|
37
|
-
when nil
|
38
|
-
case File.extname(input).downcase
|
39
|
-
when '.apib'
|
40
|
-
:apib
|
41
|
-
when '.yaml', '.yml'
|
42
|
-
:yaml
|
43
|
-
else
|
44
|
-
fail 'Unsupported input file extension!'
|
45
|
-
end
|
46
|
-
else
|
47
|
-
fail 'Unsupported input format!'
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
30
|
def choose_drafter(opt_parser)
|
52
31
|
case opt_parser
|
53
32
|
when 'crafter'
|
54
33
|
:crafter
|
55
|
-
when '3'
|
56
|
-
:drafter_3
|
57
34
|
when '4'
|
58
35
|
:drafter_4
|
36
|
+
when 'openapi2'
|
37
|
+
:openapi2
|
38
|
+
when 'openapi3'
|
39
|
+
:openapi3
|
59
40
|
when nil
|
60
|
-
:
|
41
|
+
:drafter_4
|
61
42
|
else
|
62
43
|
raise 'Unsupported drafter version!'
|
63
44
|
end
|
@@ -77,35 +58,21 @@ def write_split_json(actions, output)
|
|
77
58
|
end
|
78
59
|
|
79
60
|
def write_json(obj, path)
|
80
|
-
json =
|
61
|
+
json = JSON.pretty_generate(obj)
|
81
62
|
File.open(path, 'w') do |file|
|
82
63
|
file.write(json)
|
83
64
|
end
|
84
65
|
end
|
85
66
|
|
86
67
|
main do |input, output|
|
87
|
-
format = guess_format(options['format'], input)
|
88
68
|
version = choose_drafter(options['drafter'])
|
89
|
-
format_key =
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
:drafter_4_apib_path
|
97
|
-
end
|
98
|
-
when :yaml
|
99
|
-
if version == :drafter_3
|
100
|
-
:drafter_yaml_path
|
101
|
-
elsif version == :crafter
|
102
|
-
:crafter_yaml_path
|
103
|
-
else
|
104
|
-
:drafter_4_yaml_path
|
105
|
-
end
|
106
|
-
else
|
107
|
-
fail NotImplementedError
|
108
|
-
end
|
69
|
+
format_key = {
|
70
|
+
crafter: :crafter_yaml_path,
|
71
|
+
drafter_4: :drafter_yaml_path,
|
72
|
+
openapi2: :openapi2_json_path,
|
73
|
+
openapi3: :openapi3_yaml_path
|
74
|
+
}[version]
|
75
|
+
|
109
76
|
tomogram = Tomograph::Tomogram.new(format_key => input)
|
110
77
|
actions = tomogram.to_a.map(&:to_hash)
|
111
78
|
|
@@ -6,13 +6,9 @@ module Tomograph
|
|
6
6
|
module ApiBlueprint
|
7
7
|
class Crafter
|
8
8
|
class Yaml
|
9
|
-
def initialize(prefix,
|
9
|
+
def initialize(prefix, drafter_yaml_path)
|
10
10
|
@prefix = prefix
|
11
|
-
@documentation =
|
12
|
-
YAML.safe_load(`drafter #{apib_path}`)
|
13
|
-
elsif drafter_yaml_path
|
14
|
-
YAML.safe_load(File.read(drafter_yaml_path))
|
15
|
-
end
|
11
|
+
@documentation = YAML.safe_load(File.read(drafter_yaml_path))
|
16
12
|
end
|
17
13
|
|
18
14
|
def groups
|
@@ -101,15 +97,15 @@ module Tomograph
|
|
101
97
|
path: "#{@prefix}#{related_actions.first.path}",
|
102
98
|
method: related_actions.first.method,
|
103
99
|
content_type: related_actions.first.content_type,
|
104
|
-
|
105
|
-
responses: related_actions.map(&:responses).flatten,
|
100
|
+
requests: related_actions.map(&:request).flatten.uniq,
|
101
|
+
responses: related_actions.map(&:responses).flatten.uniq,
|
106
102
|
resource: related_actions.first.resource
|
107
103
|
}
|
108
104
|
end
|
109
105
|
|
110
106
|
def to_tomogram
|
111
107
|
@tomogram ||= actions.inject([]) do |result, action|
|
112
|
-
result.push(Tomograph::Tomogram::Action.new(action))
|
108
|
+
result.push(Tomograph::Tomogram::Action.new(**action))
|
113
109
|
end
|
114
110
|
end
|
115
111
|
|
@@ -37,8 +37,8 @@ module Tomograph
|
|
37
37
|
end
|
38
38
|
return {} unless schema_node
|
39
39
|
|
40
|
-
|
41
|
-
rescue
|
40
|
+
JSON.parse(schema_node['content'])
|
41
|
+
rescue JSON::ParserError => e
|
42
42
|
puts "[Tomograph] Error while parsing #{e}. skipping..."
|
43
43
|
{}
|
44
44
|
end
|
@@ -6,13 +6,9 @@ module Tomograph
|
|
6
6
|
module ApiBlueprint
|
7
7
|
class Drafter4
|
8
8
|
class Yaml
|
9
|
-
def initialize(prefix,
|
9
|
+
def initialize(prefix, drafter_yaml_path)
|
10
10
|
@prefix = prefix
|
11
|
-
@documentation =
|
12
|
-
YAML.safe_load(`drafter #{apib_path}`)
|
13
|
-
elsif drafter_yaml_path
|
14
|
-
YAML.safe_load(File.read(drafter_yaml_path))
|
15
|
-
end
|
11
|
+
@documentation = YAML.safe_load(File.read(drafter_yaml_path))
|
16
12
|
end
|
17
13
|
|
18
14
|
def groups
|
@@ -101,15 +97,15 @@ module Tomograph
|
|
101
97
|
path: "#{@prefix}#{related_actions.first.path}",
|
102
98
|
method: related_actions.first.method,
|
103
99
|
content_type: related_actions.first.content_type,
|
104
|
-
|
105
|
-
responses: related_actions.map(&:responses).flatten,
|
100
|
+
requests: related_actions.map(&:request).flatten.uniq,
|
101
|
+
responses: related_actions.map(&:responses).flatten.uniq,
|
106
102
|
resource: related_actions.first.resource
|
107
103
|
}
|
108
104
|
end
|
109
105
|
|
110
106
|
def to_tomogram
|
111
107
|
@tomogram ||= actions.inject([]) do |result, action|
|
112
|
-
result.push(Tomograph::Tomogram::Action.new(action))
|
108
|
+
result.push(Tomograph::Tomogram::Action.new(**action))
|
113
109
|
end
|
114
110
|
end
|
115
111
|
|
@@ -37,8 +37,8 @@ module Tomograph
|
|
37
37
|
end
|
38
38
|
return {} unless schema_node
|
39
39
|
|
40
|
-
|
41
|
-
rescue
|
40
|
+
JSON.parse(schema_node['content'])
|
41
|
+
rescue JSON::ParserError => e
|
42
42
|
puts "[Tomograph] Error while parsing #{e}. skipping..."
|
43
43
|
{}
|
44
44
|
end
|
@@ -5,7 +5,7 @@ module Tomograph
|
|
5
5
|
class JsonSchema
|
6
6
|
def initialize(prefix, json_schema_path)
|
7
7
|
@prefix = prefix
|
8
|
-
@documentation =
|
8
|
+
@documentation = JSON.parse(File.read(json_schema_path))
|
9
9
|
end
|
10
10
|
|
11
11
|
def to_tomogram
|
@@ -14,7 +14,7 @@ module Tomograph
|
|
14
14
|
path: "#{@prefix}#{action['path']}",
|
15
15
|
method: action['method'],
|
16
16
|
content_type: action['content-type'],
|
17
|
-
|
17
|
+
requests: action['requests'],
|
18
18
|
responses: action['responses'],
|
19
19
|
resource: action['resource']))
|
20
20
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'tomograph/tomogram/action'
|
2
|
+
|
3
|
+
module Tomograph
|
4
|
+
module OpenApi
|
5
|
+
class OpenApi2
|
6
|
+
def initialize(prefix, json_schema_path)
|
7
|
+
@prefix = prefix
|
8
|
+
@documentation = JSON.parse(File.read(json_schema_path))
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_tomogram
|
12
|
+
@tomogram ||= @documentation['paths'].inject([]) do |result, action|
|
13
|
+
action[1].keys.each do |method|
|
14
|
+
result.push(Tomograph::Tomogram::Action.new(
|
15
|
+
path: "#{@prefix}#{action[0]}",
|
16
|
+
method: method.upcase,
|
17
|
+
content_type: '',
|
18
|
+
requests: [],
|
19
|
+
responses: responses(action[1][method]['responses'], @documentation['definitions']),
|
20
|
+
resource: ''))
|
21
|
+
end
|
22
|
+
result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def responses(resp, defi)
|
27
|
+
resp.inject([]) do |result, reponse|
|
28
|
+
if reponse[1]['schema']
|
29
|
+
result.push(
|
30
|
+
status: reponse[0],
|
31
|
+
body: schema(reponse[1]['schema'], defi),
|
32
|
+
'content-type': ''
|
33
|
+
)
|
34
|
+
else
|
35
|
+
result.push(
|
36
|
+
status: reponse[0],
|
37
|
+
body: {},
|
38
|
+
'content-type': ''
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def schema(sche, defi)
|
45
|
+
if sche.keys.include?('$ref')
|
46
|
+
res = sche.merge('definitions' => {sche["$ref"][14..-1] => defi[sche["$ref"][14..-1]]})
|
47
|
+
if defi[sche["$ref"][14..-1]].to_s.include?('$ref')
|
48
|
+
keys = defi[sche["$ref"][14..-1]].to_s.split('"').find_all{|word| word.include?('definitions') }
|
49
|
+
keys.each do |key|
|
50
|
+
res["definitions"].merge!({key[14..-1] => defi[key[14..-1]]})
|
51
|
+
end
|
52
|
+
end
|
53
|
+
res
|
54
|
+
else
|
55
|
+
if sche.to_s.include?('$ref')
|
56
|
+
res = sche.merge('definitions' => {})
|
57
|
+
keys = sche.to_s.split('"').find_all{|word| word.include?('definitions') }
|
58
|
+
keys.each do |key|
|
59
|
+
res["definitions"].merge!({key[14..-1] => defi[key[14..-1]]})
|
60
|
+
end
|
61
|
+
res
|
62
|
+
else
|
63
|
+
sche
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def search_hash(hash, key)
|
69
|
+
return hash[key] if hash.assoc(key)
|
70
|
+
hash.delete_if{|key, value| value.class != Hash}
|
71
|
+
new_hash = Hash.new
|
72
|
+
hash.each_value {|values| new_hash.merge!(values)}
|
73
|
+
unless new_hash.empty?
|
74
|
+
search_hash(new_hash, key)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_resources
|
79
|
+
return @to_resources if @to_resources
|
80
|
+
|
81
|
+
@to_resources = @documentation.group_by { |action| action['resource'] }
|
82
|
+
@to_resources = @to_resources.each_with_object({}) do |(resource, actions), resource_map|
|
83
|
+
requests = actions.map do |action|
|
84
|
+
"#{action['method']} #{@prefix}#{action['path']}"
|
85
|
+
end
|
86
|
+
resource_map[resource] = requests
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'tomograph/tomogram/action'
|
2
|
+
|
3
|
+
module Tomograph
|
4
|
+
module OpenApi
|
5
|
+
class OpenApi3
|
6
|
+
def initialize(prefix, openapi3_yaml_path)
|
7
|
+
@prefix = prefix
|
8
|
+
@documentation = YAML.load(File.read(openapi3_yaml_path))
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_tomogram
|
12
|
+
@tomogram ||= @documentation['paths'].inject([]) do |result, action|
|
13
|
+
action[1].keys.each do |method|
|
14
|
+
result.push(Tomograph::Tomogram::Action.new(
|
15
|
+
path: "#{@prefix}#{action[0]}",
|
16
|
+
method: method.upcase,
|
17
|
+
content_type: '',
|
18
|
+
requests: [],
|
19
|
+
responses: responses(action[1][method]['responses'], @documentation['components']['schemas']),
|
20
|
+
resource: ''))
|
21
|
+
end
|
22
|
+
result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def responses(resp, defi)
|
27
|
+
resp.inject([]) do |result, response|
|
28
|
+
if response[1]['content'] == nil
|
29
|
+
#TODO 403Forbidden
|
30
|
+
result.push(
|
31
|
+
status: response[0],
|
32
|
+
body: {},
|
33
|
+
'content-type': ''
|
34
|
+
)
|
35
|
+
elsif response[1]['content'].values[0]['schema']
|
36
|
+
result.push(
|
37
|
+
status: response[0],
|
38
|
+
body: schema(response[1]['content'].values[0]['schema'], defi),
|
39
|
+
'content-type': ''
|
40
|
+
)
|
41
|
+
else
|
42
|
+
result.push(
|
43
|
+
status: response[0],
|
44
|
+
body: {},
|
45
|
+
'content-type': ''
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def schema(sche, defi)
|
52
|
+
if sche.keys.include?('$ref')
|
53
|
+
sche.merge!('components' => {})
|
54
|
+
sche['components'].merge!('schemas' => {})
|
55
|
+
sche['components']['schemas'].merge!({sche["$ref"][21..-1] => defi[sche["$ref"][21..-1]]})
|
56
|
+
|
57
|
+
if defi[sche["$ref"][21..-1]].to_s.include?('$ref')
|
58
|
+
keys = defi[sche["$ref"][21..-1]].to_s.split('"').find_all{|word| word.include?('#/components/schemas/') }
|
59
|
+
keys.each do |key|
|
60
|
+
sche['components']['schemas'].merge!({key[21..-1] => defi[key[21..-1]]})
|
61
|
+
|
62
|
+
if defi[key[21..-1]].to_s.include?('$ref')
|
63
|
+
keys2 = defi[key[21..-1]].to_s.split('"').find_all{|word| word.include?('#/components/schemas/') }
|
64
|
+
keys2.each do |key2|
|
65
|
+
sche['components']['schemas'].merge!({key2[21..-1] => defi[key2[21..-1]]})
|
66
|
+
|
67
|
+
if defi[key2[21..-1]].to_s.include?('$ref')
|
68
|
+
keys3 = defi[key2[21..-1]].to_s.split('"').find_all { |word| word.include?('#/components/schemas/') }.uniq
|
69
|
+
keys3.each do |key3|
|
70
|
+
sche['components']['schemas'].merge!({ key3[21..-1] => defi[key3[21..-1]] })
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
sche
|
80
|
+
|
81
|
+
else
|
82
|
+
if sche.to_s.include?('$ref')
|
83
|
+
res = sche.merge('definitions' => {})
|
84
|
+
keys = sche.to_s.split('"').find_all{|word| word.include?('definitions') }
|
85
|
+
keys.each do |key|
|
86
|
+
res["definitions"].merge!({key[21..-1] => defi[key[21..-1]]})
|
87
|
+
end
|
88
|
+
res
|
89
|
+
else
|
90
|
+
sche
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def search_hash(hash, key)
|
96
|
+
return hash[key] if hash.assoc(key)
|
97
|
+
hash.delete_if{|key, value| value.class != Hash}
|
98
|
+
new_hash = Hash.new
|
99
|
+
hash.each_value {|values| new_hash.merge!(values)}
|
100
|
+
unless new_hash.empty?
|
101
|
+
search_hash(new_hash, key)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_resources
|
106
|
+
return @to_resources if @to_resources
|
107
|
+
|
108
|
+
@to_resources = @documentation.group_by { |action| action['resource'] }
|
109
|
+
@to_resources = @to_resources.each_with_object({}) do |(resource, actions), resource_map|
|
110
|
+
requests = actions.map do |action|
|
111
|
+
"#{action['method']} #{@prefix}#{action['path']}"
|
112
|
+
end
|
113
|
+
resource_map[resource] = requests
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/lib/tomograph/tomogram.rb
CHANGED
@@ -1,23 +1,26 @@
|
|
1
|
-
require '
|
1
|
+
require 'json'
|
2
2
|
require 'tomograph/path'
|
3
3
|
require 'tomograph/api_blueprint/json_schema'
|
4
|
-
require 'tomograph/api_blueprint/drafter_3/yaml'
|
5
4
|
require 'tomograph/api_blueprint/drafter_4/yaml'
|
6
5
|
require 'tomograph/api_blueprint/crafter/yaml'
|
6
|
+
require 'tomograph/openapi/openapi2'
|
7
|
+
require 'tomograph/openapi/openapi3'
|
7
8
|
|
8
9
|
module Tomograph
|
9
10
|
class Tomogram
|
10
11
|
extend Gem::Deprecate
|
11
12
|
|
12
|
-
def initialize(prefix: '',
|
13
|
+
def initialize(prefix: '', drafter_yaml_path: nil, tomogram_json_path: nil, crafter_yaml_path: nil, openapi2_json_path: nil, openapi3_yaml_path: nil)
|
13
14
|
@documentation = if tomogram_json_path
|
14
15
|
Tomograph::ApiBlueprint::JsonSchema.new(prefix, tomogram_json_path)
|
15
|
-
elsif
|
16
|
-
Tomograph::ApiBlueprint::
|
17
|
-
elsif
|
18
|
-
Tomograph::
|
16
|
+
elsif crafter_yaml_path
|
17
|
+
Tomograph::ApiBlueprint::Crafter::Yaml.new(prefix, crafter_yaml_path)
|
18
|
+
elsif openapi2_json_path
|
19
|
+
Tomograph::OpenApi::OpenApi2.new(prefix, openapi2_json_path)
|
20
|
+
elsif openapi3_yaml_path
|
21
|
+
Tomograph::OpenApi::OpenApi3.new(prefix, openapi3_yaml_path)
|
19
22
|
else
|
20
|
-
Tomograph::ApiBlueprint::
|
23
|
+
Tomograph::ApiBlueprint::Drafter4::Yaml.new(prefix, drafter_yaml_path)
|
21
24
|
end
|
22
25
|
@prefix = prefix
|
23
26
|
end
|
@@ -26,13 +29,8 @@ module Tomograph
|
|
26
29
|
@actions ||= @documentation.to_tomogram
|
27
30
|
end
|
28
31
|
|
29
|
-
def to_hash
|
30
|
-
to_a.map(&:to_hash)
|
31
|
-
end
|
32
|
-
deprecate :to_hash, 'to_a with method access', 2018, 8
|
33
|
-
|
34
32
|
def to_json
|
35
|
-
|
33
|
+
JSON.pretty_generate(to_a.map(&:to_hash))
|
36
34
|
end
|
37
35
|
|
38
36
|
def find_request(method:, path:)
|