lurker 0.6.1 → 0.6.2

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.hound.yml +62 -0
  5. data/.rubocop.yml +62 -0
  6. data/Gemfile +1 -1
  7. data/Rakefile +2 -268
  8. data/features/atom_persistent_within_the_same_type.feature +0 -2
  9. data/features/controller_nested_schema_scaffolding.feature +0 -1
  10. data/features/controller_schema_scaffolding.feature +0 -1
  11. data/features/html_generation.feature +0 -1
  12. data/features/minitest.feature +0 -2
  13. data/features/multidomain_support.feature +0 -1
  14. data/features/multitype_request_support.feature +0 -2
  15. data/features/partials.feature +0 -1
  16. data/features/request_nested_schema_scaffolding.feature +0 -1
  17. data/features/request_schema_scaffolding.feature +0 -1
  18. data/features/schema_suffixes.feature +0 -1
  19. data/features/schema_updating_within_test_suite.feature +37 -2
  20. data/features/test_endpoint.feature +0 -1
  21. data/lib/lurker.rb +5 -1
  22. data/lib/lurker/endpoint.rb +121 -176
  23. data/lib/lurker/endpoint/http_parameters.rb +77 -0
  24. data/lib/lurker/endpoint/response_codes.rb +42 -0
  25. data/lib/lurker/erb_schema_context.rb +8 -6
  26. data/lib/lurker/jaml_descriptor.rb +8 -1
  27. data/lib/lurker/json_schema_hash.rb +48 -0
  28. data/lib/lurker/presenters/base_presenter.rb +1 -2
  29. data/lib/lurker/presenters/endpoint_presenter.rb +17 -10
  30. data/lib/lurker/presenters/json_presenter.rb +6 -6
  31. data/lib/lurker/presenters/schema_presenter.rb +15 -14
  32. data/lib/lurker/presenters/service_presenter.rb +5 -6
  33. data/lib/lurker/rendering_controller.rb +0 -1
  34. data/lib/lurker/request.rb +3 -1
  35. data/lib/lurker/sandbox.rb +0 -1
  36. data/lib/lurker/schema_modifier.rb +4 -2
  37. data/lib/lurker/schema_modifier/atom.rb +14 -5
  38. data/lib/lurker/server.rb +4 -5
  39. data/lib/lurker/service.rb +3 -4
  40. data/lib/lurker/spec_helper/rspec.rb +15 -57
  41. data/lib/lurker/spy.rb +9 -5
  42. data/lib/lurker/templates/layouts/_sidemenu.html.erb +1 -1
  43. data/lib/lurker/templates/lurker/rendering/show.html.erb +5 -1
  44. data/lib/lurker/templates/public/application.css +2 -2
  45. data/lib/lurker/templates/stylesheets/docs.css +0 -1
  46. data/lib/lurker/utils.rb +18 -0
  47. data/lib/lurker/validator.rb +3 -6
  48. data/lib/lurker/version.rb +1 -1
  49. data/tasks/build.rake +57 -0
  50. data/tasks/deploy.rake +139 -0
  51. data/tasks/generate.rake +78 -0
  52. data/templates/generate_stuff.rb +4 -4
  53. data/templates/lurker_app.rb +1 -1
  54. metadata +9 -2
  55. metadata.gz.sig +0 -0
@@ -54,7 +54,6 @@ Feature: atom persistent within test suite
54
54
  id: '100'
55
55
  controller: api/v2/users
56
56
  action: update
57
- suffix: ''
58
57
  """
59
58
 
60
59
  Scenario: json schema tests response parameters and keep atom unchanged using "users/update"
@@ -133,6 +132,5 @@ Feature: atom persistent within test suite
133
132
  id: '100'
134
133
  controller: api/v2/users
135
134
  action: update
136
- suffix: ''
137
135
 
138
136
  """
@@ -76,6 +76,5 @@ Feature: controller nested schema scaffolding
76
76
  action: index
77
77
  query_params:
78
78
  limit: 1
79
- suffix: ''
80
79
 
81
80
  """
@@ -66,6 +66,5 @@ Feature: controller schema scaffolding
66
66
  id: '1'
67
67
  controller: api/v1/users
68
68
  action: show
69
- suffix: ''
70
69
 
71
70
  """
@@ -40,7 +40,6 @@ Feature: html generation
40
40
  path_params:
41
41
  action: create
42
42
  controller: api/v1/users
