tomograph 0.4.0 → 1.0.0

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
  SHA1:
3
- metadata.gz: 3228f341aa3435287604e817404f80f26eec5b71
4
- data.tar.gz: c6db2eb21936aa5523e1c3506cb1fca18d795a70
3
+ metadata.gz: ee14aba11b9ba06a3a83fbe84055570c888cb013
4
+ data.tar.gz: 0f364c057b8f2569deb21397db74cbb13eb3fe0e
5
5
  SHA512:
6
- metadata.gz: c82ba6aa0a2137a3f63884f327cdca1ae0f731f455bb8b888f5e99afeddb3d17702144880e1b90d322043e13d81543578aa9f72afa15c6fc3407524e5bf106da
7
- data.tar.gz: a815f9f5c0e800e20929dbf9c83734e1bdb743754dcd08ffeb61813517c174ef431cbc9c238a38863599ee0d288887720dbf324b2f5de335afeb997da4d8d7cf
6
+ metadata.gz: 18d05e492cca9fa7de92a32b564f926842457a1351125beb293d078f8e3d3022503cb8194ef0b0f2b3f6ea4381722871e70cee9fe3eab149b2c031f90157969b
7
+ data.tar.gz: 36cb8b49d448f23c08c814c0a0a2bd83a2c84f5c4a347892b543622bcc71563bcea4c364015c87f275f624bc259161e981554c69e8faf0a63a2abfa86c1dd933
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.3
2
+ TargetRubyVersion: 2.2
3
3
 
4
4
  Metrics/LineLength:
5
5
  Max: 120
@@ -9,3 +9,6 @@ Style/Documentation:
9
9
 
10
10
  Style/FrozenStringLiteralComment:
11
11
  Enabled: false
12
+
13
+ Metrics/BlockLength:
14
+ Enabled: false
data/README.md CHANGED
@@ -1,40 +1,194 @@
1
1
  # tomograph
2
2
 
3
- This is make API Tomogram
3
+ Convert API Blueprint to JSON Schema and search.
4
4
 
5
- ## Install
5
+ ## Installation
6
6
 
7
- config/application.rb
7
+ Add this line to your application's Gemfile:
8
8
 
9
+ ```ruby
10
+ gem 'tomograph'
9
11
  ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install tomograph
20
+
21
+ ## Usage
22
+
23
+ ```ruby
10
24
  require 'tomograph'
11
25
  ```
12
26
 
13
- ## Config
27
+ ```ruby
28
+ tomogram = Tomograph::Tomogram.new(apib_path: '/path/to/doc.apib')
29
+ ```
30
+
31
+ ## Convert
32
+
33
+ ```ruby
34
+ tomogram.to_json
35
+ ```
36
+
37
+ Example input:
38
+ ```
39
+ FORMAT: 1A
40
+ HOST: http://test.local
41
+
42
+ # project
43
+
44
+ # Group project
45
+
46
+ Project
47
+
48
+ ## Authentication [/sessions]
49
+
50
+ ### Sign In [POST]
51
+
52
+ + Request (application/json)
53
+
54
+ + Attributes
55
+ + login (string, required)
56
+ + password (string, required)
57
+ + captcha (string, optional)
58
+
59
+ + Response 401 (application/json)
60
+
61
+ + Response 429 (application/json)
62
+
63
+ + Response 201 (application/json)
64
+
65
+ + Attributes
66
+ + confirmation (Confirmation, optional)
67
+ + captcha (string, optional)
68
+ + captcha_does_not_match (boolean, optional)
69
+
70
+
71
+ # Data Structures
72
+
73
+ ## Confirmation (object)
74
+ + id (string, required)
75
+ + type (string, required)
76
+ + operation (string, required)
77
+ ```
78
+
79
+ Example output:
80
+ ```
81
+ [
82
+ {
83
+ "path": "/sessions",
84
+ "method": "POST",
85
+ "request": {
86
+ "$schema": "http://json-schema.org/draft-04/schema#",
87
+ "type": "object",
88
+ "properties": {
89
+ "login": {
90
+ "type": "string"
91
+ },
92
+ "password": {
93
+ "type": "string"
94
+ },
95
+ "captcha": {
96
+ "type": "string"
97
+ }
98
+ },
99
+ "required": [
100
+ "login",
101
+ "password"
102
+ ]
103
+ },
104
+ "responses": [
105
+ {
106
+ "status": "401",
107
+ "body": {}
108
+ },
109
+ {
110
+ "status": "429",
111
+ "body": {}
112
+ },
113
+ {
114
+ "status": "201",
115
+ "body": {
116
+ "$schema": "http://json-schema.org/draft-04/schema#",
117
+ "type": "object",
118
+ "properties": {
119
+ "confirmation": {
120
+ "type": "object",
121
+ "properties": {
122
+ "id": {
123
+ "type": "string"
124
+ },
125
+ "type": {
126
+ "type": "string"
127
+ },
128
+ "operation": {
129
+ "type": "string"
130
+ }
131
+ },
132
+ "required": [
133
+ "id",
134
+ "type",
135
+ "operation"
136
+ ]
137
+ },
138
+ "captcha": {
139
+ "type": "string"
140
+ },
141
+ "captcha_does_not_match": {
142
+ "type": "boolean"
143
+ }
144
+ }
145
+ }
146
+ }
147
+ ]
148
+ }
149
+ ]
150
+ ```
151
+
152
+ ## Search
14
153
 
15
- ### documentation
154
+ ### find_request
16
155
 
17
- For gem needed by a ```drafter``` generate and save ```yaml``` file, and then turn it on config.
156
+ ```ruby
157
+ request = tomogram.find_request(method: 'GET', path: '/status/1?qwe=rty')
18
158
  ```
19
- drafter doc/api.apib -o doc/api.yaml
159
+
160
+ ### find_responses
161
+
162
+
163
+ ```ruby
164
+ responses = request.find_responses(status: '200')
20
165
  ```
21
166
 
167
+ ## Params
168
+
169
+ Example:
170
+
22
171
  ```ruby
