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.
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')