43
- suffix: ''
44
43
  """
45
44
 
46
45
  When I successfully run `bin/lurker convert`
@@ -61,7 +61,6 @@ Feature: minitest
61
61
  controller: api/v1/repos
62
62
  user_id: '1'
63
63
  id: '1'
64
- suffix: ''
65
64
 
66
65
  """
67
66
 
@@ -100,7 +99,6 @@ Feature: minitest
100
99
  extensions:
101
100
  path_info: "/api/v1/users/1"
102
101
  method: PATCH
103
- suffix: ''
104
102
  path_params:
105
103
  action: update
106
104
  controller: api/v1/users
@@ -50,7 +50,6 @@ Feature: mutidomain support
50
50
  action: destroy
51
51
  controller: api/v1/users
52
52
  id: 1
53
- suffix: ''
54
53
  """
55
54
 
56
55
  When I successfully run `bin/lurker convert`
@@ -55,7 +55,6 @@ Feature: multitype request support
55
55
  id: '1'
56
56
  controller: api/v2/users
57
57
  action: update
58
- suffix: ''
59
58
  """
60
59
 
61
60
  Scenario: json schema tests response parameters and update request parameters using "users/update"
@@ -134,6 +133,5 @@ Feature: multitype request support
134
133
  id: '1'
135
134
  controller: api/v2/users
136
135
  action: update
137
- suffix: ''
138
136
 
139
137
  """
@@ -61,7 +61,6 @@ Feature: partials
61
61
  action: create
62
62
  controller: api/v1/repos
63
63
  user_id: '1'
64
- suffix: ''
65
64
  """
66
65
 
67
66
  Given a file named "spec/requests/repo_creation_spec.rb" with:
@@ -66,6 +66,5 @@ Feature: request nested schema scaffolding
66
66
  controller: api/v1/repos
67
67
  user_id: '1'
68
68
  id: '1'
69
- suffix: ''
70
69
 
71
70
  """
@@ -76,6 +76,5 @@ Feature: request schema scaffolding
76
76
  controller: api/v1/users
77
77
  query_params:
78
78
  limit: '1'
79
- suffix: ''
80
79
 
81
80
  """
@@ -48,7 +48,6 @@ Feature: schema suffixes
48
48
  extensions:
49
49
  path_info: "/api/v1/users/razum2um/repos/lurker.json"
50
50
  method: PATCH
51
- suffix: ''
52
51
  path_params:
53
52
  action: update
54
53
  controller: api/v1/repos
@@ -26,6 +26,9 @@ Feature: schema updating within test suite
26
26
  type: string
27
27
  example: 'Bob'
28
28
  responseCodes:
29
+ - status: 400
30
+ successful: true
31
+ description: ''
29
32
  - status: 200
30
33
  successful: true
31
34
  description: ''
@@ -54,7 +57,37 @@ Feature: schema updating within test suite
54
57
  id: '1'
55
58
  controller: api/v2/users
56
59
  action: update
57
- suffix: ''
60
+ """
61
+
62
+ Scenario: json schema tests response parameters and request parameters and show errors from both using "users/update"
63
+ Given a file named "spec/controllers/api/v2/users_controller_blank_spec.rb" with:
64
+ """ruby
65
+ require "spec_helper"
66
+
67
+ describe Api::V2::UsersController, :lurker do
68
+ render_views
69
+
70
+ let(:user) do
71
+ User.where(name: 'razum2um', surname: 'Unknown').first_or_create!
72
+ end
73
+
74
+ it "updates a user surname as string" do
75
+ patch :update, id: user.id, user: { name: '', surname: 'Marley' }
76
+ expect(response).not_to be_success
77
+ end
78
+ end
79
+ """
80
+
81
+ When I run `bin/rspec spec/controllers/api/v2/users_controller_blank_spec.rb`
82
+ Then the output should contain failures:
83
+ """
84
+ Lurker::ValidationError:
85
+ Request
86
+ The property '#/user' contains additional properties ["surname"]
87
+ Response
88
+ The property '#/' contains additional properties ["errors"]
89
+
90
+ 1 example, 1 failure
58
91
  """
59
92
 
60
93
  Scenario: json schema tests response parameters and update request parameters using "users/update"
@@ -105,6 +138,9 @@ Feature: schema updating within test suite
105
138
  type: string
106
139
  example: Marley
107
140
  responseCodes:
141
+ - status: 400
142
+ successful: true
143
+ description: ''
108
144
  - status: 200
109
145
  successful: true
110
146
  description: ''
@@ -133,6 +169,5 @@ Feature: schema updating within test suite
133
169
  id: '1'
134
170
  controller: api/v2/users
135
171
  action: update
136
- suffix: ''
137
172
 
138
173
  """
