openapi_parser 0.6.1 → 0.7.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
  SHA256:
3
- metadata.gz: 7480b22f5a7f1e12c9c1d2b21dbcc50b711bb06fba8f31e7cdff9f340f7f6fe8
4
- data.tar.gz: d9fecd50469f7e3f8bf88cf5076c8b78df24a5d825920748f518bcbd38ca0677
3
+ metadata.gz: e6fdbfb2bf6d7101bc016fc611e144cba0c53ee1456f433f62200f6b216d15ef
4
+ data.tar.gz: 27f093ed770beca6b908f9079306afc4f74d81de136c9da8e295ab3ccf833cc6
5
5
  SHA512:
6
- metadata.gz: 18af5156bd11cd2388608f2c835cfc44a049f55e5729c25997d44a7e434f00f9278ea1b39ef5490044aa950252148585e101f021659d34fa023046af03f96401
7
- data.tar.gz: e706defe789e26b503018653b8be22671a0155a2bbc39ac62c29603e7bd9a58f1cb923c9f7b51a858dde34ae06397860c1c68345828fff5be386866a2f6ca236
6
+ metadata.gz: c179283bac82745556d85ee1c02dcf0a2135caa60ff16c6f6b1fa44ff83de531631cc4b39da00c12b0a9e3e11eb2e606d9dc2b53f7b53cee47aa4e4e7a12e709
7
+ data.tar.gz: 1638236c9fd1b22b7922ce8e2f81c942748101ef71e919fa7333b8a7e854a43c5e9ba738e47a61ffd823f682997f33c15634b18f515a88d5ddb7670bda205965
@@ -1,5 +1,9 @@
1
1
  ## Unreleased
2
2
 
3
+ ## 0.7.0 (2020-01-15)
4
+ * Avoid potential `.send(:exit)` #58
5
+ * Improve PathItemFinder #44
6
+
3
7
  ## 0.6.1 (2019-10-12)
4
8
  * Bugfix: validate non-nullable response header #54
5
9
  * Improve grammar in error messages #55
@@ -1,10 +1,7 @@
1
1
  class OpenAPIParser::PathItemFinder
2
2
  # @param [OpenAPIParser::Schemas::Paths] paths
3
3
  def initialize(paths)
4
- @root = PathNode.new('/')
5
4
  @paths = paths
6
-
7
- @paths.path.each { |path, _path_item_object| @root.register_path_node(path.split('/'), path) }
8
5
  end
9
6
 
10
7
  # find operation object and if not exist return nil
@@ -41,71 +38,83 @@ class OpenAPIParser::PathItemFinder
41
38
  end
42
39
  end
43
40
 
44
- # path tree node
45
- class PathNode
46
- attr_accessor :full_path, :name
47
-
48
- def initialize(name)
49
- @name = name
50
- @children = Hash.new { |h, k| h[k] = PathNode.new(k) }
51
- @path_template_node = nil # we can't initialize because recursive initialize...
52
- @full_path = nil
41
+ private
42
+ # check if there is a identical path in the schema (without any param)
43
+ def matches_directly?(request_path, http_method)
44
+ @paths.path[request_path]&.operation(http_method)
53
45
  end
54
46
 
55
- # @param [Array<string>] splited_path
56
- # @param [String] full_path
57
- def register_path_node(splited_path, full_path)
58
- if splited_path.empty?
59
- @full_path = full_path
60
- return
61
- end
62
-
63
- path_name = splited_path.shift
64
-
65
- child = path_template?(path_name) ? path_template_node(path_name) : children[path_name]
66
- child.register_path_node(splited_path, full_path)
47
+ # used to filter paths with different depth or without given http method
48
+ def different_depth_or_method?(splitted_schema_path, splitted_request_path, path_item, http_method)
49
+ splitted_schema_path.size != splitted_request_path.size || !path_item.operation(http_method)
67
50
  end
68
51
 
69
- # @return [String, nil] and Hash
70
- def find_full_path(splited_path)
71
- return [@full_path, {}] if splited_path.empty?
72
-
73
- path_name = splited_path.shift
52
+ # check if the path item is a template
53
+ # EXAMPLE: path_template?('{id}') => true
54
+ def path_template?(schema_path_item)
55
+ schema_path_item.start_with?('{') && schema_path_item.end_with?('}')
56
+ end
74
57
 
