hoodoo 1.0.2
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 +7 -0
- data/bin/hoodoo +5 -0
- data/lib/hoodoo.rb +27 -0
- data/lib/hoodoo/active.rb +32 -0
- data/lib/hoodoo/active/active_model/uuid_validator.rb +45 -0
- data/lib/hoodoo/active/active_record/base.rb +81 -0
- data/lib/hoodoo/active/active_record/creator.rb +134 -0
- data/lib/hoodoo/active/active_record/dated.rb +343 -0
- data/lib/hoodoo/active/active_record/error_mapping.rb +351 -0
- data/lib/hoodoo/active/active_record/finder.rb +606 -0
- data/lib/hoodoo/active/active_record/search_helper.rb +189 -0
- data/lib/hoodoo/active/active_record/secure.rb +431 -0
- data/lib/hoodoo/active/active_record/support.rb +106 -0
- data/lib/hoodoo/active/active_record/translated.rb +87 -0
- data/lib/hoodoo/active/active_record/uuid.rb +80 -0
- data/lib/hoodoo/active/active_record/writer.rb +321 -0
- data/lib/hoodoo/client.rb +23 -0
- data/lib/hoodoo/client/augmented_array.rb +29 -0
- data/lib/hoodoo/client/augmented_base.rb +168 -0
- data/lib/hoodoo/client/augmented_hash.rb +23 -0
- data/lib/hoodoo/client/client.rb +354 -0
- data/lib/hoodoo/client/endpoint/endpoint.rb +427 -0
- data/lib/hoodoo/client/endpoint/endpoints/amqp.rb +180 -0
- data/lib/hoodoo/client/endpoint/endpoints/auto_session.rb +194 -0
- data/lib/hoodoo/client/endpoint/endpoints/http.rb +203 -0
- data/lib/hoodoo/client/endpoint/endpoints/http_based.rb +367 -0
- data/lib/hoodoo/client/endpoint/endpoints/not_found.rb +59 -0
- data/lib/hoodoo/client/headers.rb +269 -0
- data/lib/hoodoo/communicators.rb +23 -0
- data/lib/hoodoo/communicators/fast.rb +44 -0
- data/lib/hoodoo/communicators/pool.rb +601 -0
- data/lib/hoodoo/communicators/slow.rb +84 -0
- data/lib/hoodoo/data.rb +51 -0
- data/lib/hoodoo/data/resources/caller.rb +39 -0
- data/lib/hoodoo/data/resources/errors.rb +28 -0
- data/lib/hoodoo/data/resources/log.rb +31 -0
- data/lib/hoodoo/data/resources/session.rb +26 -0
- data/lib/hoodoo/data/types/error_primitive.rb +27 -0
- data/lib/hoodoo/data/types/permissions.rb +40 -0
- data/lib/hoodoo/data/types/permissions_defaults.rb +32 -0
- data/lib/hoodoo/data/types/permissions_full.rb +28 -0
- data/lib/hoodoo/data/types/permissions_resources.rb +31 -0
- data/lib/hoodoo/discovery.rb +20 -0
- data/lib/hoodoo/errors.rb +19 -0
- data/lib/hoodoo/errors/error_descriptions.rb +229 -0
- data/lib/hoodoo/errors/errors.rb +322 -0
- data/lib/hoodoo/generator.rb +139 -0
- data/lib/hoodoo/logger.rb +23 -0
- data/lib/hoodoo/logger/fast_writer.rb +27 -0
- data/lib/hoodoo/logger/flattener_mixin.rb +36 -0
- data/lib/hoodoo/logger/logger.rb +387 -0
- data/lib/hoodoo/logger/slow_writer.rb +49 -0
- data/lib/hoodoo/logger/writer_mixin.rb +52 -0
- data/lib/hoodoo/logger/writers/file_writer.rb +45 -0
- data/lib/hoodoo/logger/writers/log_entries_dot_com_writer.rb +64 -0
- data/lib/hoodoo/logger/writers/stream_writer.rb +43 -0
- data/lib/hoodoo/middleware.rb +33 -0
- data/lib/hoodoo/presenters.rb +45 -0
- data/lib/hoodoo/presenters/base.rb +281 -0
- data/lib/hoodoo/presenters/base_dsl.rb +519 -0
- data/lib/hoodoo/presenters/common_resource_fields.rb +31 -0
- data/lib/hoodoo/presenters/embedding.rb +232 -0
- data/lib/hoodoo/presenters/types/array.rb +118 -0
- data/lib/hoodoo/presenters/types/boolean.rb +26 -0
- data/lib/hoodoo/presenters/types/date.rb +26 -0
- data/lib/hoodoo/presenters/types/date_time.rb +26 -0
- data/lib/hoodoo/presenters/types/decimal.rb +47 -0
- data/lib/hoodoo/presenters/types/enum.rb +55 -0
- data/lib/hoodoo/presenters/types/field.rb +158 -0
- data/lib/hoodoo/presenters/types/float.rb +26 -0
- data/lib/hoodoo/presenters/types/hash.rb +361 -0
- data/lib/hoodoo/presenters/types/integer.rb +26 -0
- data/lib/hoodoo/presenters/types/object.rb +117 -0
- data/lib/hoodoo/presenters/types/string.rb +53 -0
- data/lib/hoodoo/presenters/types/tags.rb +24 -0
- data/lib/hoodoo/presenters/types/text.rb +26 -0
- data/lib/hoodoo/presenters/types/uuid.rb +54 -0
- data/lib/hoodoo/services.rb +34 -0
- data/lib/hoodoo/services/discovery/discoverers/by_consul.rb +66 -0
- data/lib/hoodoo/services/discovery/discoverers/by_convention.rb +173 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/by_drb.rb +195 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server.rb +166 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server_start.rb +37 -0
- data/lib/hoodoo/services/discovery/discovery.rb +186 -0
- data/lib/hoodoo/services/discovery/results/for_amqp.rb +58 -0
- data/lib/hoodoo/services/discovery/results/for_http.rb +85 -0
- data/lib/hoodoo/services/discovery/results/for_local.rb +85 -0
- data/lib/hoodoo/services/discovery/results/for_remote.rb +57 -0
- data/lib/hoodoo/services/middleware/amqp_log_message.rb +186 -0
- data/lib/hoodoo/services/middleware/amqp_log_writer.rb +119 -0
- data/lib/hoodoo/services/middleware/endpoints/inter_resource_local.rb +130 -0
- data/lib/hoodoo/services/middleware/endpoints/inter_resource_remote.rb +202 -0
- data/lib/hoodoo/services/middleware/exception_reporting/base_reporter.rb +105 -0
- data/lib/hoodoo/services/middleware/exception_reporting/exception_reporting.rb +115 -0
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/airbrake_reporter.rb +64 -0
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/raygun_reporter.rb +63 -0
- data/lib/hoodoo/services/middleware/interaction.rb +127 -0
- data/lib/hoodoo/services/middleware/middleware.rb +2705 -0
- data/lib/hoodoo/services/middleware/rack_monkey_patch.rb +73 -0
- data/lib/hoodoo/services/services/context.rb +153 -0
- data/lib/hoodoo/services/services/implementation.rb +132 -0
- data/lib/hoodoo/services/services/interface.rb +934 -0
- data/lib/hoodoo/services/services/permissions.rb +250 -0
- data/lib/hoodoo/services/services/request.rb +189 -0
- data/lib/hoodoo/services/services/response.rb +316 -0
- data/lib/hoodoo/services/services/service.rb +141 -0
- data/lib/hoodoo/services/services/session.rb +729 -0
- data/lib/hoodoo/utilities.rb +12 -0
- data/lib/hoodoo/utilities/string_inquirer.rb +54 -0
- data/lib/hoodoo/utilities/utilities.rb +380 -0
- data/lib/hoodoo/utilities/uuid.rb +44 -0
- data/lib/hoodoo/version.rb +17 -0
- data/spec/active/active_record/base_spec.rb +57 -0
- data/spec/active/active_record/creator_spec.rb +88 -0
- data/spec/active/active_record/dated_spec.rb +248 -0
- data/spec/active/active_record/error_mapping_spec.rb +360 -0
- data/spec/active/active_record/finder_spec.rb +744 -0
- data/spec/active/active_record/search_helper_spec.rb +384 -0
- data/spec/active/active_record/secure_spec.rb +435 -0
- data/spec/active/active_record/support_spec.rb +225 -0
- data/spec/active/active_record/translated_spec.rb +19 -0
- data/spec/active/active_record/uuid_spec.rb +72 -0
- data/spec/active/active_record/writer_spec.rb +272 -0
- data/spec/alchemy/alchemy-amq.rb +33 -0
- data/spec/client/augmented_array_spec.rb +15 -0
- data/spec/client/augmented_base_spec.rb +50 -0
- data/spec/client/augmented_hash_spec.rb +15 -0
- data/spec/client/client_spec.rb +955 -0
- data/spec/client/endpoint/endpoint_spec.rb +70 -0
- data/spec/client/endpoint/endpoints/amqp_spec.rb +16 -0
- data/spec/client/endpoint/endpoints/auto_session_spec.rb +9 -0
- data/spec/client/endpoint/endpoints/http_based_spec.rb +9 -0
- data/spec/client/endpoint/endpoints/http_spec.rb +103 -0
- data/spec/client/endpoint/endpoints/not_found_spec.rb +35 -0
- data/spec/client/headers_spec.rb +172 -0
- data/spec/communicators/fast_spec.rb +9 -0
- data/spec/communicators/pool_spec.rb +339 -0
- data/spec/communicators/slow_spec.rb +15 -0
- data/spec/data/resources/caller_spec.rb +156 -0
- data/spec/data/resources/errors_spec.rb +22 -0
- data/spec/data/resources/log_spec.rb +20 -0
- data/spec/data/resources/session_spec.rb +15 -0
- data/spec/data/types/error_primitive_spec.rb +15 -0
- data/spec/data/types/permissions_defaults_spec.rb +25 -0
- data/spec/data/types/permissions_full_spec.rb +44 -0
- data/spec/data/types/permissions_resources_spec.rb +34 -0
- data/spec/data/types/permissions_spec.rb +37 -0
- data/spec/errors/error_descriptions_spec.rb +98 -0
- data/spec/errors/errors_spec.rb +346 -0
- data/spec/integration/service_actions_spec.rb +112 -0
- data/spec/logger/fast_writer_spec.rb +18 -0
- data/spec/logger/logger_spec.rb +259 -0
- data/spec/logger/slow_writer_spec.rb +144 -0
- data/spec/logger/writers/file_writer_spec.rb +37 -0
- data/spec/logger/writers/log_entries_dot_com_writer_spec.rb +29 -0
- data/spec/logger/writers/stream_writer_spec.rb +38 -0
- data/spec/presenters/base_dsl_spec.rb +111 -0
- data/spec/presenters/base_spec.rb +871 -0
- data/spec/presenters/common_resource_fields_spec.rb +30 -0
- data/spec/presenters/embedding_spec.rb +87 -0
- data/spec/presenters/types/array_spec.rb +249 -0
- data/spec/presenters/types/boolean_spec.rb +51 -0
- data/spec/presenters/types/date_spec.rb +57 -0
- data/spec/presenters/types/date_time_spec.rb +59 -0
- data/spec/presenters/types/decimal_spec.rb +58 -0
- data/spec/presenters/types/enum_spec.rb +71 -0
- data/spec/presenters/types/field_spec.rb +77 -0
- data/spec/presenters/types/float_spec.rb +50 -0
- data/spec/presenters/types/hash_spec.rb +1069 -0
- data/spec/presenters/types/integer_spec.rb +50 -0
- data/spec/presenters/types/object_spec.rb +177 -0
- data/spec/presenters/types/string_spec.rb +65 -0
- data/spec/presenters/types/tags_spec.rb +56 -0
- data/spec/presenters/types/text_spec.rb +50 -0
- data/spec/presenters/types/uuid_spec.rb +46 -0
- data/spec/presenters/walk_spec.rb +198 -0
- data/spec/services/discovery/discoverers/by_consul_spec.rb +29 -0
- data/spec/services/discovery/discoverers/by_convention_spec.rb +67 -0
- data/spec/services/discovery/discoverers/by_drb/by_drb_spec.rb +80 -0
- data/spec/services/discovery/discoverers/by_drb/drb_server_spec.rb +205 -0
- data/spec/services/discovery/discovery_spec.rb +73 -0
- data/spec/services/discovery/results/for_amqp_spec.rb +17 -0
- data/spec/services/discovery/results/for_http_spec.rb +37 -0
- data/spec/services/discovery/results/for_local_spec.rb +21 -0
- data/spec/services/discovery/results/for_remote_spec.rb +15 -0
- data/spec/services/middleware/amqp_log_message_spec.rb +60 -0
- data/spec/services/middleware/amqp_log_writer_spec.rb +95 -0
- data/spec/services/middleware/endpoints/inter_resource_local_spec.rb +9 -0
- data/spec/services/middleware/endpoints/inter_resource_remote_spec.rb +9 -0
- data/spec/services/middleware/exception_reporting/base_reporter_spec.rb +16 -0
- data/spec/services/middleware/exception_reporting/exception_reporting_spec.rb +92 -0
- data/spec/services/middleware/exception_reporting/reporters/airbrake_reporter_spec.rb +24 -0
- data/spec/services/middleware/exception_reporting/reporters/raygun_reporter_spec.rb +23 -0
- data/spec/services/middleware/middleware_cors_spec.rb +93 -0
- data/spec/services/middleware/middleware_create_update_spec.rb +489 -0
- data/spec/services/middleware/middleware_dated_at_spec.rb +186 -0
- data/spec/services/middleware/middleware_exotic_communication_spec.rb +560 -0
- data/spec/services/middleware/middleware_logging_spec.rb +356 -0
- data/spec/services/middleware/middleware_multi_local_spec.rb +1094 -0
- data/spec/services/middleware/middleware_multi_remote_spec.rb +1440 -0
- data/spec/services/middleware/middleware_permissions_spec.rb +1014 -0
- data/spec/services/middleware/middleware_public_spec.rb +238 -0
- data/spec/services/middleware/middleware_spec.rb +1569 -0
- data/spec/services/middleware/string_inquirer_spec.rb +30 -0
- data/spec/services/services/application_spec.rb +74 -0
- data/spec/services/services/context_spec.rb +48 -0
- data/spec/services/services/implementation_spec.rb +45 -0
- data/spec/services/services/interface_spec.rb +262 -0
- data/spec/services/services/permissions_spec.rb +249 -0
- data/spec/services/services/request_spec.rb +95 -0
- data/spec/services/services/response_spec.rb +250 -0
- data/spec/services/services/session_spec.rb +432 -0
- data/spec/spec_helper.rb +298 -0
- data/spec/utilities/utilities_spec.rb +537 -0
- data/spec/utilities/uuid_spec.rb +20 -0
- metadata +615 -0
@@ -0,0 +1,250 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: permissions.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: Allow/ask/deny support for resources and actions.
|
6
|
+
# ----------------------------------------------------------------------
|
7
|
+
# 26-Jan-2015 (ADH): Created.
|
8
|
+
########################################################################
|
9
|
+
|
10
|
+
module Hoodoo; module Services
|
11
|
+
|
12
|
+
# The Permissions class provides a way to store and recall information on
|
13
|
+
# action behaviour for resources. It is just a way to store and query this
|
14
|
+
# information; actually enforcing the result is up to the caller.
|
15
|
+
#
|
16
|
+
# Permissions are based on the standard actions - +list+, +show+, +create+,
|
17
|
+
# +update+ and +delete+ - with defined permissions of constants DENY
|
18
|
+
# (prohibit access), ALLOW (allow access) and ASK. The intention of ASK is
|
19
|
+
# that some other component - usually a service application - should be
|
20
|
+
# passed details of the request and asked if it should be permitted.
|
21
|
+
#
|
22
|
+
# Callers must *ensure* they *only* use the DENY, ALLOW and ASK constants
|
23
|
+
# defined herein, without making assumptions about their assigned values.
|
24
|
+
#
|
25
|
+
# There is both a default set of permissions in addition to per-resource
|
26
|
+
# permissions and there is a fallback for cases where a permission for a
|
27
|
+
# particular action has not been defined. This lets you define the baseline
|
28
|
+
# behaviour in the fallback cases and only describe exceptions to that
|
29
|
+
# baseline through the Permissions interface, minimising caller workload.
|
30
|
+
#
|
31
|
+
# Hoodoo::Services::Middleware uses an instance of this class to determine
|
32
|
+
# whether or not it should pass on inbound requests to service applications.
|
33
|
+
#
|
34
|
+
# Example:
|
35
|
+
#
|
36
|
+
# Here, an object is created with a default fallback of DENY, then has the
|
37
|
+
# action "list" allowed for all resources and says that resource "Member"
|
38
|
+
# must ask someone for permission if its "show" action is requested.
|
39
|
+
# Another resource "Ping" allows any action unconditionally.
|
40
|
+
#
|
41
|
+
# p = Hoodoo::Services::Permissions.new
|
42
|
+
# p.set_default( :list, Hoodoo::Services::Permissions::ALLOW )
|
43
|
+
# p.set_resource( :Member, :show, Hoodoo::Services::Permissions::ASK )
|
44
|
+
# p.set_resource_fallback( :Ping, Hoodoo::Services::Permissions::ALLOW )
|
45
|
+
#
|
46
|
+
# puts JSON.pretty_generate( p.to_h() )
|
47
|
+
#
|
48
|
+
# # Yields...
|
49
|
+
# #
|
50
|
+
# # {
|
51
|
+
# # "default": {
|
52
|
+
# # "else": "deny",
|
53
|
+
# # "actions": {
|
54
|
+
# # "list": "allow"
|
55
|
+
# # }
|
56
|
+
# # },
|
57
|
+
# # "resources": {
|
58
|
+
# # "Member": {
|
59
|
+
# # "actions": {
|
60
|
+
# # "show": "ask"
|
61
|
+
# # }
|
62
|
+
# # },
|
63
|
+
# # "Ping": {
|
64
|
+
# # "else": "allow"
|
65
|
+
# # }
|
66
|
+
# # }
|
67
|
+
# # }
|
68
|
+
#
|
69
|
+
class Permissions
|
70
|
+
|
71
|
+
# Permission is denied; the action should not be permitted.
|
72
|
+
#
|
73
|
+
DENY = 'deny'
|
74
|
+
|
75
|
+
# Permission is granted; the action should be permitted.
|
76
|
+
#
|
77
|
+
ALLOW = 'allow'
|
78
|
+
|
79
|
+
# Something else (e.g. a service application) needs to be asked to see if
|
80
|
+
# it permits the action.
|
81
|
+
#
|
82
|
+
ASK = 'ask'
|
83
|
+
|
84
|
+
# All currently known (allowed/supported) permission policies.
|
85
|
+
#
|
86
|
+
ALLOWED_POLICIES = [
|
87
|
+
DENY,
|
88
|
+
ALLOW,
|
89
|
+
ASK
|
90
|
+
]
|
91
|
+
|
92
|
+
# Create a new Permissions instance, optionally from a Hash of the format
|
93
|
+
# returned by #to_h.
|
94
|
+
#
|
95
|
+
# By default the object is initialised with a default fallback which
|
96
|
+
# denies all actions for all resources.
|
97
|
+
#
|
98
|
+
def initialize( hash = nil )
|
99
|
+
if hash.nil?
|
100
|
+
@permissions = {}
|
101
|
+
set_default_fallback( DENY )
|
102
|
+
else
|
103
|
+
from_h!( hash )
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Set the default fallback for actions. If a resource does not have a
|
108
|
+
# specific entry for it in the Permissions object and if the action does
|
109
|
+
# not have a default permission, then this permission used.
|
110
|
+
#
|
111
|
+
# +permission+:: DENY, ALLOW or ASK.
|
112
|
+
#
|
113
|
+
def set_default_fallback( permission )
|
114
|
+
action_name = action_name.to_s
|
115
|
+
|
116
|
+
@permissions[ 'default' ] ||= {}
|
117
|
+
@permissions[ 'default' ][ 'else' ] = permission
|
118
|
+
end
|
119
|
+
|
120
|
+
# Set the default permission for the given action. If a resource does not
|
121
|
+
# have a specific entry for it in the Permissions object but the action
|
122
|
+
# matches the given name, then this permission is used.
|
123
|
+
#
|
124
|
+
# +action_name+:: Action as a String or Symbol, from: +list+, +show+,
|
125
|
+
# +create+, +update+ or +delete+.
|
126
|
+
#
|
127
|
+
# +permission+:: DENY, ALLOW or ASK.
|
128
|
+
#
|
129
|
+
def set_default( action_name, permission )
|
130
|
+
action_name = action_name.to_s
|
131
|
+
|
132
|
+
@permissions[ 'default' ] ||= {}
|
133
|
+
@permissions[ 'default' ][ 'actions' ] ||= {}
|
134
|
+
@permissions[ 'default' ][ 'actions' ][ action_name ] = permission
|
135
|
+
end
|
136
|
+
|
137
|
+
# Set the default fallback for a resource. If the resource is asked to
|
138
|
+
# perform an action that's not otherwise listed in the resource's entry
|
139
|
+
# in the Permissions object, then this permission is used.
|
140
|
+
#
|
141
|
+
# +resource_name+:: Resource name as a Symbol or String, e.g. "+Purchase+"
|
142
|
+
# or +:Member+.
|
143
|
+
#
|
144
|
+
# +permission+:: DENY, ALLOW or ASK.
|
145
|
+
#
|
146
|
+
def set_resource_fallback( resource_name, permission )
|
147
|
+
resource_name = resource_name.to_s
|
148
|
+
|
149
|
+
@permissions[ 'resources' ] ||= {}
|
150
|
+
@permissions[ 'resources' ][ resource_name ] ||= {}
|
151
|
+
@permissions[ 'resources' ][ resource_name ][ 'else' ] = permission
|
152
|
+
end
|
153
|
+
|
154
|
+
# Set the permissions an action on a resource.
|
155
|
+
#
|
156
|
+
# +resource_name+:: Resource name as a Symbol or String, e.g. "+Purchase+"
|
157
|
+
# or +:Member+.
|
158
|
+
#
|
159
|
+
# +action_name+:: Action as a String or Symbol, from: +list+, +show+,
|
160
|
+
# +create+, +update+ or +delete+.
|
161
|
+
#
|
162
|
+
# +permission+:: DENY, ALLOW or ASK.
|
163
|
+
#
|
164
|
+
def set_resource( resource_name, action_name, permission )
|
165
|
+
resource_name = resource_name.to_s
|
166
|
+
action_name = action_name.to_s
|
167
|
+
|
168
|
+
@permissions[ 'resources' ] ||= {}
|
169
|
+
@permissions[ 'resources' ][ resource_name ] ||= {}
|
170
|
+
@permissions[ 'resources' ][ resource_name ][ 'actions' ] ||= {}
|
171
|
+
@permissions[ 'resources' ][ resource_name ][ 'actions' ][ action_name ] = permission
|
172
|
+
end
|
173
|
+
|
174
|
+
# For the given resource, is the given action permitted? Returns one of the
|
175
|
+
# ALLOW, DENY or ASK constant values.
|
176
|
+
#
|
177
|
+
# +resource_name+:: Resource name as a Symbol or String, e.g. "+Purchase+"
|
178
|
+
# or +:Member+.
|
179
|
+
#
|
180
|
+
# +action_name+:: Action as a String or Symbol, from: +list+, +show+,
|
181
|
+
# +create+, +update+ or +delete+.
|
182
|
+
#
|
183
|
+
def permitted?( resource_name, action_name )
|
184
|
+
resource_name = resource_name.to_s
|
185
|
+
action_name = action_name.to_s
|
186
|
+
|
187
|
+
tree = if @permissions.has_key?( 'resources' )
|
188
|
+
@permissions[ 'resources' ][ resource_name ]
|
189
|
+
end || {}
|
190
|
+
|
191
|
+
result = permitted_in?( tree, action_name )
|
192
|
+
|
193
|
+
if result.nil?
|
194
|
+
tree = @permissions[ 'default' ] || {}
|
195
|
+
result = permitted_in?( tree, action_name )
|
196
|
+
end
|
197
|
+
|
198
|
+
return result || DENY
|
199
|
+
end
|
200
|
+
|
201
|
+
# Return a Hash representative of this permissions object, which can be
|
202
|
+
# stored elsewhere, used to initialise another instance or written to an
|
203
|
+
# existing instance with #from_h!.
|
204
|
+
#
|
205
|
+
def to_h
|
206
|
+
@permissions
|
207
|
+
end
|
208
|
+
|
209
|
+
# Overwrite this instances's permissions with those from the given Hash.
|
210
|
+
#
|
211
|
+
# +hash+:: Permissions hash, which must come (directly or indirectly) from
|
212
|
+
# a #to_h call.
|
213
|
+
#
|
214
|
+
def from_h!( hash )
|
215
|
+
@permissions = Hoodoo::Utilities.stringify( hash )
|
216
|
+
end
|
217
|
+
|
218
|
+
# Merge the permissions described by the given Hash with those inside this
|
219
|
+
# instance. This will add to, or overwrite permissions with those from the
|
220
|
+
# given input Hash.
|
221
|
+
#
|
222
|
+
# +hash+:: Permissions hash, which must come (directly or indirectly) from
|
223
|
+
# a #to_h call.
|
224
|
+
#
|
225
|
+
def merge!( hash )
|
226
|
+
@permissions = Hoodoo::Utilities.deep_merge_into(
|
227
|
+
@permissions,
|
228
|
+
Hoodoo::Utilities.stringify( hash )
|
229
|
+
)
|
230
|
+
end
|
231
|
+
|
232
|
+
private
|
233
|
+
|
234
|
+
# For a given permissions sub-tree at the level of +actions+ / +else+,
|
235
|
+
# return an entry for the given action name. May return +nil+ if the
|
236
|
+
# tree has neither a matching action nor an +else+ case.
|
237
|
+
#
|
238
|
+
# +tree+:: Permissions sub-tree at the +actions+ / +else+ level.
|
239
|
+
# +action_name+:: As for #permitted?
|
240
|
+
#
|
241
|
+
def permitted_in?( tree, action_name )
|
242
|
+
result = if tree.has_key?( 'actions' )
|
243
|
+
tree[ 'actions' ][ action_name ]
|
244
|
+
end || tree[ 'else' ]
|
245
|
+
|
246
|
+
return result
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
end; end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: request.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: A high level description of a client's request, with all of
|
6
|
+
# the "raw" Rack request data parsed, verified as far as
|
7
|
+
# possible and generally cleaned up. Instances of this class
|
8
|
+
# are given to Hoodoo::Services::Implementation methods for
|
9
|
+
# each new request.
|
10
|
+
# ----------------------------------------------------------------------
|
11
|
+
# 24-Sep-2014 (ADH): Created.
|
12
|
+
########################################################################
|
13
|
+
|
14
|
+
module Hoodoo; module Services
|
15
|
+
|
16
|
+
# Instances of the Hoodoo::Services::Request class are passed to service
|
17
|
+
# interface implementations when requests come in via Rack, after basic
|
18
|
+
# checks have been passed and a particular interface implementation has
|
19
|
+
# been identified by endpoint.
|
20
|
+
#
|
21
|
+
# Descriptions of default values expected out of accessors herein refer
|
22
|
+
# to the use case when driven through Hoodoo::Services::Middleware. If the
|
23
|
+
# class is instantiated "bare" it gains no default values at all (all
|
24
|
+
# read accessors would report +nil+).
|
25
|
+
#
|
26
|
+
class Request
|
27
|
+
|
28
|
+
# Encapsulation of all parameters related only to modifying a
|
29
|
+
# list of results. Other parameters may modify lists too, but they
|
30
|
+
# also modify other representations (e.g. single-resource 'show').
|
31
|
+
#
|
32
|
+
class ListParameters
|
33
|
+
|
34
|
+
# List offset, for index views; an integer; always defined.
|
35
|
+
#
|
36
|
+
attr_accessor :offset
|
37
|
+
|
38
|
+
# List page size, for index views; an integer; always defined.
|
39
|
+
#
|
40
|
+
attr_accessor :limit
|
41
|
+
|
42
|
+
# A Hash of String keys and values, where each key is a field for
|
43
|
+
# sorting and each value is the direction to sort that field.
|
44
|
+
#
|
45
|
+
attr_accessor :sort_data
|
46
|
+
|
47
|
+
# List search key/value pairs as a hash, all keys/values strings; {}
|
48
|
+
# if there's no search data in the request URI query string.
|
49
|
+
#
|
50
|
+
attr_accessor :search_data
|
51
|
+
|
52
|
+
# List filter key/value pairs as a hash, all keys/values strings; {}
|
53
|
+
# if there's no filter data in the request URI query string.
|
54
|
+
#
|
55
|
+
attr_accessor :filter_data
|
56
|
+
|
57
|
+
# Set up defaults in this instance.
|
58
|
+
#
|
59
|
+
def initialize
|
60
|
+
self.offset = 0
|
61
|
+
self.limit = 50
|
62
|
+
self.sort_data = { 'created_at' => 'desc' }
|
63
|
+
self.search_data = {}
|
64
|
+
self.filter_data = {}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Requested locale for internationalised operations; +"en-nz"+ by
|
69
|
+
# default.
|
70
|
+
#
|
71
|
+
attr_accessor :locale
|
72
|
+
|
73
|
+
# Define read/write accessors for properties related to "X-Foo"
|
74
|
+
# headers. See the Middleware for details.
|
75
|
+
#
|
76
|
+
Hoodoo::Client::Headers.define_accessors_for_header_equivalents( self )
|
77
|
+
|
78
|
+
# Hash of HTTP headers _in_ _Rack_ _format_ - e.g. +HTTP_X_INTERACTION_ID+
|
79
|
+
# for the "X-Interaction-ID" header, for read-only use. All keys are in
|
80
|
+
# upper case, are Strings, have "HTTP_" at the start and use underscores
|
81
|
+
# where the original request might've used an underscore or hyphen. The
|
82
|
+
# usual curious Rack exceptions of +CONTENT_TYPE+ and +CONTENT_LENGTH+ do
|
83
|
+
# apply, though. This is a superset of header values including those sent
|
84
|
+
# by the client in its request and anything Rack itself might have added.
|
85
|
+
#
|
86
|
+
attr_accessor :headers
|
87
|
+
|
88
|
+
# Parsed payload hash, for create and update actions only; else +nil+.
|
89
|
+
#
|
90
|
+
attr_accessor :body
|
91
|
+
|
92
|
+
# An array of zero or more path components making up the URI *after* the
|
93
|
+
# service endpoint has been accounted for. For example, with a service
|
94
|
+
# endpoint of "products", this URI:
|
95
|
+
#
|
96
|
+
# http://test.com/v1/products/1234/foo.json
|
97
|
+
#
|
98
|
+
# ...would lead to this path component array:
|
99
|
+
#
|
100
|
+
# [ '1234', 'foo' ]
|
101
|
+
#
|
102
|
+
# The first element of the path components array is exposed in the
|
103
|
+
# read-only #ident accessor.
|
104
|
+
#
|
105
|
+
attr_reader :uri_path_components
|
106
|
+
|
107
|
+
# Set the array returned by #uri_path_components and record the first
|
108
|
+
# element in the value returned by #ident.
|
109
|
+
#
|
110
|
+
# +ary+:: Path component array to record. If +nil+ or not an array,
|
111
|
+
# +nil+ is stored for uri_path_components and #ident.
|
112
|
+
#
|
113
|
+
def uri_path_components=( ary )
|
114
|
+
if ary.is_a?( ::Array )
|
115
|
+
@uri_path_components = ary
|
116
|
+
@ident = ary.first
|
117
|
+
else
|
118
|
+
@uri_path_components = nil
|
119
|
+
@ident = nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# The first entry in the #uri_path_components array, or +nil+ if the
|
124
|
+
# array is empty. This supports a common case for inter-resource calls
|
125
|
+
# where a UUID or other unique identifier is provided through the first
|
126
|
+
# path element ("+.../v1/resource/uuid+").
|
127
|
+
#
|
128
|
+
attr_reader :ident
|
129
|
+
|
130
|
+
# A filename extension on the URI path component, if any, else an empty
|
131
|
+
# string. The _first_ dot in the _last_ path component is looked for (see
|
132
|
+
# also #uri_path_components), so for example this URI:
|
133
|
+
#
|
134
|
+
# http://test.com/v1/products/1.2.3.4/foo.my.tar.gz
|
135
|
+
#
|
136
|
+
# ...would lead to this URI path extension string:
|
137
|
+
#
|
138
|
+
# 'my.tar.gz'
|
139
|
+
#
|
140
|
+
attr_accessor :uri_path_extension
|
141
|
+
|
142
|
+
# The Hoodoo::Services::Request::ListParameters instance
|
143
|
+
# associated with this request.
|
144
|
+
#
|
145
|
+
attr_accessor :list
|
146
|
+
|
147
|
+
# Define a set of now-deprecated accessors that are basically
|
148
|
+
# just proxies through to the "list" instance. See #list.
|
149
|
+
#
|
150
|
+
%i{
|
151
|
+
offset
|
152
|
+
limit
|
153
|
+
sort_data
|
154
|
+
search_data
|
155
|
+
filter_data
|
156
|
+
}.each do | method |
|
157
|
+
define_method( "list_#{ method }" ) do
|
158
|
+
list.send( method )
|
159
|
+
end
|
160
|
+
|
161
|
+
define_method( "list_#{ method }=" ) do | value |
|
162
|
+
list.send( "#{ method }=", value )
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Array of strings giving requested embedded items; [] if there are
|
167
|
+
# none requested.
|
168
|
+
#
|
169
|
+
attr_accessor :embeds
|
170
|
+
|
171
|
+
# Array of strings giving requested referenced items; [] if there are
|
172
|
+
# none requested.
|
173
|
+
#
|
174
|
+
attr_accessor :references
|
175
|
+
|
176
|
+
# Set up defaults in this instance.
|
177
|
+
#
|
178
|
+
def initialize
|
179
|
+
self.locale = 'en-nz'
|
180
|
+
self.uri_path_components = []
|
181
|
+
self.uri_path_extension = ''
|
182
|
+
self.list = Hoodoo::Services::Request::ListParameters.new
|
183
|
+
self.embeds = []
|
184
|
+
self.references = []
|
185
|
+
self.headers = {}.freeze
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
end; end
|
@@ -0,0 +1,316 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: response.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: A high level description of a service's response to a
|
6
|
+
# client's request. The middleware constructs instances and
|
7
|
+
# fills in some of the data for every client request, then
|
8
|
+
# passes it to Hoodoo::Services::Implementation methods so
|
9
|
+
# the service can fill in the rest of the data.
|
10
|
+
# ----------------------------------------------------------------------
|
11
|
+
# 24-Sep-2014 (ADH): Created.
|
12
|
+
########################################################################
|
13
|
+
|
14
|
+
require 'json'
|
15
|
+
|
16
|
+
module Hoodoo; module Services
|
17
|
+
|
18
|
+
# The service middleware creates a Hoodoo::Services::Response instance for
|
19
|
+
# each request it handles, populating it with some data before and after the
|
20
|
+
# service implementation runs as part of standard pre- and post-processing.
|
21
|
+
# In the middle, the service implementation is given the instance and adds
|
22
|
+
# its own data to it.
|
23
|
+
#
|
24
|
+
# The instance carries data about both error conditions and successful work.
|
25
|
+
# In the successful case, #http_status_code and #body data is set by the
|
26
|
+
# service and used in the response. In the error case (see #errors), the
|
27
|
+
# HTTP status code is taken from the first error in the errors collection and
|
28
|
+
# the response body will be the JSON representation of that collection - any
|
29
|
+
# HTTP status code or response body data previously set by the service will
|
30
|
+
# be ignored.
|
31
|
+
#
|
32
|
+
class Response
|
33
|
+
|
34
|
+
# Obtain a reference to the Hoodoo::Errors instance for this response;
|
35
|
+
# use Hoodoo::Errors#add_error to add to the collection directly. For
|
36
|
+
# convenience, this class also provides the #add_error proxy instance
|
37
|
+
# method (syntactic sugar for most service implementations, but with a
|
38
|
+
# return value that helps keep the service middleware code clean).
|
39
|
+
#
|
40
|
+
# It's possible to change the errors object if you want to swap it for any
|
41
|
+
# reason, though this is generally discouraged - especially if the existing
|
42
|
+
# errors collection isn't empty. The middleware does this as part of
|
43
|
+
# request handling, but generally speaking nobody else should need to.
|
44
|
+
#
|
45
|
+
attr_accessor :errors
|
46
|
+
|
47
|
+
# HTTP status code that will be involved in the response. Default is 200.
|
48
|
+
# Integer, or something that can be converted to one with +to_i+. If errors
|
49
|
+
# are added to the response then the status code is derived from the first
|
50
|
+
# error in the collection, overriding any value set here. See #errors.
|
51
|
+
#
|
52
|
+
attr_accessor :http_status_code
|
53
|
+
|
54
|
+
# A service implementation can set (and read back, should it wish) the
|
55
|
+
# API call response body data using this #body / #body= accessor. This is
|
56
|
+
# converted to a client-facing representation automatically (e.g. to JSON).
|
57
|
+
#
|
58
|
+
# The response body *MUST* be either a Ruby +Array+ or a Ruby +Hash+.
|
59
|
+
# For *internal* *use* *only* a Ruby +String+ of pre-encoded response data
|
60
|
+
# is also accepted.
|
61
|
+
#
|
62
|
+
# This method is aliased as #set_resource, for semantic use when you want
|
63
|
+
# to set the response body to a representation (as a Hash) of a resource.
|
64
|
+
# When you want to set an Array of items for a list, it is strongly
|
65
|
+
# recommended that you call #set_resources and pass a total dataset size
|
66
|
+
# in addition to just the Array containing a page of list data.
|
67
|
+
#
|
68
|
+
# When reading response data, the body information is only valid if
|
69
|
+
# method #halt_processing? returns +false+.
|
70
|
+
#
|
71
|
+
attr_accessor :body
|
72
|
+
alias_method :set_resource, :body=
|
73
|
+
|
74
|
+
# Read back a the dataset size given by a prior call to #set_resources,
|
75
|
+
# or +nil+ if none has been provided (either the response contains no
|
76
|
+
# list yet/at all, or an Array was given but the dataset size was not
|
77
|
+
# supplied).
|
78
|
+
#
|
79
|
+
attr_reader :dataset_size
|
80
|
+
|
81
|
+
# Create a new instance, ready to take on a response. The service
|
82
|
+
# middleware is responsible for doing this.
|
83
|
+
#
|
84
|
+
# +interaction_id+:: The UUID of the interaction taking place for which a
|
85
|
+
# response is required.
|
86
|
+
#
|
87
|
+
def initialize( interaction_id )
|
88
|
+
|
89
|
+
unless Hoodoo::UUID.valid?( interaction_id )
|
90
|
+
raise "Hoodoo::Services::Response.new must be given a valid Interaction ID (got '#{ interaction_id.inspect }')"
|
91
|
+
end
|
92
|
+
|
93
|
+
@interaction_id = interaction_id
|
94
|
+
@errors = Hoodoo::Errors.new()
|
95
|
+
@headers = {}
|
96
|
+
@http_status_code = 200
|
97
|
+
@body = {}
|
98
|
+
@dataset_size = nil
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns +true+ if processing should halt, e.g. because errors have been
|
103
|
+
# added to the errors collection. Check here whenever you would consider an
|
104
|
+
# early exit due to errors arising in processing (otherwise they will just
|
105
|
+
# continue to accumulate).
|
106
|
+
#
|
107
|
+
def halt_processing?
|
108
|
+
@errors.has_errors?
|
109
|
+
end
|
110
|
+
|
111
|
+
# Similar to #body and #set_resource, but used when you are returning an
|
112
|
+
# array of items. Although you can just assign an array to either of
|
113
|
+
# #body or #set_resource, calling #set_resources is more semantically
|
114
|
+
# correct and provides an additional feature; you can specify the total
|
115
|
+
# number of items in the dataset.
|
116
|
+
#
|
117
|
+
# For example, if you were listing a page of 50 resource instances but
|
118
|
+
# the total matching dataset of that list included 344 instances, you
|
119
|
+
# would pass 344 in the +dataset_size+ input parameter. This is optional
|
120
|
+
# but highly recommended as it is often very useful for calling clients.
|
121
|
+
#
|
122
|
+
# +array+:: Array of resource representations (Ruby Array with
|
123
|
+
# Ruby Hash entries representing rendered resources,
|
124
|
+
# ideally through the Hoodoo::Presenters framework).
|
125
|
+
#
|
126
|
+
# +dataset_size+:: Optional _total_ number of items in the entire dataset
|
127
|
+
# of which +array+ is, most likely, just a subset due to
|
128
|
+
# paginated lists via offset and limit parameters.
|
129
|
+
#
|
130
|
+
def set_resources( array, dataset_size = nil )
|
131
|
+
self.body = array
|
132
|
+
@dataset_size = dataset_size
|
133
|
+
end
|
134
|
+
|
135
|
+
# Add an HTTP header to the internal collection that will be used for the
|
136
|
+
# response. Trying to set data for the same HTTP header name more than once
|
137
|
+
# will result in an exception being raised unless the +overwrite+ parameter
|
138
|
+
# is used (this is strongly discouraged in the general case).
|
139
|
+
#
|
140
|
+
# +name+:: Correct case and punctuation HTTP header name (e.g.
|
141
|
+
# "Content-Type").
|
142
|
+
#
|
143
|
+
# +value+:: Value for the header, as a string or something that behaves
|
144
|
+
# sensibly when +to_s+ is invoked upon it.
|
145
|
+
#
|
146
|
+
# +overwrite+:: Optional. Pass +true+ to allow the same HTTP header name to
|
147
|
+
# be set more than once - the new value overwrites the old.
|
148
|
+
# By default this is prohibited and an exception will be
|
149
|
+
# raised to avoid accidental value overwrites.
|
150
|
+
#
|
151
|
+
def add_header( name, value, overwrite = false )
|
152
|
+
name = name.to_s
|
153
|
+
dname = name.downcase
|
154
|
+
value = value.to_s
|
155
|
+
|
156
|
+
if ( overwrite == false && @headers.has_key?( dname ) )
|
157
|
+
hash = @headers[ dname ]
|
158
|
+
name = hash.keys[ 0 ]
|
159
|
+
value = hash.values[ 0 ]
|
160
|
+
raise "Hoodoo::Services::Response\#add_header: Value '#{ value }' already defined for header '#{ name }'"
|
161
|
+
else
|
162
|
+
@headers[ dname ] = { name => value }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Check the stored value of a given HTTP header. Checks are case
|
167
|
+
# insensitive. Returns the value stored by a prior #add_header call, or
|
168
|
+
# +nil+ for no value (or an explicitly stored value of +nil+)
|
169
|
+
#
|
170
|
+
# +name+:: HTTP header name (e.g. "Content-Type", "CONTENT-TYPE").
|
171
|
+
#
|
172
|
+
def get_header( name )
|
173
|
+
value_hash = @headers[ name.downcase ]
|
174
|
+
return nil if value_hash.nil?
|
175
|
+
return value_hash.values[ 0 ]
|
176
|
+
end
|
177
|
+
|
178
|
+
# Returns the list previously set headers in a name: value Hash.
|
179
|
+
#
|
180
|
+
def headers
|
181
|
+
@headers.inject( {} ) do | result, kv_array |
|
182
|
+
result.merge( kv_array[ 1 ] )
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Add an error to the internal collection. Passes input parameters through
|
187
|
+
# to Hoodoo::Errors#add_error, so see that for details. For convenience,
|
188
|
+
# returns the for-rack representation of the response so far, so that code
|
189
|
+
# which wishes to add one error and abort request processing immediately
|
190
|
+
# can just do:
|
191
|
+
#
|
192
|
+
# return response_object.add_error( ... )
|
193
|
+
#
|
194
|
+
# ...as part of processing a Rack invocation of the +call+ method. This is
|
195
|
+
# really only useful for the service middleware.
|
196
|
+
#
|
197
|
+
# +code+:: Error code (e.g. "platform.generic").
|
198
|
+
# +options+:: Options Hash - see Hoodoo::Errors#add_error.
|
199
|
+
#
|
200
|
+
# Example:
|
201
|
+
#
|
202
|
+
# response.add_error(
|
203
|
+
# 'generic.not_found',
|
204
|
+
# 'message' => 'Optional custom message',
|
205
|
+
# 'reference' => { :ident => 'mandatory reference data' }
|
206
|
+
# )
|
207
|
+
#
|
208
|
+
# In the above example, the mandatory reference data +uuid+ comes
|
209
|
+
# from the description for the 'platform.not_found' message - see the
|
210
|
+
# Hoodoo::ErrorDescriptions#initialize _implementation_ and Platform API.
|
211
|
+
#
|
212
|
+
def add_error( code, options = nil )
|
213
|
+
@errors.add_error( code, options )
|
214
|
+
return for_rack()
|
215
|
+
end
|
216
|
+
|
217
|
+
# Add a precompiled error to the error collection. Pass error code,
|
218
|
+
# error message and reference data directly.
|
219
|
+
#
|
220
|
+
# In most cases you should be calling #add_error instead, *NOT* here.
|
221
|
+
#
|
222
|
+
# *No* *validation* is performed. You should only really call here if
|
223
|
+
# storing an error / errors from another, trusted source with assumed
|
224
|
+
# validity (e.g. another service called remotely with errors in the JSON
|
225
|
+
# response). It's possible to store invalid error data using this call,
|
226
|
+
# which means counter-to-documentation results could be returned to API
|
227
|
+
# clients. That is Very Bad.
|
228
|
+
#
|
229
|
+
# Pass optionally the HTTP status code to use if this happens to be the
|
230
|
+
# first stored error. If this is omitted, 500 is kept as the default.
|
231
|
+
#
|
232
|
+
# As with #add_error, returns a Rack representation of the response.
|
233
|
+
#
|
234
|
+
def add_precompiled_error( code, message, reference, http_status = 500 )
|
235
|
+
@errors.add_precompiled_error( code, message, reference, http_status )
|
236
|
+
return for_rack()
|
237
|
+
end
|
238
|
+
|
239
|
+
# Add errors from a Hoodoo::Errors instance to this response's error
|
240
|
+
# collection.
|
241
|
+
#
|
242
|
+
# +errors_object+:: Hoodoo::Errors instance to merge into the error
|
243
|
+
# collection of 'this' response object.
|
244
|
+
#
|
245
|
+
# Returns +true+ if errors were merged, else +false+ (the source
|
246
|
+
# collection was empty).
|
247
|
+
#
|
248
|
+
def add_errors( errors_object )
|
249
|
+
return @errors.merge!( errors_object )
|
250
|
+
end
|
251
|
+
|
252
|
+
# Set the standard not found error message (generic.not_found), to
|
253
|
+
# be used durning a 'show' call when the requested resource does not
|
254
|
+
# exist.
|
255
|
+
#
|
256
|
+
# +ident+:: The identifier of the resource which was not found
|
257
|
+
#
|
258
|
+
# Example:
|
259
|
+
#
|
260
|
+
# return response.not_found( ident ) if resource.nil?
|
261
|
+
#
|
262
|
+
def not_found( ident )
|
263
|
+
@errors.add_error( 'generic.not_found', :reference => { :ident => ident } )
|
264
|
+
end
|
265
|
+
|
266
|
+
# Convert the internal response data into something that Rack expects.
|
267
|
+
# The return value of this method can be passed back to Rack from Rack
|
268
|
+
# middleware or applications. Usually, this is only called directly by
|
269
|
+
# Hoodoo::Services::Middleware.
|
270
|
+
#
|
271
|
+
def for_rack
|
272
|
+
|
273
|
+
rack_response = Rack::Response.new
|
274
|
+
|
275
|
+
# Work out the status code and basic response body
|
276
|
+
|
277
|
+
if @errors.has_errors?
|
278
|
+
http_status_code = @errors.http_status_code
|
279
|
+
body_data = @errors.render( @interaction_id )
|
280
|
+
else
|
281
|
+
http_status_code = @http_status_code
|
282
|
+
body_data = @body
|
283
|
+
end
|
284
|
+
|
285
|
+
rack_response.status = http_status_code.to_i
|
286
|
+
|
287
|
+
# We're not using JSON5, so the Platform API says that outmost arrays
|
288
|
+
# are wrapped with a top-level object key "_data".
|
289
|
+
|
290
|
+
if body_data.is_a?( ::Array )
|
291
|
+
response_hash = { '_data' => body_data }
|
292
|
+
response_hash[ '_dataset_size' ] = @dataset_size unless @dataset_size.nil?
|
293
|
+
response_string = ::JSON.generate( response_hash )
|
294
|
+
elsif body_data.is_a?( ::Hash )
|
295
|
+
response_string = ::JSON.generate( body_data )
|
296
|
+
elsif body_data.is_a?( ::String )
|
297
|
+
response_string = body_data
|
298
|
+
else
|
299
|
+
raise "Hoodoo::Services::Response\#for_rack given unrecognised body data class '#{ body_data.class.name }'"
|
300
|
+
end
|
301
|
+
|
302
|
+
rack_response.write( response_string )
|
303
|
+
|
304
|
+
# Finally, sort out the headers
|
305
|
+
|
306
|
+
headers().each do | header_name, header_value |
|
307
|
+
rack_response[ header_name ] = header_value
|
308
|
+
end
|
309
|
+
|
310
|
+
# Return the complete response
|
311
|
+
|
312
|
+
return rack_response.finish
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
end; end
|