@@ -47,7 +47,6 @@ Feature: test endpoint
47
47
  extensions:
48
48
  path_info: "/api/v1/users/1"
49
49
  method: PATCH
50
- suffix: ''
51
50
  path_params:
52
51
  action: update
53
52
  controller: api/v1/users
@@ -47,7 +47,9 @@ module Lurker
47
47
  class UndocumentedResponseCode < ValidationError; end
48
48
  end
49
49
 
50
+ require 'lurker/jaml_descriptor'
50
51
  require 'lurker/schema'
52
+ require 'lurker/json_schema_hash'
51
53
  require 'lurker/schema_modifier'
52
54
  require 'lurker/schema_modifier/hash'
53
55
  require 'lurker/schema_modifier/array'
@@ -55,10 +57,12 @@ require 'lurker/schema_modifier/atom'
55
57
  require 'lurker/ref_object'
56
58
  require 'lurker/erb_schema_context'
57
59
  require 'lurker/service'
58
- require 'lurker/jaml_descriptor'
59
60
  require 'lurker/validator'
60
61
  require 'lurker/validation_error'
62
+ require 'lurker/utils'
61
63
  require 'lurker/endpoint'
64
+ require 'lurker/endpoint/response_codes'
65
+ require 'lurker/endpoint/http_parameters'
62
66
  require 'lurker/rendering_controller'
63
67
  require 'lurker/form_builder'
64
68
  require 'lurker/presenters/json_presenter'
@@ -4,221 +4,166 @@ require 'json-schema'
4
4
 
5
5
  # Endpoints represent the schema for an API endpoint
6
6
  # The #consume_* methods will raise exceptions if input differs from the schema
7
- class Lurker::Endpoint
8
-
9
- attr_reader :schema, :service, :endpoint_path, :current_scaffold, :extensions
10
- attr_accessor :errors
11
-
12
- def initialize(endpoint_path, extensions={}, service=Lurker::Service.default_service)
13
- @endpoint_path = endpoint_path
14
- @extensions = extensions
15
- @service = service
16
- @errors = []
17
- @persisted = false
18
- @schema = File.exist?(endpoint_path) ? load_schema : build_schema
19
- end
20
-
21
- def persist!
22
- schema.ordered!.write_to(endpoint_path)
23
- @persisted = true
24
- end
25
-
26
- def indexed?
27
- prefix.present? && description.present?
28
- end
29
-
30
- def consume!(request_params, response_params, status_code, successful=true)
31
- consume_request(request_params, successful)
32
- consume_response(response_params, status_code, successful)
33
- raise_errors!
34
- end
35
-
36
- def consume_request(params, successful=true)
37
- if successful
38
- Lurker::SchemaModifier.merge!(request_parameters, stringify_keys(params))
7
+ module Lurker
8
+ class Endpoint
9
+ include Lurker::Utils
10
+
11
+ attr_reader :schema, :service, :endpoint_path, :extensions
12
+ attr_reader :request_parameters, :response_parameters, :response_codes
13
+ attr_accessor :errors
14
+
15
+ def initialize(endpoint_path, extensions = {}, service = Lurker::Service.default_service)
16
+ @endpoint_path = endpoint_path
17
+ @extensions = extensions
18
+ @service = service
19
+ @errors = []
20
+ @persisted = false
21
+ @schema = File.exist?(endpoint_path) ? load_schema : build_schema
22
+
23
+ initialize_schema_properties
39
24
  end
40
- end
41
25
 
42
- def consume_response(params, status_code, successful=true)
43
- return validate_response(params, status_code, successful) if persisted?
26
+ def persist!
27
+ schema.ordered! unless persisted?
28
+ schema.write_to(endpoint_path)
44
29
 
45
- if successful
46
- Lurker::SchemaModifier.merge!(response_parameters, stringify_keys(params))
30
+ @persisted = true
47
31
  end
48
32
 
49
- if !status_code_exists?(status_code, successful)
50
- response_code = {
51
- "status" => status_code,
52
- "successful" => successful,
53
- "description" => ""
54
- }
55
-
56
- Lurker::SchemaModifier.append!(response_codes, response_code)
33
+ def indexed?
34
+ prefix.present? && description.present?
57
35
  end
58
- end
59
36
 
