praxis 0.13.0 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +15 -2
- data/CHANGELOG.md +54 -1
- data/bin/praxis +49 -2
- data/lib/api_browser/Gruntfile.js +247 -90
- data/lib/api_browser/app/bower_components/angular-mocks/.bower.json +19 -0
- data/lib/api_browser/app/bower_components/angular-mocks/README.md +57 -0
- data/lib/api_browser/app/bower_components/angular-mocks/angular-mocks.js +2193 -0
- data/lib/api_browser/app/bower_components/angular-mocks/bower.json +9 -0
- data/lib/api_browser/app/bower_components/angular-mocks/package.json +27 -0
- data/lib/api_browser/app/bower_components/angular/.bower.json +6 -5
- data/lib/api_browser/app/bower_components/angular/README.md +23 -4
- data/lib/api_browser/app/bower_components/angular/angular-csp.css +6 -0
- data/lib/api_browser/app/bower_components/angular/angular.js +2287 -1597
- data/lib/api_browser/app/bower_components/angular/angular.min.js +212 -205
- data/lib/api_browser/app/bower_components/angular/angular.min.js.gzip +0 -0
- data/lib/api_browser/app/bower_components/angular/angular.min.js.map +3 -3
- data/lib/api_browser/app/bower_components/angular/bower.json +2 -1
- data/lib/api_browser/app/bower_components/angular/package.json +25 -0
- data/lib/api_browser/app/bower_components/showdown/.bower.json +39 -0
- data/lib/api_browser/app/bower_components/showdown/.jshintignore +2 -0
- data/lib/api_browser/app/bower_components/showdown/.travis.yml +8 -0
- data/lib/api_browser/app/bower_components/showdown/Gruntfile.js +100 -0
- data/lib/api_browser/app/bower_components/showdown/README.md +317 -0
- data/lib/api_browser/app/bower_components/showdown/bower.json +26 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.js +1606 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.js.map +1 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.min.js +2 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/github.min.js +2 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/github.min.js.map +1 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/prettify.min.js +2 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/prettify.min.js.map +1 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/table.min.js +2 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/table.min.js.map +1 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/twitter.min.js +2 -0
- data/lib/api_browser/app/bower_components/showdown/compressed/extensions/twitter.min.js.map +1 -0
- data/lib/api_browser/app/bower_components/showdown/license.txt +34 -0
- data/lib/api_browser/app/bower_components/showdown/package.json +47 -0
- data/lib/api_browser/app/bower_components/showdown/src/extensions/github.js +25 -0
- data/lib/api_browser/app/bower_components/showdown/src/extensions/prettify.js +29 -0
- data/lib/api_browser/app/bower_components/showdown/src/extensions/table.js +106 -0
- data/lib/api_browser/app/bower_components/showdown/src/extensions/twitter.js +42 -0
- data/lib/api_browser/app/bower_components/showdown/src/ng-showdown.js +150 -0
- data/lib/api_browser/app/bower_components/showdown/src/showdown.js +1454 -0
- data/lib/api_browser/app/index.html +6 -4
- data/lib/api_browser/app/js/app.js +1 -2
- data/lib/api_browser/app/js/controllers/action.js +4 -4
- data/lib/api_browser/app/js/controllers/controller.js +1 -1
- data/lib/api_browser/app/js/controllers/menu.js +5 -3
- data/lib/api_browser/app/js/controllers/type.js +5 -5
- data/lib/api_browser/app/js/directives/attribute_description.js +5 -5
- data/lib/api_browser/app/js/directives/attribute_table.js +1 -1
- data/lib/api_browser/app/js/directives/attribute_table_row.js +2 -2
- data/lib/api_browser/app/js/directives/no_container.js +1 -1
- data/lib/api_browser/app/js/directives/request_body.js +5 -5
- data/lib/api_browser/app/js/directives/request_headers.js +3 -6
- data/lib/api_browser/app/js/directives/request_parameters.js +3 -6
- data/lib/api_browser/app/js/directives/type_label.js +4 -5
- data/lib/api_browser/app/js/factories/Documentation.js +4 -4
- data/lib/api_browser/app/js/factories/PayloadTemplates.js +2 -2
- data/lib/api_browser/app/js/factories/TypeTemplates.js +3 -3
- data/lib/api_browser/app/js/filters/markdown.js +6 -0
- data/lib/api_browser/app/js/filters/resource_name.js +2 -2
- data/lib/api_browser/app/sass/modules/_header.scss +2 -7
- data/lib/api_browser/app/sass/{main.scss → praxis.scss} +0 -0
- data/lib/api_browser/app/sass/variables/_bootstrap-variables.scss +370 -367
- data/lib/api_browser/app/views/action.html +2 -2
- data/lib/api_browser/app/views/controller.html +2 -2
- data/lib/api_browser/app/views/directives/attribute_description.html +1 -1
- data/lib/api_browser/app/views/layout.html +2 -11
- data/lib/api_browser/app/views/navbar.html +9 -0
- data/lib/api_browser/app/views/resource/_actions.html +1 -1
- data/lib/api_browser/app/views/type.html +2 -2
- data/lib/api_browser/app/views/type/_details.html +2 -1
- data/lib/api_browser/bower.json +5 -0
- data/lib/api_browser/package.json +18 -7
- data/lib/praxis.rb +8 -3
- data/lib/praxis/action_definition.rb +28 -6
- data/lib/praxis/api_definition.rb +30 -2
- data/lib/praxis/api_general_info.rb +36 -0
- data/lib/praxis/bootloader.rb +1 -0
- data/lib/praxis/collection.rb +34 -0
- data/lib/praxis/controller.rb +7 -0
- data/lib/praxis/dispatcher.rb +3 -0
- data/lib/praxis/links.rb +2 -8
- data/lib/praxis/media_type.rb +6 -24
- data/lib/praxis/media_type_collection.rb +6 -2
- data/lib/praxis/plugin_concern.rb +2 -1
- data/lib/praxis/request.rb +24 -15
- data/lib/praxis/request_stages/request_stage.rb +19 -4
- data/lib/praxis/request_stages/validate_params_and_headers.rb +1 -1
- data/lib/praxis/request_stages/validate_payload.rb +1 -1
- data/lib/praxis/resource_definition.rb +45 -10
- data/lib/praxis/response_definition.rb +46 -27
- data/lib/praxis/restful_doc_generator.rb +94 -7
- data/lib/praxis/simple_media_type.rb +2 -9
- data/lib/praxis/stage.rb +1 -4
- data/lib/praxis/tasks/api_docs.rb +51 -19
- data/lib/praxis/tasks/routes.rb +19 -15
- data/lib/praxis/types/media_type_common.rb +31 -0
- data/lib/praxis/types/multipart.rb +4 -4
- data/lib/praxis/version.rb +1 -1
- data/praxis.gemspec +2 -2
- data/spec/api_browser/factories/documentation_spec.js +50 -0
- data/spec/api_browser/filters/attribute_name_spec.js +23 -0
- data/spec/functional_spec.rb +62 -10
- data/spec/praxis/action_definition_spec.rb +12 -4
- data/spec/praxis/api_definition_spec.rb +159 -0
- data/spec/praxis/api_general_info_spec.rb +36 -0
- data/spec/praxis/bootloader_spec.rb +10 -1
- data/spec/praxis/media_type_collection_spec.rb +46 -53
- data/spec/praxis/media_type_spec.rb +6 -6
- data/spec/praxis/request_stage_spec.rb +7 -2
- data/spec/praxis/request_stages_validate_spec.rb +12 -7
- data/spec/praxis/resource_definition_spec.rb +62 -0
- data/spec/praxis/response_definition_spec.rb +26 -16
- data/spec/praxis/stage_spec.rb +4 -8
- data/spec/praxis/types/collection_spec.rb +144 -0
- data/spec/spec_app/app/controllers/instances.rb +8 -2
- data/spec/spec_app/design/api.rb +11 -0
- data/spec/spec_app/design/media_types/instance.rb +12 -0
- data/spec/spec_app/design/media_types/volume.rb +9 -2
- data/spec/spec_app/design/media_types/volume_snapshot.rb +9 -6
- data/spec/spec_app/design/resources/instances.rb +25 -10
- data/spec/support/spec_media_types.rb +1 -1
- data/spec/support/spec_resource_definitions.rb +2 -0
- data/tasks/thor/app.rb +15 -10
- data/tasks/thor/example.rb +115 -115
- data/tasks/thor/templates/generator/empty_app/.gitignore +2 -0
- data/tasks/thor/templates/generator/empty_app/docs/app.js +1 -0
- data/tasks/thor/templates/generator/empty_app/docs/styles.scss +3 -0
- metadata +50 -9
- data/lib/api_browser/app/css/main.css +0 -4511
- data/lib/praxis/types/collection.rb +0 -17
data/lib/praxis/links.rb
CHANGED
@@ -58,7 +58,7 @@ module Praxis
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
def self.
|
61
|
+
def self.define_reader!(name)
|
62
62
|
# it's faster to use define_method in this case than module_eval
|
63
63
|
# because we save the attribute lookup on every access.
|
64
64
|
attribute = self.attributes[name]
|
@@ -79,12 +79,6 @@ module Praxis
|
|
79
79
|
self.__send__(name)
|
80
80
|
end
|
81
81
|
end
|
82
|
-
|
83
|
-
@reference.attribute.type.instance_eval do
|
84
|
-
define_method(using) do
|
85
|
-
self.__send__(name)
|
86
|
-
end
|
87
|
-
end
|
88
82
|
end
|
89
83
|
|
90
84
|
end
|
@@ -103,7 +97,7 @@ module Praxis
|
|
103
97
|
# This is primarily necessary only for example generation.
|
104
98
|
def self.fixup_reference_struct_methods
|
105
99
|
self.links.each do |name, using|
|
106
|
-
next if @reference.attribute.attributes.has_key?(
|
100
|
+
next if @reference.attribute.attributes.has_key?(using)
|
107
101
|
@reference.attribute.type.instance_eval do
|
108
102
|
define_method(using) do
|
109
103
|
return nil unless attributes[:links]
|
data/lib/praxis/media_type.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Praxis
|
2
2
|
class MediaType < Praxis::Blueprint
|
3
|
+
include Types::MediaTypeCommon
|
3
4
|
|
4
5
|
class DSLCompiler < Attributor::DSLCompiler
|
5
6
|
def links(&block)
|
@@ -7,25 +8,6 @@ module Praxis
|
|
7
8
|
end
|
8
9
|
end
|
9
10
|
|
10
|
-
def self.description(text=nil)
|
11
|
-
@description = text if text
|
12
|
-
@description
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.identifier(identifier=nil)
|
16
|
-
return @identifier unless identifier
|
17
|
-
# TODO: parse the string and extract things like collection , and format type?...
|
18
|
-
@identifier = identifier
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.describe(shallow = false)
|
22
|
-
hash = super
|
23
|
-
unless shallow
|
24
|
-
hash.merge!(identifier: @identifier, description: @description)
|
25
|
-
end
|
26
|
-
hash
|
27
|
-
end
|
28
|
-
|
29
11
|
def self.attributes(opts={}, &block)
|
30
12
|
super(opts.merge(dsl_compiler: MediaType::DSLCompiler), &block)
|
31
13
|
end
|
@@ -34,16 +16,16 @@ module Praxis
|
|
34
16
|
super
|
35
17
|
if @attribute && self.attributes.key?(:links) && self.attributes[:links].type < Praxis::Links
|
36
18
|
# Only define out special links accessor if it was setup using the special DSL
|
37
|
-
# (we might have an app defining an attribute called `links` on its own, in which
|
19
|
+
# (we might have an app defining an attribute called `links` on its own, in which
|
38
20
|
# case we leave it be)
|
39
21
|
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
40
|
-
|
41
|
-
|
42
|
-
|
22
|
+
def links
|
23
|
+
self.class::Links.new(@object)
|
24
|
+
end
|
43
25
|
RUBY
|
44
26
|
end
|
45
27
|
end
|
46
|
-
|
28
|
+
|
47
29
|
end
|
48
30
|
|
49
31
|
end
|
@@ -26,7 +26,12 @@ module Praxis
|
|
26
26
|
class << self
|
27
27
|
attr_accessor :member_attribute
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
|
+
def self.inherited(klass)
|
31
|
+
warn "DEPRECATION: MediaTypeCollection is deprecated and will be removed by 1.0"
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
30
35
|
def self._finalize!
|
31
36
|
super
|
32
37
|
|
@@ -35,7 +40,6 @@ module Praxis
|
|
35
40
|
include StructCollection
|
36
41
|
end
|
37
42
|
end
|
38
|
-
|
39
43
|
end
|
40
44
|
|
41
45
|
def self.member_type(type=nil)
|
data/lib/praxis/request.rb
CHANGED
@@ -4,15 +4,25 @@ module Praxis
|
|
4
4
|
attr_reader :env, :query
|
5
5
|
attr_accessor :route_params, :action
|
6
6
|
|
7
|
+
PATH_VERSION_PREFIX = "/v".freeze
|
8
|
+
CONTENT_TYPE_NAME = 'CONTENT_TYPE'.freeze
|
9
|
+
PATH_INFO_NAME = 'PATH_INFO'.freeze
|
10
|
+
REQUEST_METHOD_NAME = 'REQUEST_METHOD'.freeze
|
11
|
+
QUERY_STRING_NAME = 'QUERY_STRING'.freeze
|
12
|
+
API_VERSION_HEADER_NAME = "HTTP_X_API_VERSION".freeze
|
13
|
+
API_VERSION_PARAM_NAME = 'api_version'.freeze
|
14
|
+
API_NO_VERSION_NAME = 'n/a'.freeze
|
15
|
+
VERSION_USING_DEFAULTS = [:header,:params].freeze
|
16
|
+
|
7
17
|
def initialize(env)
|
8
18
|
@env = env
|
9
|
-
@query = Rack::Utils.parse_nested_query(env[
|
19
|
+
@query = Rack::Utils.parse_nested_query(env[QUERY_STRING_NAME])
|
10
20
|
@route_params = {}
|
11
21
|
@path_version_matcher = path_version_matcher
|
12
22
|
end
|
13
23
|
|
14
24
|
def content_type
|
15
|
-
@env[
|
25
|
+
@env[CONTENT_TYPE_NAME]
|
16
26
|
end
|
17
27
|
|
18
28
|
# The media type (type/subtype) portion of the CONTENT_TYPE header
|
@@ -26,27 +36,24 @@ module Praxis
|
|
26
36
|
end
|
27
37
|
|
28
38
|
def path
|
29
|
-
@env[
|
39
|
+
@env[PATH_INFO_NAME]
|
30
40
|
end
|
31
41
|
|
32
42
|
attr_accessor :headers, :params, :payload
|
33
43
|
|
34
44
|
def params_hash
|
35
45
|
return {} if params.nil?
|
36
|
-
|
37
|
-
params.attributes.each_with_object({}) do |(k,v),hash|
|
38
|
-
hash[k] = v
|
39
|
-
end
|
46
|
+
params.attributes
|
40
47
|
end
|
41
48
|
|
42
49
|
def verb
|
43
|
-
@env[
|
50
|
+
@env[REQUEST_METHOD_NAME]
|
44
51
|
end
|
45
52
|
|
46
53
|
def raw_params
|
47
54
|
@raw_params ||= begin
|
48
55
|
params = query.merge(route_params)
|
49
|
-
params.delete(
|
56
|
+
params.delete(API_VERSION_PARAM_NAME)
|
50
57
|
params
|
51
58
|
end
|
52
59
|
end
|
@@ -66,21 +73,23 @@ module Praxis
|
|
66
73
|
end
|
67
74
|
|
68
75
|
def self.path_version_prefix
|
69
|
-
|
76
|
+
PATH_VERSION_PREFIX
|
70
77
|
end
|
78
|
+
|
79
|
+
PATH_VERSION_MATCHER = %r{^#{self.path_version_prefix}(?<version>[^\/]+)\/}.freeze
|
71
80
|
|
72
81
|
def path_version_matcher
|
73
|
-
|
82
|
+
PATH_VERSION_MATCHER
|
74
83
|
end
|
75
84
|
|
76
|
-
def version(using:
|
85
|
+
def version(using: VERSION_USING_DEFAULTS )
|
77
86
|
result = nil
|
78
87
|
Array(using).find do |mode|
|
79
88
|
case mode
|
80
89
|
when :header ;
|
81
|
-
result = env[
|
90
|
+
result = env[API_VERSION_HEADER_NAME]
|
82
91
|
when :params ;
|
83
|
-
result = @query[
|
92
|
+
result = @query[API_VERSION_PARAM_NAME]
|
84
93
|
when :path ;
|
85
94
|
m = self.path.match(@path_version_matcher)
|
86
95
|
result = m[:version] unless m.nil?
|
@@ -88,7 +97,7 @@ module Praxis
|
|
88
97
|
raise "Unknown method for retrieving the API version: #{mode}"
|
89
98
|
end
|
90
99
|
end
|
91
|
-
return result ||
|
100
|
+
return result || API_NO_VERSION_NAME
|
92
101
|
end
|
93
102
|
|
94
103
|
def load_headers(context)
|
@@ -7,11 +7,10 @@ module Praxis
|
|
7
7
|
class RequestStage < Stage
|
8
8
|
extend Forwardable
|
9
9
|
|
10
|
-
def_delegators :@context, :controller, :action, :request
|
11
10
|
alias :dispatcher :application # it's technically application in the base Stage
|
12
11
|
|
13
12
|
def path
|
14
|
-
[name]
|
13
|
+
@the_path ||= [name].freeze
|
15
14
|
end
|
16
15
|
|
17
16
|
def execute_controller_callbacks(callbacks)
|
@@ -31,9 +30,25 @@ module Praxis
|
|
31
30
|
nil
|
32
31
|
end
|
33
32
|
|
34
|
-
def
|
35
|
-
setup!
|
33
|
+
def setup!
|
36
34
|
setup_deferred_callbacks!
|
35
|
+
end
|
36
|
+
|
37
|
+
# Avoid using delegators, and create the explicit functions:
|
38
|
+
# def_delegators :@context, :controller, :action, :request
|
39
|
+
# they allocate all kinds of things and we don't need the generality here
|
40
|
+
def controller
|
41
|
+
@context.controller
|
42
|
+
end
|
43
|
+
def action
|
44
|
+
@context.action
|
45
|
+
end
|
46
|
+
def request
|
47
|
+
@context.request
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def run
|
37
52
|
|
38
53
|
# stage-level callbacks (typically empty) will never shortcut
|
39
54
|
execute_callbacks(self.before_callbacks)
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'active_support/concern'
|
2
2
|
require 'active_support/inflector'
|
3
3
|
|
4
|
-
|
5
4
|
module Praxis
|
6
5
|
module ResourceDefinition
|
7
6
|
extend ActiveSupport::Concern
|
8
|
-
|
7
|
+
DEFAULT_RESOURCE_HREF_ACTION = :show
|
8
|
+
|
9
9
|
included do
|
10
10
|
@version = 'n/a'.freeze
|
11
11
|
@actions = Hash.new
|
@@ -21,7 +21,7 @@ module Praxis
|
|
21
21
|
attr_reader :routing_config
|
22
22
|
attr_reader :responses
|
23
23
|
attr_reader :version_options
|
24
|
-
|
24
|
+
|
25
25
|
# opaque hash of user-defined medata, used to decorate the definition,
|
26
26
|
# and also available in the generated JSON documents
|
27
27
|
attr_reader :metadata
|
@@ -42,24 +42,55 @@ module Praxis
|
|
42
42
|
@media_type = media_type
|
43
43
|
end
|
44
44
|
|
45
|
-
def version(version=nil, options=
|
45
|
+
def version(version=nil, options=nil)
|
46
46
|
return @version unless version
|
47
47
|
@version = version
|
48
|
-
@version_options = options
|
48
|
+
@version_options = options || {using: [:header,:params]}
|
49
|
+
end
|
50
|
+
|
51
|
+
def canonical_path( action_name=nil )
|
52
|
+
if action_name
|
53
|
+
raise "Canonical path for #{self.name} is already defined as: '#{@canonical_action_name}'. 'canonical_path' can only be defined once." if @canonical_action_name
|
54
|
+
@canonical_action_name = action_name
|
55
|
+
else
|
56
|
+
# Resolution of the actual action definition needs to be done lazily, since we can use the `canonical_path` stanza
|
57
|
+
# at the top of the resource, well before the actual action is defined.
|
58
|
+
unless @canonical_action
|
59
|
+
href_action = @canonical_action_name || DEFAULT_RESOURCE_HREF_ACTION
|
60
|
+
@canonical_action = actions.fetch(href_action) do
|
61
|
+
raise "Error: trying to set canonical_href of #{self.name}. Action '#{href_action}' does not exist"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
return @canonical_action
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_href( params )
|
69
|
+
canonical_path.primary_route.path.expand(params)
|
49
70
|
end
|
50
71
|
|
72
|
+
def parse_href(path)
|
73
|
+
param_values = canonical_path.primary_route.path.params(path)
|
74
|
+
attrs = canonical_path.params.attributes
|
75
|
+
param_values.each_with_object({}) do |(key,value),hash|
|
76
|
+
hash[key.to_sym] = attrs[key.to_sym].load(value,[key])
|
77
|
+
end
|
78
|
+
rescue => e
|
79
|
+
raise Praxis::Exception.new("Error parsing or coercing parameters from href: #{path}\n"+e.message)
|
80
|
+
end
|
81
|
+
|
51
82
|
def action_defaults(&block)
|
52
83
|
return @action_defaults unless block_given?
|
53
84
|
|
54
85
|
@action_defaults << block
|
55
86
|
end
|
56
|
-
|
87
|
+
|
57
88
|
def params(type=Attributor::Struct, **opts, &block)
|
58
89
|
warn 'DEPRECATION: ResourceDefinition.params is deprecated. Use it in action_defaults instead.'
|
59
90
|
action_defaults do
|
60
91
|
params type, **opts, &block
|
61
92
|
end
|
62
|
-
end
|
93
|
+
end
|
63
94
|
|
64
95
|
def payload(type=Attributor::Struct, **opts, &block)
|
65
96
|
warn 'DEPRECATION: ResourceDefinition.payload is deprecated. Use action_defaults instead.'
|
@@ -74,7 +105,7 @@ module Praxis
|
|
74
105
|
headers **opts, &block
|
75
106
|
end
|
76
107
|
end
|
77
|
-
|
108
|
+
|
78
109
|
def response(name, **args)
|
79
110
|
warn 'DEPRECATION: ResourceDefinition.response is deprecated. Use action_defaults instead.'
|
80
111
|
action_defaults do
|
@@ -84,6 +115,7 @@ module Praxis
|
|
84
115
|
|
85
116
|
def action(name, &block)
|
86
117
|
raise ArgumentError, "can not create ActionDefinition without block" unless block_given?
|
118
|
+
raise ArgumentError, "Action names must be defined using symbols (Got: #{name} (of type #{name.class}))" unless name.is_a? Symbol
|
87
119
|
@actions[name] = ActionDefinition.new(name, self, &block)
|
88
120
|
end
|
89
121
|
|
@@ -92,13 +124,16 @@ module Praxis
|
|
92
124
|
@description
|
93
125
|
end
|
94
126
|
|
95
|
-
|
127
|
+
def id
|
128
|
+
self.name.gsub('::'.freeze,'-'.freeze)
|
129
|
+
end
|
96
130
|
|
97
131
|
def describe
|
98
132
|
{}.tap do |hash|
|
99
133
|
hash[:description] = description
|
100
|
-
hash[:media_type] = media_type.
|
134
|
+
hash[:media_type] = media_type.id if media_type
|
101
135
|
hash[:actions] = actions.values.map(&:describe)
|
136
|
+
hash[:name] = self.name
|
102
137
|
hash[:metadata] = metadata
|
103
138
|
end
|
104
139
|
end
|
@@ -36,23 +36,23 @@ module Praxis
|
|
36
36
|
return @spec[:media_type] if media_type.nil?
|
37
37
|
|
38
38
|
@spec[:media_type] = case media_type
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
media_type
|
44
|
-
else
|
45
|
-
raise Exceptions::InvalidConfiguration.new(
|
46
|
-
'Invalid media_type specification. media_type must be a Praxis::MediaType'
|
47
|
-
)
|
48
|
-
end
|
49
|
-
when SimpleMediaType
|
39
|
+
when String
|
40
|
+
SimpleMediaType.new(media_type)
|
41
|
+
when Class
|
42
|
+
if media_type < Praxis::Types::MediaTypeCommon
|
50
43
|
media_type
|
51
44
|
else
|
52
45
|
raise Exceptions::InvalidConfiguration.new(
|
53
|
-
'Invalid media_type specification. media_type must be a
|
46
|
+
'Invalid media_type specification. media_type must be a Praxis::MediaType'
|
54
47
|
)
|
55
48
|
end
|
49
|
+
when SimpleMediaType
|
50
|
+
media_type
|
51
|
+
else
|
52
|
+
raise Exceptions::InvalidConfiguration.new(
|
53
|
+
'Invalid media_type specification. media_type must be a String, MediaType or SimpleMediaType'
|
54
|
+
)
|
55
|
+
end
|
56
56
|
end
|
57
57
|
|
58
58
|
def location(loc=nil)
|
@@ -94,11 +94,11 @@ module Praxis
|
|
94
94
|
)
|
95
95
|
end
|
96
96
|
@spec[:headers][k] = v
|
97
|
-
|
97
|
+
end
|
98
98
|
else
|
99
99
|
raise Exceptions::InvalidConfiguration.new(
|
100
100
|
"A header definition can only take a String (to match the name) or" +
|
101
|
-
|
101
|
+
" a Hash (to match both the name and the value). Received: #{hdr.inspect}"
|
102
102
|
)
|
103
103
|
end
|
104
104
|
end
|
@@ -122,7 +122,7 @@ module Praxis
|
|
122
122
|
end
|
123
123
|
unless headers == nil
|
124
124
|
headers.each do |name, value|
|
125
|
-
content[:headers][name] = _describe_header(value)
|
125
|
+
content[:headers][name] = _describe_header(value)
|
126
126
|
end
|
127
127
|
end
|
128
128
|
unless parts == nil
|
@@ -134,7 +134,7 @@ module Praxis
|
|
134
134
|
def _describe_header(data)
|
135
135
|
data_type = data.is_a?(Regexp) ? :regexp : :string
|
136
136
|
data_value = data.is_a?(Regexp) ? data.inspect : data
|
137
|
-
|
137
|
+
{ :value => data_value, :type => data_type }
|
138
138
|
end
|
139
139
|
|
140
140
|
def validate( response )
|
@@ -151,15 +151,15 @@ module Praxis
|
|
151
151
|
raise ArgumentError, "Parts definition for response #{name} needs a :like argument or a block/proc" if !args.empty?
|
152
152
|
return @parts
|
153
153
|
end
|
154
|
-
|
154
|
+
if like && a_proc
|
155
155
|
raise ArgumentError, "Parts definition for response #{name} does not allow :like and a block simultaneously"
|
156
156
|
end
|
157
157
|
if like
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
158
|
+
template = ApiDefinition.instance.response(like)
|
159
|
+
@parts = template.compile(nil, **args)
|
160
|
+
else # block
|
161
|
+
@parts = Praxis::ResponseDefinition.new('anonymous', **args, &a_proc)
|
162
|
+
end
|
163
163
|
end
|
164
164
|
|
165
165
|
# Validates Status code
|
@@ -238,17 +238,36 @@ module Praxis
|
|
238
238
|
def validate_content_type!(response)
|
239
239
|
return unless media_type
|
240
240
|
|
241
|
-
|
241
|
+
response_content_type = {}
|
242
242
|
if response.headers['Content-Type']
|
243
|
-
|
243
|
+
response_content_type = Praxis::ContentTypeParser.parse(response.headers['Content-Type'])
|
244
244
|
end
|
245
245
|
|
246
|
-
|
246
|
+
expected_content_type = Praxis::ContentTypeParser.parse(media_type.identifier)
|
247
|
+
|
248
|
+
unless response_content_type[:type] == expected_content_type[:type]
|
247
249
|
raise Exceptions::Validation.new(
|
248
|
-
"Bad Content-Type header.
|
249
|
-
" does not match type #{
|
250
|
+
"Bad Content-Type header. #{response.headers['Content-Type']}" +
|
251
|
+
" does not match type #{expected_content_type[:type]} as described in response: #{self.name}"
|
250
252
|
)
|
251
253
|
end
|
254
|
+
|
255
|
+
if (expected_params = expected_content_type[:params])
|
256
|
+
expected_params.each do |param_name,expected_param|
|
257
|
+
response_param = response_content_type[:params].fetch(param_name) do
|
258
|
+
raise Exceptions::Validation.new(
|
259
|
+
"Bad Content-Type header: #{response.headers['Content-Type']}" +
|
260
|
+
" does not contain expected param '#{param_name}' as described in response: #{self.name}"
|
261
|
+
)
|
262
|
+
end
|
263
|
+
unless response_param == expected_param
|
264
|
+
raise Exceptions::Validation.new(
|
265
|
+
"Bad Content-Type header: #{response.headers['Content-Type']}" +
|
266
|
+
" param: #{param_name} does not match expected value #{expected_param} as described in response: #{self.name}"
|
267
|
+
)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
252
271
|
end
|
253
272
|
|
254
273
|
def validate_parts!(response)
|