23
- Tomograph.configure do |config|
24
- config.documentation = 'doc/api.yaml'
25
- end
172
+ Tomograph::Tomogram.new(prefix: '/api/v2', apib_path: '/path/to/doc.apib')
26
173
  ```
27
174
 
28
- Then you can use the API tomogram by using the following call:
175
+ or
29
176
 
30
177
  ```ruby
31
- Tomograph::Tomogram.json
178
+ Tomograph::Tomogram.new(prefix: '/api/v2', drafter_yaml_path: '/path/to/doc.yaml')
32
179
  ```
33
180
 
34
- API tomogram return to json format.
181
+ ### apib_path
182
+
183
+ Path to API Blueprint documentation. There must be an installed [drafter](https://github.com/apiaryio/drafter) to parse it.
184
+
185
+ ### drafter_yaml_path
186
+
187
+ Path to API Blueprint documentation pre-parsed with `drafter` and saved to a YAML file.
35
188
 
36
189
  ### prefix
37
- Default empty String. This is prefix for url if in docimentation URI short .
190
+
191
+ Default empty String. Prefix of API requests. Example: `'/api'`.
38
192
 
39
193
  ## License
40
194
 
data/lib/tomograph.rb CHANGED
@@ -1,14 +1,4 @@
1
1
  require 'tomograph/tomogram'
2
- require 'tomograph/configuration'
3
2
 
4
3
  module Tomograph
5
- class << self
6
- def configure
7
- yield configuration
8
- end
9
-
10
- def configuration
11
- @configuration ||= Configuration.new
12
- end
13
- end
14
4
  end
@@ -0,0 +1,87 @@
1
+ require 'tomograph/api_blueprint/yaml/action'
2
+ require 'tomograph/tomogram/action'
3
+
4
+ module Tomograph
5
+ module ApiBlueprint
6
+ class Yaml
7
+ def initialize(prefix, apib_path, drafter_yaml_path)
8
+ @prefix = prefix
9
+ @documentation = if apib_path
10
+ YAML.safe_load(`drafter #{apib_path}`)
11
+ else
12
+ YAML.safe_load(File.read("#{Rails.root}/#{drafter_yaml_path}"))
13
+ end
14
+ end
15
+
16
+ def groups
17
+ @groups ||= @documentation['content'][0]['content'].inject([]) do |result_groups, group|
18
+ next result_groups unless group?(group)
19
+ result_groups.push(group)
20
+ end
21
+ end
22
+
23
+ def group?(group)
24
+ group['element'] != 'copy' && # Element is a human readable text
25
+ group['meta']['classes'][0] == 'resourceGroup' # skip Data Structures
26
+ end
27
+
28
+ def resources
29
+ @resources ||= groups.inject([]) do |result_groups, group|
30
+ result_groups.push(group['content'].inject([]) do |result_resources, resource|
31
+ next result_resources unless resource?(resource)
32
+ result_resources.push('resource' => resource, 'resource_path' => resource_path(resource))
33
+ end)
34
+ end.flatten
35
+ end
36
+
37
+ def resource?(resource)
38
+ resource['element'] != 'copy' # Element is a human readable text
39
+ end
40
+
41
+ def resource_path(resource)
42
+ resource['attributes'] && resource['attributes']['href']
43
+ end
44
+
45
+ def transitions
46
+ @transitions ||= resources.inject([]) do |result_resources, resource|
47
+ result_resources.push(resource['resource']['content'].inject([]) do |result_transitions, transition|
48
+ next result_transitions unless transition?(transition)
49
+ result_transitions.push('transition' => transition,
50
+ 'transition_path' => transition_path(transition, resource['resource_path']))
51
+ end)
52
+ end.flatten
53
+ end
54
+
55
+ def transition?(transition)
56
+ transition['element'] == 'transition'
57
+ end
58
+
59
+ def transition_path(transition, resource_path)
60
+ transition['attributes'] && transition['attributes']['href'] || resource_path
61
+ end
62
+
63
+ def actions
64
+ @actions ||= transitions.inject([]) do |result_transition, transition|
65
+ result_transition.push(transition['transition']['content'].inject([]) do |result_contents, content|
66
+ next result_contents unless action?(content)
67
+ result_contents.push(Tomograph::ApiBlueprint::Yaml::Action.new(
68
+ content['content'], transition['transition_path']
69
+ ))
70
+ end)
71
+ end.flatten
72
+ end
73
+
74
+ def action?(content)
75
+ content['element'] == 'httpTransaction'
76
+ end
77
+
78
+ def to_tomogram
79
+ @tomogram ||= Tomograph::Tomogram::Action.merge(
80
+ actions.inject([]) do |result, action|
81
+ result.push(action.to_tomogram.add_prefix(@prefix))
82
+ end
83
+ )
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,59 @@
1
+ require 'tomograph/tomogram/action'
2
+
3
+ module Tomograph
4
+ module ApiBlueprint
5
+ class Yaml
6
+ class Action
7
+ def initialize(content, path)
8
+ @content = content
9
+ @path = path
10
+ end
11
+
12
+ attr_reader :path
13
+
14
+ def method
15
+ @method ||= @content.first['attributes']['method']
16
+ end
17
+
18
+ def request
19
+ return @request if @request
20
+
21
+ request_action = @content.find { |el| el['element'] == 'httpRequest' }
22
+ @request = json_schema(request_action['content'])
23
+ end
24
+
25
+ def json_schema(actions)
26
+ schema_node = actions.find do |action|
27
+ action && action['element'] == 'asset' && action['attributes']['contentType'] == 'application/schema+json'
28
+ end
29
+ return {} unless schema_node
30
+
31
+ MultiJson.load(schema_node['content'])
32
+ rescue MultiJson::ParseError => e
33
+ puts "[Tomograph] Error while parsing #{e}. skipping..."
34
+ {}
35
+ end
36
+
37
+ def responses
38
+ return @responses if @responses
39
+
40
+ @responses = @content.select do |response|
41
+ response['element'] == 'httpResponse' && response['attributes']
42
+ end
43
+ @responses = @responses.map do |response|
44
+ {
45
+ 'status' => response['attributes']['statusCode'],
46
+ 'body' => json_schema(response['content'])
47
+ }
48
+ end
49
+ end
50
+
51
+ def to_tomogram
52
+ Tomograph::Tomogram::Action.new(
53
+ path: path, method: method, request: request, responses: responses
54
+ )
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,53 @@
1
+ module Tomograph
2
+ class Path
3
+ def initialize(path)
4
+ unless path && !path.empty?
5
+ @path = ''
6
+ return
7
+ end
8
+ @path = path
9
+ @path = delete_till_the_end('{&')
10
+ @path = delete_till_the_end('{?')
11
+ @path = cut_off_query_params
12
+ @path = remove_the_slash_at_the_end
13
+ end
14
+
15
+ def match(find_path)
16
+ regexp =~ find_path
17
+ end
18
+
19
+ def regexp
20
+ return @regexp if @regexp
21
+
22
+ str = Regexp.escape(to_s)
23
+ str = str.gsub(/\\{\w+\\}/, '[^&=\/]+')
24
+ str = "\\A#{str}\\z"
25
+ @regexp = Regexp.new(str)
26
+ end
27
+
28
+ def to_s
29
+ @path
30
+ end
31
+
32
+ private
33
+
34
+ def delete_till_the_end(beginning_with)
35
+ path_index = @path.index(beginning_with)
36
+ path_index ||= 0
37
+ path_index -= 1
38
+ @path.slice(0..path_index)
39
+ end
40
+
41
+ def remove_the_slash_at_the_end
42
+ if @path[-1] == '/'
43
+ @path[0..-2]
44
+ else
45
+ @path
46
+ end
47
+ end
48
+
49
+ def cut_off_query_params
50
+ @path.gsub(/\?.*\z/, '')
51
+ end
52
+ end
53
+ end
@@ -1,139 +1,27 @@
1
1
  require 'multi_json'