60
- def verb
61
- @verb ||= endpoint_path.match(/([A-Z]*)\.json(\.yml)?(\.erb)?$/)[1]
62
- end
37
+ def consume!(request_params, response_params, status_code, successful = true)
38
+ consume_request(request_params, successful)
39
+ consume_response(response_params, status_code, successful)
63
40
 
64
- def path
65
- @path ||= endpoint_path.
66
- gsub(service.service_dir, "").
67
- match(/\/?(.*)[-\/][A-Z]+\.json(\.yml)?(\.erb)?$/)[1]
68
- end
41
+ raise_errors!
42
+ end
69
43
 
70
- # properties
44
+ def consume_request(params, successful = true)
45
+ request_parameters.validate(params) if persisted?
46
+ request_parameters.add(params) if successful
47
+ end
71
48
 
72
- def deprecated?
73
- @schema["deprecated"]
74
- end
49
+ def consume_response(params, status_code, successful = true)
50
+ if persisted?
51
+ response_codes.validate!(status_code, successful)
52
+ response_parameters.validate(params) if successful
75
53
 
54
+ return
55
+ end
76
56
 
77
- def prefix
78
- @schema["prefix"]
79
- end
57
+ response_parameters.add(params) if successful
58
+ response_codes.add(status_code, successful) unless response_codes.exists?(
59
+ status_code, successful)
60
+ end
80
61
 
81
- def description
82
- @schema["description"]
83
- end
62
+ def verb
63
+ @verb ||= endpoint_path.match(/([A-Z]*)\.json(\.yml)?(\.erb)?$/)[1]
64
+ end
84
65
 
85
- def url_params
86
- (schema.extensions['path_params'] || {}).reject { |k, _| ['action', 'controller', 'format'].include? k }
87
- end
66
+ def path
67
+ @path ||= endpoint_path.
68
+ gsub(service.service_dir, "").
69
+ match(/\/?(.*)[-\/][A-Z]+\.json(\.yml)?(\.erb)?$/)[1]
70
+ end
88
71
 
89
- def query_params
90
- (schema.extensions['query_params'] || {})
91
- end
72
+ # properties
92
73
 
93
- def request_parameters
94
- @schema["requestParameters"] ||= {}
95
- end
74
+ def deprecated?
75
+ @schema["deprecated"]
76
+ end
96
77
 
97
- def response_parameters
98
- @schema["responseParameters"] ||= {}
99
- end
78
+ def prefix
79
+ @schema["prefix"]
80
+ end
100
81
 
101
- def response_codes
102
- @schema["responseCodes"] ||= []
103
- end
82
+ def description
83
+ @schema["description"]
84
+ end
104
85
 
105
- protected
86
+ def url_params
87
+ (schema.extensions['path_params'] || {}).reject { |k, _| %w(action controller format).include? k }
88
+ end
106
89
 
107
- def persisted?
108
- !!@persisted
109
- end
90
+ def query_params
91
+ (schema.extensions['query_params'] || {})
92
+ end
110
93
 
111
- def load_schema
112
- @persisted = true
94
+ protected
113
95
 
114
- Lurker::Schema.new(
115
- load_file(endpoint_path),
116
- stringify_keys(extensions)
117
- )
118
- end
96
+ def initialize_schema_properties
97
+ @response_codes = ResponseCodes.new(schema)
119
98
 
120
- def build_schema
121
- @persisted = false
122
-
123
- Lurker::Schema.new(
124
- {
125
- "prefix" => "",
126
- "description" => "",
127
- "responseCodes" => []
128
- },
129
- stringify_keys(extensions)
130
- )
131
- end
99
+ @response_parameters = HttpParameters.new(schema,
100
+ schema_key: 'responseParameters', schema_id: endpoint_path, human_name: 'Response')
132
101
 
133
- def load_file(fname)
134
- if fname.match(/\.erb$/)
135
- context = Lurker::ErbSchemaContext.new
136
- erb = ERB.new(IO.read(fname)).result(context.get_binding)
137
- YAML.load(erb)
138
- else
139
- YAML.load_file(fname)
102
+ @request_parameters = HttpParameters.new(schema,
103
+ schema_key: 'requestParameters', schema_id: endpoint_path, human_name: 'Request')
140
104
  end
141
- end
142
105
 
