angus 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- 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')
|