2
+ require 'tomograph/path'
3
+ require 'tomograph/api_blueprint/yaml'
2
4
 
3
5
  module Tomograph
4
- class Tomogram < String
5
- class << self
6
- def json
7
- single_sharps = find_resource
8
-
9
- result = []
10
- single_sharps.map do |single_sharp|
11
- result += single_sharp['content'].inject([]) do |result, resource|
12
- next result if text_node?(resource)
13
-
14
- result.concat(extract_actions(resource))
15
- end
16
- end
17
- MultiJson.dump(result)
18
- end
19
-
20
- def delete_query_and_last_slash(path)
21
- path = delete_till_the_end(path, '{&')
22
- path = delete_till_the_end(path, '{?')
23
-
24
- remove_the_slash_at_the_end(path)
25
- end
26
-
27
- private
28
-
29
- def extract_actions(resource)
30
- actions = []
31
- resource_path = resource['attributes'] && resource['attributes']['href']
32
-
33
- transitions = resource['content']
34
- transitions.each do |transition|
35
- next unless transition['element'] == 'transition'
36
-
37
- path = transition['attributes'] && transition['attributes']['href'] || resource_path
38
-
39
- transition['content'].each do |content|
40
- next unless content['element'] == 'httpTransaction'
41
-
42
- action = build_action(content, path)
43
- actions << action if action
44
- end
45
- end
46
-
47
- actions.group_by { |action| action['method'] + action['path'] }.map do |_key, resource_actions|
48
- # because in yaml a response has a copy of the same request we can only use the first
49
- {
50
- 'path' => resource_actions.first['path'],
51
- 'method' => resource_actions.first['method'],
52
- 'request' => resource_actions.first['request'],
53
- 'responses' => resource_actions.flat_map { |action| action['responses'] }.compact
54
- }
55
- end
56
- end
57
-
58
- def find_resource
59
- documentation['content'][0]['content'].find_all do |resource|
60
- !text_node?(resource) &&
61
- resource['meta']['classes'][0] == 'resourceGroup' # skip Data Structures
62
- end
63
- end
64
-
65
- def documentation
66
- if Tomograph.configuration.drafter_yaml
67
- YAML.load(Tomograph.configuration.drafter_yaml)
68
- else
69
- YAML.load(File.read("#{Rails.root}/#{Tomograph.configuration.documentation}"))
70
- end
71
- end
72
-
73
- def delete_till_the_end(path, beginning_with)
74
- path_index = path.index(beginning_with)
75
- path_index ||= 0
76
- path_index -= 1
77
- path.slice(0..path_index)
78
- end
79
-
80
- def remove_the_slash_at_the_end(path)
81
- if path[-1] == '/'
82
- path[0..-2]
83
- else
84
- path
85
- end
86
- end
87
-
88
- def build_action(content, path)
89
- return if text_node?(content)
90
-
91
- action_to_hash(content['content'], path)
92
- end
93
-
94
- def action_to_hash(actions, path)
95
- {
96
- 'path' => "#{Tomograph.configuration.prefix}#{delete_query_and_last_slash(path)}",
97
- 'method' => actions.first['attributes']['method'],
98
- 'request' => request(actions),
99
- 'responses' => responses(actions)
100
- }
101
- end
102
-
103
- def request(actions)
104
- request_action = actions.find {|el| el['element'] === 'httpRequest' }
105
- json_schema(request_action['content'])
106
- end
107
-
108
- def json_schema?(action)
109
- action && action['element'] == 'asset' && action['attributes']['contentType'] == 'application/schema+json'
110
- end
111
-
112
- def responses(actions)
113
- response_actions = actions.select {|el| el['element'] === 'httpResponse' }
114
-
115
- response_actions.map do |response|
116
- return unless response['attributes'] # if no response
6
+ class Tomogram
7
+ def initialize(prefix: '', apib_path: nil, drafter_yaml_path: nil)
8
+ @documentation = Tomograph::ApiBlueprint::Yaml.new(prefix, apib_path, drafter_yaml_path)
9
+ @prefix = prefix
10
+ end
117
11
 