143
- def validate(expected_params, given_params, prefix=nil)
144
- schema = set_additional_properties_false_on(expected_params.dup)
145
- schema['id'] = "file://#{endpoint_path}"
146
- unless (_errors = Lurker::Validator.new(schema, stringify_keys(given_params), record_errors: true).validate).empty?
147
- self.errors << prefix
148
- _errors.each { |e| self.errors << "- #{e}" }
149
- return false
106
+ def persisted?
107
+ !!@persisted
150
108
  end
151
- true
152
- end
153
109
 
154
- def validate_response(params, status_code, successful)
155
- if !status_code_exists?(status_code, successful)
156
- raise Lurker::UndocumentedResponseCode,
157
- 'Undocumented response: %s, successful: %s' % [
158
- status_code, successful
159
- ]
160
- elsif successful
161
- validate(response_parameters, params, 'Response')
162
- else
163
- true
110
+ def load_schema
111
+ @persisted = true
112
+
113
+ Lurker::Schema.new(
114
+ load_file(endpoint_path),
115
+ stringify_keys(extensions)
116
+ )
164
117
  end
165
- end
166
118
 
167
- def status_code_exists?(status_code, successful)
168
- !!response_codes.detect do |code|
169
- code["successful"] == successful &&
170
- (code["status"] == status_code || # 200
171
- code["status"].to_i == status_code) # "200 OK"
119
+ def build_schema
120
+ @persisted = false
121
+
122
+ Lurker::Schema.new(
123
+ {
124
+ "prefix" => "",
125
+ "description" => "",
126
+ "responseCodes" => []
127
+ },
128
+ stringify_keys(extensions)
129
+ )
172
130
  end
173
- end
174
131
 
175
- def raise_errors!
176
- unless errors.empty?
177
- raise Lurker::ValidationError.new(word_wrap((
178
- # ['Schema', "- #{endpoint_path}"] +
179
- errors
180
- # + ['Diff', current_scaffold.schema.diff(schema)]
181
- ).join("\n")))
132
+ def load_file(fname)
133
+ if fname.match(/\.erb$/)
134
+ context = Lurker::ErbSchemaContext.new
135
+ erb = ERB.new(IO.read(fname)).result(context.get_binding)
136
+ YAML.load(erb)
137
+ else
138
+ YAML.load_file(fname)
139
+ end
182
140
  end
183
- end
184
141
 
185
- def word_wrap(text)
186
- text.gsub(/\s+in schema/m, "\n in schema")
187
- end
142
+ def raise_errors!
143
+ return if response_parameters.errors.empty?
188
144
 
189
- # default additionalProperties on objects to false
190
- # create a copy, so we don't mutate the input
191
- def set_additional_properties_false_on(value)
192
- if value.kind_of? Hash
193
- copy = value.dup
194
- if value["type"] == "object" || value.has_key?("properties")
195
- copy["additionalProperties"] ||= false
196
- end
197
- value.each do |key, hash_val|
198
- unless key == "additionalProperties"
199
- copy[key] = set_additional_properties_false_on(hash_val)
200
- end
145
+ errors = (request_parameters.errors | response_parameters.errors) * "\n"
146
+ exception = Lurker::ValidationError.new(word_wrap errors)
147
+ if (example = Lurker::Spy.current.block).respond_to?(:metadata) && (metadata = example.metadata).respond_to?(:location, true)
148
+ exception.set_backtrace [metadata.send(:location)]
201
149
  end
202
- copy
203
- elsif value.kind_of? Array
204
- copy = value.map do |arr_val|
205
- set_additional_properties_false_on(arr_val)
206
- end
207
- else
208
- value
150
+ raise exception
209
151
  end
210
- end
211
152
 
212
- def stringify_keys(obj)
213
- case obj
214
- when Hash
215
- result = {}
216
- obj.each do |k, v|
217
- result[k.to_s] = stringify_keys(v)
153
+ def word_wrap(text)
154
+ # strip .json# | .json.yml# | .json.yml.erb#
155
+ text = text.reverse
156
+ text.gsub!(/(\n|^)#bre\./, "\nbre.")
157
+ text.gsub!(/(\n|^)#lmy\./, "\nlmy.")
158
+ text.gsub!(/(\n|^)#nosj\./, "\nnosj.")
159
+ text.strip!
160
+ text = text.reverse
161
+
162
+ text.gsub!(/\s+in schema/m, "\n in schema")
163
+ if defined?(Rails)
164
+ text.gsub!(/file:\/\/#{Rails.root}\//m, "")
218
165
  end
219
- result
220
- when Array then obj.map { |v| stringify_keys(v) }
221
- else obj
166
+ text
222
167
  end
223
168
  end
224
169
  end