tomograph 0.4.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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