118
- {
119
- 'status' => response['attributes']['statusCode'],
120
- 'body' => json_schema(response['content'])
121
- }
122
- end.compact
123
- end
12
+ def to_hash
13
+ @documentation.to_tomogram.map(&:to_hash)
14
+ end
124
15
 
125
- def json_schema(actions)
126
- schema_node = actions.find {|action| json_schema?(action) }
127
- return {} unless schema_node
16
+ def to_json
17
+ MultiJson.dump(to_hash)
18
+ end
128
19
 
129
- MultiJson.load(schema_node['content'])
130
- rescue MultiJson::ParseError => e
131
- puts "[Tomograph] Error while parsing #{ e }. skipping..."
132
- {}
133
- end
20
+ def find_request(method:, path:)
21
+ path = Tomograph::Path.new(path).to_s
134
22
 
135
- def text_node?(node)
136
- node['element'] == 'copy' # Element is a human readable text
23
+ @documentation.to_tomogram.find do |action|
24
+ action.method == method && action.path.match(path)
137
25
  end
138
26
  end
139
27
  end
@@ -0,0 +1,47 @@
1
+ require 'tomograph/path'
2
+
3
+ module Tomograph
4
+ class Tomogram
5
+ class Action
6
+ attr_reader :path, :method, :request, :responses
7
+
8
+ def initialize(path:, method:, request:, responses:)
9
+ @path ||= Tomograph::Path.new(path)
10
+ @method ||= method
11
+ @request ||= request
12
+ @responses ||= responses
13
+ end
14
+
15
+ def self.merge(actions)
16
+ actions.group_by { |action| "#{action.method} #{action.path}" }.map do |_key, related_actions|
17
+ new(
18
+ path: related_actions.first.path.to_s,
19
+ method: related_actions.first.method,
20
+ request: related_actions.first.request,
21
+ responses: related_actions.map(&:responses).flatten
22
+ )
23
+ end.flatten
24
+ end
25
+
26
+ def add_prefix(prefix)
27
+ @path = Tomograph::Path.new("#{prefix}#{path}")
28
+ self
29
+ end
30
+
31
+ def find_responses(status:)
32
+ to_hash['responses'].find_all do |response|
33
+ response['status'] == status.to_s
34
+ end
35
+ end
36
+
37
+ def to_hash
38
+ @action ||= {
39
+ 'path' => path,
40
+ 'method' => method,
41
+ 'request' => request,
42
+ 'responses' => responses
43
+ }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,3 +1,3 @@
1
1
  module Tomograph
