angus 0.0.9 → 0.0.10
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 +15 -0
- data/lib/angus/base.rb +6 -3
- data/lib/angus/base_proxy.rb +20 -0
- data/lib/angus/base_resource.rb +8 -3
- data/lib/angus/generator/templates/definitions/operations.yml.erb +2 -0
- data/lib/angus/generator/templates/resources/resource.rb.erb +2 -2
- data/lib/angus/generator/thor/resource.rb +1 -1
- data/lib/angus/middleware/exception_handler.rb +2 -2
- data/lib/angus/request_handler.rb +7 -7
- data/lib/angus/rspec/support/examples.rb +3 -2
- data/lib/angus/rspec/support/examples/describe_errors.rb +23 -6
- data/lib/angus/utils/exceptions/invalid_parameter_type.rb +18 -0
- data/lib/angus/utils/exceptions/required_parameter_not_fond.rb +19 -0
- data/lib/angus/utils/params_validator.rb +81 -0
- data/spec/angus/middleware/exception_handler_spec.rb +16 -1
- data/spec/angus/rspec/examples/describe_errors_spec.rb +6 -0
- data/spec/functional/basic_spec.rb +2 -2
- data/spec/functional/empty_resource_spec.rb +2 -2
- data/spec/functional/no_resources_spec.rb +2 -2
- data/spec/functional/type_validation/definitions/admins/operations.yml +70 -0
- data/spec/functional/type_validation/definitions/messages.yml +0 -0
- data/spec/functional/type_validation/definitions/representations.yml +9 -0
- data/spec/functional/type_validation/definitions/service.yml +3 -0
- data/spec/functional/type_validation/models/admin.rb +1 -0
- data/spec/functional/type_validation/resources/admins.rb +35 -0
- data/spec/functional/type_validation/services/type_validation.rb +13 -0
- data/spec/functional/type_validation_spec.rb +283 -0
- data/spec/support/matchers/have_error_response_matcher.rb +40 -0
- data/spec/support/matchers/have_in_message_matcher.rb +47 -0
- data/spec/support/matchers/have_success_response_matcher.rb +40 -0
- metadata +95 -93
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZTQ5Yzg0NjIyMTE1Y2MyMTA3MjkwM2VlMTFkOTNkMGY0NDIyYmYwNw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NzEzOTg0NzJjZGY5NjA3ZmVkNTg1MGFhNTExZDU0Yzk5OTQ3OTlhNw==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OGJkNzEyZWYzMWNhM2U1OGE5NmNiM2U3MDc4NGQ2Yzc2ZTY2OWI3MmZlN2Nm
|
10
|
+
ZmZjNjUyZjNlMDY3YTkwZTg5ODNiMjZjMjQ5YmI1NjE0ZTk2M2E3MmQzN2Jj
|
11
|
+
ZGFjZTAwMGFhN2U1OTM4NjQ5ZjA4OWQ3NDQzYmZmNGUzNDZiMTc=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MjVjMTQ5ZmRmYWE2NzMzYTJkNzAwYzc2MmQ3N2RhNjE2ODU4MzliMTYzYjcx
|
14
|
+
ODM5Njk0YmJhNDkyY2ZlYTgxZTE3ZTg1ZmY2YTc3ODQyMjM4OWU1Yjc4MmZl
|
15
|
+
YzQ1M2E0MmM1NTkwMzM1N2RiZDA3YWQ2Mzg5ZjlmZDM2MzdhY2I=
|
data/lib/angus/base.rb
CHANGED
@@ -12,6 +12,8 @@ require_relative 'base_actions'
|
|
12
12
|
require_relative 'proxy_actions'
|
13
13
|
require_relative 'definition_reader'
|
14
14
|
|
15
|
+
require_relative 'utils/params_validator'
|
16
|
+
|
15
17
|
require 'angus/sdoc'
|
16
18
|
|
17
19
|
module Angus
|
@@ -120,11 +122,12 @@ module Angus
|
|
120
122
|
response_metadata = resource_definition.build_response_metadata(operation.response_elements)
|
121
123
|
|
122
124
|
router.on(method, op_path) do |env, params|
|
123
|
-
request
|
124
|
-
params
|
125
|
+
request = Rack::Request.new(env)
|
126
|
+
params = Params.indifferent_params(params)
|
125
127
|
response = Response.new
|
126
128
|
|
127
|
-
resource = resource_definition.resource_class.new(request, params)
|
129
|
+
resource = resource_definition.resource_class.new(request, params, operation)
|
130
|
+
resource.run_validations!
|
128
131
|
|
129
132
|
op_response = resource.send(operation.code_name)
|
130
133
|
op_response = {} unless op_response.is_a?(Hash)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Angus
|
2
|
+
|
3
|
+
class BaseProxy
|
4
|
+
|
5
|
+
def initialize(request_handler, definition_block)
|
6
|
+
@request_handler = request_handler
|
7
|
+
@definition_block = definition_block
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
@request_handler.dup.call!(env)
|
12
|
+
end
|
13
|
+
|
14
|
+
def definitions
|
15
|
+
@definition_block.call
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/lib/angus/base_resource.rb
CHANGED
@@ -5,9 +5,14 @@ module Angus
|
|
5
5
|
|
6
6
|
attr_reader :request, :params
|
7
7
|
|
8
|
-
def initialize(request, params)
|
9
|
-
@request
|
10
|
-
@params
|
8
|
+
def initialize(request, params, operation)
|
9
|
+
@request = request
|
10
|
+
@params = params
|
11
|
+
@operation = operation
|
12
|
+
end
|
13
|
+
|
14
|
+
def run_validations!
|
15
|
+
ParamsValidator.new(@operation).valid?(@params)
|
11
16
|
end
|
12
17
|
|
13
18
|
end
|
@@ -17,8 +17,10 @@
|
|
17
17
|
required: <%= response_required(action_name) %>
|
18
18
|
type: <%= response_type(action_name) %>
|
19
19
|
|
20
|
+
<% if is_demo? %>
|
20
21
|
messages:
|
21
22
|
- key: <%= message_key(action_name) %>
|
22
23
|
description: '<%= message_description(action_name) %>'
|
24
|
+
<% end %>
|
23
25
|
<% end %>
|
24
26
|
<% end %>
|
@@ -3,9 +3,9 @@ class <%= classify(resource_name) %> < Angus::BaseResource
|
|
3
3
|
<% actions.each do |action_name| %>
|
4
4
|
def <%= action_name %>
|
5
5
|
<% if is_demo? -%>
|
6
|
-
{ users: [{ id: 1, name: '
|
6
|
+
{ users: [{ id: 1, name: 'Bon Scott' }, { id: 2, name: 'Brian Johnson' }] }
|
7
7
|
<% end -%>
|
8
8
|
end
|
9
9
|
<% end %>
|
10
10
|
<% end -%>
|
11
|
-
end
|
11
|
+
end
|
@@ -8,9 +8,9 @@ module Angus
|
|
8
8
|
class ExceptionHandler
|
9
9
|
include Angus::StatusCodes
|
10
10
|
|
11
|
-
def initialize(app
|
11
|
+
def initialize(app)
|
12
12
|
@app = app
|
13
|
-
@definition_reader = Angus::DefinitionReader.new(definitions)
|
13
|
+
@definition_reader = Angus::DefinitionReader.new(app.base_middleware.definitions)
|
14
14
|
end
|
15
15
|
|
16
16
|
def call(env)
|
@@ -4,6 +4,7 @@ require_relative 'response'
|
|
4
4
|
require_relative 'responses'
|
5
5
|
require_relative 'middleware/exception_handler'
|
6
6
|
require_relative 'exceptions'
|
7
|
+
require_relative 'base_proxy'
|
7
8
|
|
8
9
|
module Angus
|
9
10
|
class RequestHandler
|
@@ -30,17 +31,16 @@ module Angus
|
|
30
31
|
end
|
31
32
|
|
32
33
|
def to_app
|
33
|
-
inner_app = lambda {
|
34
|
+
inner_app = BaseProxy.new(self, lambda { @definitions })
|
35
|
+
|
34
36
|
@app ||= @middleware.reverse.inject(inner_app) do |app, middleware|
|
35
37
|
klass, args, block = middleware
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
-
if klass == Middleware::ExceptionHandler
|
40
|
-
klass.new(app, @definitions)
|
41
|
-
else
|
42
|
-
klass.new(app, *args, &block)
|
39
|
+
app.class.send(:define_method, :base_middleware) do
|
40
|
+
inner_app
|
43
41
|
end
|
42
|
+
|
43
|
+
klass.new(app, *args, &block)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
@@ -11,7 +11,8 @@ require_relative 'examples/describe_errors'
|
|
11
11
|
#
|
12
12
|
# @param operation the operation being specified, ex: GET /profiles
|
13
13
|
# @param service the Service (rack app) that exposes the operation
|
14
|
-
def describe_operation(operation,
|
14
|
+
def describe_operation(operation, service_class, &block)
|
15
|
+
service = service_class.new
|
15
16
|
|
16
17
|
describe(operation) do
|
17
18
|
include Rack::Test::Methods
|
@@ -44,7 +45,7 @@ def describe_operation(operation, service, &block)
|
|
44
45
|
it "raises #{e} with status = #{status_code}", :caller => __caller do
|
45
46
|
|
46
47
|
Angus::RSpec::Examples::DescribeErrors.mock_service(service, e, self) do
|
47
|
-
get '/'
|
48
|
+
get '/error'
|
48
49
|
|
49
50
|
if response.http_status_code != status_code
|
50
51
|
e = RSpec::Expectations::ExpectationNotMetError.new(
|
@@ -8,17 +8,34 @@ module Angus
|
|
8
8
|
#
|
9
9
|
# @param [Class] Class that responds to .get
|
10
10
|
# @param error Exception that will be raised when executing a get route
|
11
|
-
# @param [#app] example
|
11
|
+
# @param [#app] example Running example
|
12
12
|
def self.mock_service(base, error, example, &block)
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
base_class = if base.is_a?(Class)
|
15
|
+
base
|
16
|
+
else
|
17
|
+
base.class
|
18
|
+
end
|
19
|
+
|
20
|
+
mock = Class.new(base_class) do
|
21
|
+
def initialize(base, error)
|
22
|
+
@base = base
|
23
|
+
@error = error
|
24
|
+
|
25
|
+
super()
|
26
|
+
|
27
|
+
router.on(:get, '/error') do
|
28
|
+
raise error
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def class
|
33
|
+
@base
|
17
34
|
end
|
18
35
|
end
|
19
36
|
|
20
37
|
example.define_singleton_method :app do
|
21
|
-
mock
|
38
|
+
mock.new(base_class, error)
|
22
39
|
end
|
23
40
|
|
24
41
|
begin
|
@@ -33,4 +50,4 @@ module Angus
|
|
33
50
|
end
|
34
51
|
end
|
35
52
|
end
|
36
|
-
end
|
53
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Angus
|
2
|
+
class RequiredParameterNotFound < StandardError
|
3
|
+
|
4
|
+
attr_accessor :not_found_parameters
|
5
|
+
|
6
|
+
def initialize(not_found_parameters)
|
7
|
+
@not_found_parameters = not_found_parameters
|
8
|
+
end
|
9
|
+
|
10
|
+
def error_key
|
11
|
+
'TODO define'
|
12
|
+
end
|
13
|
+
|
14
|
+
def message
|
15
|
+
@not_found_parameters.map(&:name).join(', ')
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative 'exceptions/required_parameter_not_fond'
|
2
|
+
require_relative 'exceptions/invalid_parameter_type'
|
3
|
+
|
4
|
+
module Angus
|
5
|
+
class ParamsValidator
|
6
|
+
|
7
|
+
DEFAULT_DATE_FORMAT = '%Y-%m-%d'
|
8
|
+
DEFAULT_DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S%z'
|
9
|
+
|
10
|
+
def initialize(operation)
|
11
|
+
@operation = operation
|
12
|
+
@request_elements = operation.request_elements
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid?(params)
|
16
|
+
all_required_fields?(params)
|
17
|
+
all_valid_types?(params)
|
18
|
+
end
|
19
|
+
|
20
|
+
def all_required_fields?(params)
|
21
|
+
required_fields = @request_elements.select(&:required)
|
22
|
+
|
23
|
+
no_found_parameters = required_fields.select {|rf| !params.include?(rf.name.to_sym) }
|
24
|
+
|
25
|
+
unless no_found_parameters.empty?
|
26
|
+
raise RequiredParameterNotFound.new(no_found_parameters)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def all_valid_types?(params)
|
31
|
+
@request_elements.each do |field|
|
32
|
+
type = field.type
|
33
|
+
method_name = "valid_#{type}?"
|
34
|
+
|
35
|
+
if self.respond_to?(method_name)
|
36
|
+
param = params[field.name.to_sym]
|
37
|
+
|
38
|
+
valid = self.send(method_name, field, param)
|
39
|
+
|
40
|
+
raise InvalidParameterType.new(field, param) unless valid
|
41
|
+
else
|
42
|
+
# TODO handle complex types
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# String are all ways valid
|
48
|
+
def valid_string?(field, param)
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def valid_integer?(field, param)
|
53
|
+
!!(param =~ /^[-+]?[0-9]+$/)
|
54
|
+
end
|
55
|
+
|
56
|
+
def valid_decimal?(field, param)
|
57
|
+
!!(param =~/^[-+]?([0-9]+(\.[0-9]+)?$)/)
|
58
|
+
end
|
59
|
+
|
60
|
+
def valid_date?(field, param)
|
61
|
+
begin
|
62
|
+
Date.strptime(param, DEFAULT_DATE_FORMAT)
|
63
|
+
|
64
|
+
true
|
65
|
+
rescue ArgumentError
|
66
|
+
false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def valid_datetime?(field, param)
|
71
|
+
begin
|
72
|
+
DateTime.strptime(param, DEFAULT_DATETIME_FORMAT)
|
73
|
+
|
74
|
+
true
|
75
|
+
rescue ArgumentError
|
76
|
+
false
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -1,11 +1,26 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
require 'angus/base_proxy'
|
3
4
|
require 'angus/middleware/exception_handler'
|
4
5
|
|
5
6
|
work_dir = File.join(File.dirname(__FILE__), '..', '..', 'functional', 'basic')
|
6
7
|
describe Angus::Middleware::ExceptionHandler, { :work_dir => work_dir } do
|
7
8
|
|
8
|
-
let(:
|
9
|
+
let(:request_handler) {
|
10
|
+
double(:request_handler, call: double(:app))
|
11
|
+
}
|
12
|
+
|
13
|
+
let(:definition_block) {
|
14
|
+
double(:definition_handler, call: nil)
|
15
|
+
}
|
16
|
+
|
17
|
+
let(:base_proxy) {
|
18
|
+
Angus::BaseProxy.new(request_handler, definition_block)
|
19
|
+
}
|
20
|
+
|
21
|
+
let(:app) {
|
22
|
+
double(:app, :call => app_response,
|
23
|
+
:base_middleware => base_proxy) }
|
9
24
|
|
10
25
|
let(:app_response) { :ok }
|
11
26
|
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
require 'angus-router'
|
4
|
+
|
3
5
|
require 'angus/rspec/support/examples/describe_errors'
|
4
6
|
|
5
7
|
describe Angus::RSpec::Examples::DescribeErrors do
|
@@ -11,6 +13,10 @@ describe Angus::RSpec::Examples::DescribeErrors do
|
|
11
13
|
def self.get(path, &block)
|
12
14
|
:get_v0
|
13
15
|
end
|
16
|
+
|
17
|
+
def router
|
18
|
+
@router ||= Angus::Router.new
|
19
|
+
end
|
14
20
|
end
|
15
21
|
end
|
16
22
|
|
@@ -32,7 +32,7 @@ describe Spec::Functional::Basic, { :work_dir => "#{File.dirname(__FILE__ )}/bas
|
|
32
32
|
end
|
33
33
|
|
34
34
|
context 'when no format' do
|
35
|
-
it 'sets
|
35
|
+
it 'sets an html content type' do
|
36
36
|
get '/basic/doc/0.1'
|
37
37
|
|
38
38
|
last_response.header['Content-Type'].should eq('text/html;charset=utf-8')
|
@@ -63,7 +63,7 @@ describe Spec::Functional::Basic, { :work_dir => "#{File.dirname(__FILE__ )}/bas
|
|
63
63
|
last_response.header['Content-Type'].should eq('application/json')
|
64
64
|
end
|
65
65
|
|
66
|
-
context 'when
|
66
|
+
context 'when an expected error happens' do
|
67
67
|
it 'sets the correct status code' do
|
68
68
|
get '/basic/api/0.1/users/-1'
|
69
69
|
|
@@ -16,7 +16,7 @@ describe Spec::Functional::EmptyResource,
|
|
16
16
|
last_response.status.should eq(200)
|
17
17
|
end
|
18
18
|
|
19
|
-
describe 'when
|
19
|
+
describe 'when an unknown url' do
|
20
20
|
|
21
21
|
let(:url) { '/empty_resource/api/0.1/unknown' }
|
22
22
|
|
@@ -45,7 +45,7 @@ describe Spec::Functional::EmptyResource,
|
|
45
45
|
end
|
46
46
|
|
47
47
|
context 'when no format' do
|
48
|
-
it 'sets
|
48
|
+
it 'sets an html content type' do
|
49
49
|
get url
|
50
50
|
|
51
51
|
last_response.header['Content-Type'].should eq('text/html;charset=utf-8')
|
@@ -16,7 +16,7 @@ describe Spec::Functional::NoResources,
|
|
16
16
|
last_response.status.should eq(200)
|
17
17
|
end
|
18
18
|
|
19
|
-
describe 'when
|
19
|
+
describe 'when an unknown url' do
|
20
20
|
|
21
21
|
let(:url) { '/no_resources/api/0.1/unknown' }
|
22
22
|
|
@@ -45,7 +45,7 @@ describe Spec::Functional::NoResources,
|
|
45
45
|
end
|
46
46
|
|
47
47
|
context 'when no format' do
|
48
|
-
it 'sets
|
48
|
+
it 'sets an html content type' do
|
49
49
|
get url
|
50
50
|
|
51
51
|
last_response.header['Content-Type'].should eq('text/html;charset=utf-8')
|