75
- # when ambiguous matching like this
76
- # /{entity}/me
77
- # /books/{id}
78
- # OpenAPI3 depend on the tooling so we use concrete one (using /books/)
58
+ # get the parameter name from the schema path item
59
+ # EXAMPLE: param_name('{id}') => 'id'
60
+ def param_name(schema_path_item)
61
+ schema_path_item[1..(schema_path_item.length - 2)]
62
+ end
79
63
 
80
- path_params = {}
81
- if children.has_key?(path_name)
82
- child = children[path_name]
83
- else
84
- child = path_template_node(path_name)
85
- path_params = { child.name.to_s => path_name }
64
+ # extract params by comparing the request path and the path from schema
65
+ # EXAMPLE:
66
+ # extract_params(['org', '1', 'user', '2', 'edit'], ['org', '{org_id}', 'user', '{user_id}'])
67
+ # => { 'org_id' => 1, 'user_id' => 2 }
68
+ # return nil if the schema does not match
69
+ def extract_params(splitted_request_path, splitted_schema_path)
70
+ splitted_request_path.zip(splitted_schema_path).reduce({}) do |result, zip_item|
71
+ request_path_item, schema_path_item = zip_item
72
+
73
+ if path_template?(schema_path_item)
74
+ result[param_name(schema_path_item)] = request_path_item
75
+ else
76
+ return if schema_path_item != request_path_item
77
+ end
78
+
79
+ result
86
80
  end
87
-
88
- ret, other_path_params = child.find_full_path(splited_path)
89
- [ret, path_params.merge(other_path_params)]
90
81
  end
91
82
 
92
- private
93
-
94
- attr_reader :children
95
-
96
- def path_template_node(path_name)
97
- @path_template_node ||= PathNode.new(path_name[1..(path_name.length - 2)]) # delete {} from {name}
83
+ # find all matching patchs with parameters extracted
84
+ # EXAMPLE:
85
+ # [
86
+ # ['/user/{id}/edit', { 'id' => 1 }],
87
+ # ['/user/{id}/{action}', { 'id' => 1, 'action' => 'edit' }],
88
+ # ]
89
+ def matching_paths_with_params(request_path, http_method)
90
+ splitted_request_path = request_path.split('/')
91
+
92
+ @paths.path.reduce([]) do |result, item|
93
+ path, path_item = item
94
+ splitted_schema_path = path.split('/')
95
+
96
+ next result if different_depth_or_method?(splitted_schema_path, splitted_request_path, path_item, http_method)
97
+
98
+ extracted_params = extract_params(splitted_request_path, splitted_schema_path)
99
+ result << [path, extracted_params] if extracted_params
100
+ result
98
101
  end
102
+ end
99
103
 
100
- def path_template?(path_name)
101
- path_name.start_with?('{') && path_name.end_with?('}')
102
- end
103
- end
104
+ # find mathing path and extract params
105
+ # EXAMPLE: find_path_and_params('get', '/user/1') => ['/user/{id}', { 'id' => 1 }]
106
+ def find_path_and_params(http_method, request_path)
107
+ return [request_path, {}] if matches_directly?(request_path, http_method)
108
+
109
+ matching = matching_paths_with_params(request_path, http_method)
104
110
 
105
- private
111
+ # if there are many matching paths, return the one with the smallest number of params
112
+ # (prefer /user/{id}/action over /user/{param_1}/{param_2} )
113
+ matching.min_by { |match| match[0].size }
114
+ end
106
115
 
107
116
  def parse_request_path(http_method, request_path)
108
- original_path, path_params = @root.find_full_path(request_path.split('/'))
117
+ original_path, path_params = find_path_and_params(http_method, request_path)
109
118
  return nil unless original_path # # can't find
110
119
 
111
120
  path_item_object = @paths.path[original_path]
@@ -12,7 +12,9 @@ module OpenAPIParser::Schemas
12
12
 
13
13
  # @return [Operation]
14
14
  def operation(method)
15
- send(method)
15
+ public_send(method)
16
+ rescue NoMethodError
17
+ nil
16
18
  end
17
19
  end
18
20
  end
@@ -1,3 +1,3 @@
1
1
  module OpenAPIParser
2
- VERSION = '0.6.1'.freeze
2
+ VERSION = '0.7.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openapi_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ota42y
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-10-12 00:00:00.000000000 Z
11
+ date: 2020-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -222,7 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
222
222
  - !ruby/object:Gem::Version
223
223
  version: '0'
224
224
  requirements: []
225
- rubygems_version: 3.0.3
225
+ rubygems_version: 3.0.6
226
226
  signing_key:
227
227
  specification_version: 4
228
228
  summary: OpenAPI3 parser