2
- VERSION = '0.4.0'.freeze
2
+ VERSION = '1.0.0'.freeze
3
3
  end
data/tomograph.gemspec CHANGED
@@ -8,7 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ['d.efimov']
9
9
  spec.email = ['d.efimov@fun-box.ru']
10
10
 
11
- spec.summary = 'Parser API Blueprint specifications'
11
+ spec.summary = 'Convert API Blueprint to JSON Schema and search'
12
+ spec.homepage = 'https://github.com/funbox/tomograph'
12
13
  spec.license = 'MIT'
13
14
 
14
15
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tomograph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - d.efimov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-03-23 00:00:00.000000000 Z
11
+ date: 2017-04-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json
@@ -155,11 +155,14 @@ files:
155
155
  - bin/console
156
156
  - bin/setup
157
157
  - lib/tomograph.rb
158
- - lib/tomograph/configuration.rb
158
+ - lib/tomograph/api_blueprint/yaml.rb
159
+ - lib/tomograph/api_blueprint/yaml/action.rb
160
+ - lib/tomograph/path.rb
159
161
  - lib/tomograph/tomogram.rb
162
+ - lib/tomograph/tomogram/action.rb
160
163
  - lib/tomograph/version.rb
161
164
  - tomograph.gemspec
162
- homepage:
165
+ homepage: https://github.com/funbox/tomograph
163
166
  licenses:
164
167
  - MIT
165
168
  metadata: {}
@@ -179,8 +182,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
179
182
  version: '0'
180
183
  requirements: []
181
184
  rubyforge_project:
182
- rubygems_version: 2.5.1
185
+ rubygems_version: 2.4.5
183
186
  signing_key:
184
187
  specification_version: 4
185
- summary: Parser API Blueprint specifications
188
+ summary: Convert API Blueprint to JSON Schema and search
186
189
  test_files: []
@@ -1,11 +0,0 @@
1
- module Tomograph
2
- class Configuration
3
- attr_accessor :documentation,
4
- :prefix,
5
- :drafter_yaml
6
-
7
- def initialize
8
- @prefix = ''
9
- end
10
- end
11
- end