angus 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +15 -0
  2. data/lib/angus/base.rb +6 -3
  3. data/lib/angus/base_proxy.rb +20 -0
  4. data/lib/angus/base_resource.rb +8 -3
  5. data/lib/angus/generator/templates/definitions/operations.yml.erb +2 -0
  6. data/lib/angus/generator/templates/resources/resource.rb.erb +2 -2
  7. data/lib/angus/generator/thor/resource.rb +1 -1
  8. data/lib/angus/middleware/exception_handler.rb +2 -2
  9. data/lib/angus/request_handler.rb +7 -7
  10. data/lib/angus/rspec/support/examples.rb +3 -2
  11. data/lib/angus/rspec/support/examples/describe_errors.rb +23 -6
  12. data/lib/angus/utils/exceptions/invalid_parameter_type.rb +18 -0
  13. data/lib/angus/utils/exceptions/required_parameter_not_fond.rb +19 -0
  14. data/lib/angus/utils/params_validator.rb +81 -0
  15. data/spec/angus/middleware/exception_handler_spec.rb +16 -1
  16. data/spec/angus/rspec/examples/describe_errors_spec.rb +6 -0
  17. data/spec/functional/basic_spec.rb +2 -2
  18. data/spec/functional/empty_resource_spec.rb +2 -2
  19. data/spec/functional/no_resources_spec.rb +2 -2
  20. data/spec/functional/type_validation/definitions/admins/operations.yml +70 -0
  21. data/spec/functional/type_validation/definitions/messages.yml +0 -0
  22. data/spec/functional/type_validation/definitions/representations.yml +9 -0
  23. data/spec/functional/type_validation/definitions/service.yml +3 -0
  24. data/spec/functional/type_validation/models/admin.rb +1 -0
  25. data/spec/functional/type_validation/resources/admins.rb +35 -0
  26. data/spec/functional/type_validation/services/type_validation.rb +13 -0
  27. data/spec/functional/type_validation_spec.rb +283 -0
  28. data/spec/support/matchers/have_error_response_matcher.rb +40 -0
  29. data/spec/support/matchers/have_in_message_matcher.rb +47 -0
  30. data/spec/support/matchers/have_success_response_matcher.rb +40 -0
  31. 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 = Rack::Request.new(env)
124
- params = Params.indifferent_params(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
@@ -5,9 +5,14 @@ module Angus
5
5
 
6
6
  attr_reader :request, :params
7
7
 
8
- def initialize(request, params)
9
- @request = request
10
- @params = 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: 'User one' }, { id: 2, name: 'User two' }] }
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
@@ -11,7 +11,7 @@ class Thor
11
11
  #
12
12
  # @example
13
13
  #
14
- # resource 'user', 'resouces', 'definitions'
14
+ # resource 'user', 'resources', 'definitions'
15
15
  #
16
16
  def resource(name, actions, config = {})
17
17
  action Resource.new(self, name, actions, config)
@@ -8,9 +8,9 @@ module Angus
8
8
  class ExceptionHandler
9
9
  include Angus::StatusCodes
10
10
 
11
- def initialize(app, definitions = nil)
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 { |env| self.dup.call!(env) }
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
- # HACK to improve performance for now, in reality Middleware::ExceptionHandler should get
38
- # the doc from a know place or the documentation should be available to all middleware.
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, service, &block)
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 Runing example
11
+ # @param [#app] example Running example
12
12
  def self.mock_service(base, error, example, &block)
13
13
 
14
- mock = Class.new(base) do
15
- get '/' do
16
- raise error
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,18 @@
1
+ module Angus
2
+ class InvalidParameterType < StandardError
3
+
4
+ def initialize(field, param)
5
+ @field = field
6
+ @param = param
7
+ end
8
+
9
+ def error_key
10
+ 'TODO define'
11
+ end
12
+
13
+ def message
14
+ 'TODO define'
15
+ end
16
+
17
+ end
18
+ 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(:app) { double(:app, :call => app_response) }
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 a html content type' do
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 a expected error happens' do
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 a unknown url' do
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 a html content type' do
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 a unknown url' do
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 a html content type' do
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')