praxis 0.13.0 → 0.14.0
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 +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)
|