hoodoo 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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,194 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: auto_session.rb
|
3
|
+
# (C):: Loyalty New Zealand 2015
|
4
|
+
#
|
5
|
+
# Purpose:: Resource endpoint definition.
|
6
|
+
# ----------------------------------------------------------------------
|
7
|
+
# 12-Mar-2015 (ADH): Created.
|
8
|
+
########################################################################
|
9
|
+
|
10
|
+
module Hoodoo
|
11
|
+
class Client # Just used as a namespace here
|
12
|
+
class Endpoint # Just used as a namespace here
|
13
|
+
|
14
|
+
# This endpoint wraps something which does _actual_ communication but
|
15
|
+
# requires a session; it maintains a valid session for that wrapped
|
16
|
+
# endpoint automatically. It implements the following model:
|
17
|
+
#
|
18
|
+
# - It requires a Caller ID and Caller Authentication Secret to be
|
19
|
+
# instantiated (via the +options+ parameter).
|
20
|
+
#
|
21
|
+
# - If there is no session ID available, it creates one using the above
|
22
|
+
# details. Otherwise, it tries to use the given session.
|
23
|
+
#
|
24
|
+
# - If a particular request leads to an 'invalid session' response, the
|
25
|
+
# request is marked for retry. A new session is obtained first, the
|
26
|
+
# retry happens and if it fails this time, the failure is returned to
|
27
|
+
# the caller.
|
28
|
+
#
|
29
|
+
# Since it wraps another endpoint and requires Caller information to be
|
30
|
+
# able to build sessions, instantiation requirements are rather unusual
|
31
|
+
# - see #configure_with for details.
|
32
|
+
#
|
33
|
+
class AutoSession < Hoodoo::Client::Endpoint
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
# See Hoodoo::Client::Endpoint#configure_with.
|
38
|
+
#
|
39
|
+
# Configuration option keys which _must_ be supplied are:
|
40
|
+
#
|
41
|
+
# +caller_id+:: The UUID of the Caller instance to be used
|
42
|
+
# for session creation.
|
43
|
+
#
|
44
|
+
# +caller_secret+:: The authentication secret of the Caller
|
45
|
+
# instance to be used for session creation.
|
46
|
+
#
|
47
|
+
# +session_endpoint:: A Hooodo::Client::Endpoint subclass which
|
48
|
+
# can be used for talking to the Session
|
49
|
+
# endpoint (for obvious reasons!).
|
50
|
+
#
|
51
|
+
# +discovery_result+:: A Hoodoo::Services::Discovery::ForRemote
|
52
|
+
# instance describing the required, remotely
|
53
|
+
# available resource endpoint.
|
54
|
+
#
|
55
|
+
# The pattern for creating and using this instance is:
|
56
|
+
#
|
57
|
+
# * Discover the location of the remote resource.
|
58
|
+
#
|
59
|
+
# * Use the discovery result to build an appropriate Endpoint
|
60
|
+
# subclass instance, e.g. Hoodoo::Client::Endpoint::HTTP.
|
61
|
+
#
|
62
|
+
# * Create a Hoodoo::Services::Discovery::ForRemote instance which
|
63
|
+
# describes the above endpoint via the +wrapped_endpoint+ option.
|
64
|
+
#
|
65
|
+
# * Build an instance of this auto-session Endpoint subclass,
|
66
|
+
# giving it the above object as the +discovery_result+.
|
67
|
+
#
|
68
|
+
# * Use this endpoint in the normal fashion. All the special
|
69
|
+
# mechanics of session management are handled here.
|
70
|
+
#
|
71
|
+
def configure_with( resource, version, options )
|
72
|
+
@caller_id = options[ :caller_id ]
|
73
|
+
@caller_secret = options[ :caller_secret ]
|
74
|
+
@session_endpoint = options[ :session_endpoint ]
|
75
|
+
@wrapped_endpoint = @discovery_result.wrapped_endpoint
|
76
|
+
end
|
77
|
+
|
78
|
+
public
|
79
|
+
|
80
|
+
# See Hoodoo::Client::Endpoint#list.
|
81
|
+
#
|
82
|
+
def list( query_hash = nil )
|
83
|
+
return auto_retry( :list, query_hash )
|
84
|
+
end
|
85
|
+
|
86
|
+
# See Hoodoo::Client::Endpoint#show.
|
87
|
+
#
|
88
|
+
def show( ident, query_hash = nil )
|
89
|
+
return auto_retry( :show, ident, query_hash )
|
90
|
+
end
|
91
|
+
|
92
|
+
# See Hoodoo::Client::Endpoint#create.
|
93
|
+
#
|
94
|
+
def create( body_hash, query_hash = nil )
|
95
|
+
return auto_retry( :create, body_hash, query_hash )
|
96
|
+
end
|
97
|
+
|
98
|
+
# See Hoodoo::Client::Endpoint#update.
|
99
|
+
#
|
100
|
+
def update( ident, body_hash, query_hash = nil )
|
101
|
+
return auto_retry( :update, ident, body_hash, query_hash )
|
102
|
+
end
|
103
|
+
|
104
|
+
# See Hoodoo::Client::Endpoint#delete.
|
105
|
+
#
|
106
|
+
def delete( ident, query_hash = nil )
|
107
|
+
return auto_retry( :delete, ident, query_hash )
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
# Try to perform an action through the wrapped endpoint, acquiring a
|
113
|
+
# session first if need be or if necessary reacquiring a session and
|
114
|
+
# retrying the request.
|
115
|
+
#
|
116
|
+
# +action+:: The name of the method to call in the wrapped endpoint
|
117
|
+
# - see Hoodoo::Services::Middleware::ALLOWED_ACTIONS.
|
118
|
+
#
|
119
|
+
# *args:: Any other arguments to pass to +action+.
|
120
|
+
#
|
121
|
+
def auto_retry( action, *args )
|
122
|
+
|
123
|
+
copy_updated_options_to( @wrapped_endpoint )
|
124
|
+
|
125
|
+
# We use the session endpoint as a session ID cache, in essence,
|
126
|
+
# storing the acquired ID there and passing it into the wrapped
|
127
|
+
# endpoint for the 'real' calls.
|
128
|
+
|
129
|
+
if @session_endpoint.session_id.nil?
|
130
|
+
session_creation_result = acquire_session_for( action )
|
131
|
+
return session_creation_result unless session_creation_result.nil?
|
132
|
+
else
|
133
|
+
@wrapped_endpoint.session_id = @session_endpoint.session_id
|
134
|
+
end
|
135
|
+
|
136
|
+
result = @wrapped_endpoint.send( action, *args )
|
137
|
+
|
138
|
+
if result.platform_errors.has_errors? &&
|
139
|
+
result.platform_errors.errors.size == 1 &&
|
140
|
+
result.platform_errors.errors[ 0 ][ 'code' ] == 'platform.invalid_session'
|
141
|
+
|
142
|
+
session_creation_result = acquire_session_for( action )
|
143
|
+
return session_creation_result unless session_creation_result.nil?
|
144
|
+
return @wrapped_endpoint.send( action, *args )
|
145
|
+
else
|
146
|
+
return result
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Acquire a sessino using the configured session endpoint. If this
|
151
|
+
# fails, the failure result is returned. If it seems to succeed but
|
152
|
+
# a session ID cannot be found, an internal 'generic.malformed'
|
153
|
+
# result is generated and returned.
|
154
|
+
#
|
155
|
+
# The returned data uses an appropriate response class for the
|
156
|
+
# action at hand - an augmented array for lists, else an augmented
|
157
|
+
# hash. It can be returned directly up to the calling layer.
|
158
|
+
#
|
159
|
+
# Returns +nil+ if all goes well; #session_id will be updated.
|
160
|
+
#
|
161
|
+
# +action+:: As given to #auto_retry.
|
162
|
+
#
|
163
|
+
def acquire_session_for( action )
|
164
|
+
session_creation_result = @session_endpoint.create(
|
165
|
+
'caller_id' => @caller_id,
|
166
|
+
'authentication_secret' => @caller_secret
|
167
|
+
)
|
168
|
+
|
169
|
+
if session_creation_result.platform_errors.has_errors?
|
170
|
+
data = response_class_for( action ).new
|
171
|
+
data.platform_errors.merge!( session_creation_result.platform_errors )
|
172
|
+
return data
|
173
|
+
end
|
174
|
+
|
175
|
+
@session_endpoint.session_id = session_creation_result[ 'id' ]
|
176
|
+
|
177
|
+
if @session_endpoint.session_id.nil? || @session_endpoint.session_id.empty?
|
178
|
+
data = response_class_for( action ).new
|
179
|
+
data.platform_errors.add_error(
|
180
|
+
'generic.malformed',
|
181
|
+
'message' => 'Received bad session description from Session endpoint despite "200" response code'
|
182
|
+
)
|
183
|
+
|
184
|
+
return data
|
185
|
+
else
|
186
|
+
@wrapped_endpoint.session_id = @session_endpoint.session_id
|
187
|
+
return nil
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: http.rb
|
3
|
+
# (C):: Loyalty New Zealand 2015
|
4
|
+
#
|
5
|
+
# Purpose:: Resource endpoint definition.
|
6
|
+
# ----------------------------------------------------------------------
|
7
|
+
# 05-Mar-2015 (ADH): Created.
|
8
|
+
########################################################################
|
9
|
+
|
10
|
+
require 'net/http'
|
11
|
+
require 'net/https'
|
12
|
+
|
13
|
+
module Hoodoo
|
14
|
+
class Client # Just used as a namespace here
|
15
|
+
class Endpoint # Just used as a namespace here
|
16
|
+
|
17
|
+
# Talk to a resource that is contacted over HTTP or HTTPS.
|
18
|
+
#
|
19
|
+
# Configured with a Hoodoo::Services::Discovery::ForHTTP discovery
|
20
|
+
# result instance.
|
21
|
+
#
|
22
|
+
class HTTP < Hoodoo::Client::Endpoint::HTTPBased
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
# See Hoodoo::Client::Endpoint#configure_with.
|
27
|
+
#
|
28
|
+
# Requires a Hoodoo::Services::Discovery::ForHTTP instance in the
|
29
|
+
# +discovery_result+ field of the +options+ Hash.
|
30
|
+
#
|
31
|
+
def configure_with( resource, version, options )
|
32
|
+
unless @discovery_result.is_a?( Hoodoo::Services::Discovery::ForHTTP )
|
33
|
+
raise "Hoodoo::Client::Endpoint::HTTP must be configured with a Hoodoo::Services::Discovery::ForHTTP instance - got '#{ @discovery_result.class.name }'"
|
34
|
+
end
|
35
|
+
|
36
|
+
@description = Hoodoo::Client::Endpoint::HTTPBased::DescriptionOfRequest.new
|
37
|
+
@description.discovery_result = @discovery_result
|
38
|
+
@description.endpoint_uri = @discovery_result.endpoint_uri
|
39
|
+
@description.proxy_uri = @discovery_result.proxy_uri
|
40
|
+
@description.ca_file = @discovery_result.ca_file
|
41
|
+
@description.http_timeout = @discovery_result.http_timeout
|
42
|
+
end
|
43
|
+
|
44
|
+
public
|
45
|
+
|
46
|
+
# See Hoodoo::Client::Endpoint#list.
|
47
|
+
#
|
48
|
+
def list( query_hash = nil )
|
49
|
+
d = @description.dup # This does NOT dup the objects to which @description points
|
50
|
+
d.action = :list
|
51
|
+
d.query_hash = query_hash
|
52
|
+
|
53
|
+
return do_http( d )
|
54
|
+
end
|
55
|
+
|
56
|
+
# See Hoodoo::Client::Endpoint#show.
|
57
|
+
#
|
58
|
+
def show( ident, query_hash = nil )
|
59
|
+
d = @description.dup
|
60
|
+
d.action = :show
|
61
|
+
d.ident = ident
|
62
|
+
d.query_hash = query_hash
|
63
|
+
|
64
|
+
return do_http( d )
|
65
|
+
end
|
66
|
+
|
67
|
+
# See Hoodoo::Client::Endpoint#create.
|
68
|
+
#
|
69
|
+
def create( body_hash, query_hash = nil )
|
70
|
+
d = @description.dup
|
71
|
+
d.action = :create
|
72
|
+
d.body_hash = body_hash
|
73
|
+
d.query_hash = query_hash
|
74
|
+
|
75
|
+
return do_http( d )
|
76
|
+
end
|
77
|
+
|
78
|
+
# See Hoodoo::Client::Endpoint#update.
|
79
|
+
#
|
80
|
+
def update( ident, body_hash, query_hash = nil )
|
81
|
+
d = @description.dup
|
82
|
+
d.action = :update
|
83
|
+
d.ident = ident
|
84
|
+
d.body_hash = body_hash
|
85
|
+
d.query_hash = query_hash
|
86
|
+
|
87
|
+
return do_http( d )
|
88
|
+
end
|
89
|
+
|
90
|
+
# See Hoodoo::Client::Endpoint#delete.
|
91
|
+
#
|
92
|
+
def delete( ident, query_hash = nil )
|
93
|
+
d = @description.dup
|
94
|
+
d.action = :delete
|
95
|
+
d.ident = ident
|
96
|
+
d.query_hash = query_hash
|
97
|
+
|
98
|
+
return do_http( d )
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
# Make real HTTP(S) request to a target resource and return the
|
104
|
+
# result as a Hoodoo::Client::AugmentedArray (for 'list' calls) or
|
105
|
+
# Hoodoo::Client::AugumentedHash (for all other calls) instance.
|
106
|
+
#
|
107
|
+
# +description_of_request+:: A Hoodoo::Client::Endpoint::HTTPBased::DescriptionOfRequest
|
108
|
+
# instance with all the request details
|
109
|
+
# set inside. The +discovery_data+ field
|
110
|
+
# must refer to a
|
111
|
+
# Hoodoo::Services::Discovery::ForHTTP
|
112
|
+
# instance (not re-checked internally).
|
113
|
+
#
|
114
|
+
def do_http( description_of_request )
|
115
|
+
|
116
|
+
data = get_data_for_request( description_of_request )
|
117
|
+
|
118
|
+
action = description_of_request.action
|
119
|
+
proxy = description_of_request.proxy_uri
|
120
|
+
ca_file = description_of_request.ca_file
|
121
|
+
http_timeout = description_of_request.http_timeout
|
122
|
+
|
123
|
+
proxy_host = :ENV
|
124
|
+
proxy_port = proxy_user = proxy_pass = nil
|
125
|
+
|
126
|
+
unless proxy.nil?
|
127
|
+
proxy_host = proxy.host
|
128
|
+
proxy_port = proxy.port
|
129
|
+
proxy_user = proxy.user
|
130
|
+
proxy_pass = proxy.password
|
131
|
+
end
|
132
|
+
|
133
|
+
http = Net::HTTP.new(
|
134
|
+
data.full_uri.host,
|
135
|
+
data.full_uri.port,
|
136
|
+
proxy_host,
|
137
|
+
proxy_port,
|
138
|
+
proxy_user,
|
139
|
+
proxy_pass
|
140
|
+
)
|
141
|
+
|
142
|
+
if data.full_uri.scheme == 'https'
|
143
|
+
http.use_ssl = true
|
144
|
+
|
145
|
+
# The verify_mode is *important* - VERIFY_PEER ensures that we always validate
|
146
|
+
# the connection, *and* that the presented SSL Certificate by the endpoint is
|
147
|
+
# verifiable through our CA certificate trust store.
|
148
|
+
#
|
149
|
+
# To use a self-signed cert, you may configure the ca_file to a CA that
|
150
|
+
# includes the self-signed cert, but the verify_mode setting should remain.
|
151
|
+
#
|
152
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
153
|
+
|
154
|
+
if ca_file
|
155
|
+
http.ca_file = ca_file
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
http.read_timeout = http_timeout unless http_timeout.nil?
|
160
|
+
|
161
|
+
request_class = {
|
162
|
+
:create => Net::HTTP::Post,
|
163
|
+
:update => Net::HTTP::Patch,
|
164
|
+
:delete => Net::HTTP::Delete
|
165
|
+
}[ action ] || Net::HTTP::Get
|
166
|
+
|
167
|
+
request = request_class.new( data.full_uri.request_uri() )
|
168
|
+
request.body = data.body_string unless data.body_string.empty?
|
169
|
+
|
170
|
+
request.initialize_http_header( data.header_hash )
|
171
|
+
|
172
|
+
description_of_response = DescriptionOfResponse.new
|
173
|
+
description_of_response.action = action
|
174
|
+
description_of_response.http_headers = {}
|
175
|
+
|
176
|
+
begin
|
177
|
+
http_response = http.request( request )
|
178
|
+
|
179
|
+
description_of_response.http_status_code = http_response.code.to_i
|
180
|
+
description_of_response.http_headers = http_response
|
181
|
+
description_of_response.raw_body_data = http_response.body
|
182
|
+
|
183
|
+
rescue Errno::ECONNREFUSED => e
|
184
|
+
description_of_response.http_status_code = 404
|
185
|
+
description_of_response.raw_body_data = ''
|
186
|
+
|
187
|
+
rescue Net::ReadTimeout => e
|
188
|
+
description_of_response.http_status_code = 408
|
189
|
+
description_of_response.raw_body_data = ''
|
190
|
+
|
191
|
+
rescue => e
|
192
|
+
description_of_response.http_status_code = 500
|
193
|
+
description_of_response.raw_body_data = e.message
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
return get_data_for_response( description_of_response )
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,367 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: http_based.rb
|
3
|
+
# (C):: Loyalty New Zealand 2015
|
4
|
+
#
|
5
|
+
# Purpose:: Resource endpoint definition.
|
6
|
+
# ----------------------------------------------------------------------
|
7
|
+
# 05-Mar-2015 (ADH): Created.
|
8
|
+
########################################################################
|
9
|
+
|
10
|
+
require 'json'
|
11
|
+
|
12
|
+
module Hoodoo
|
13
|
+
class Client # Just used as a namespace here
|
14
|
+
class Endpoint # Just used as a namespace here
|
15
|
+
|
16
|
+
# Base class for endpoints that have an HTTP basis to their request
|
17
|
+
# and responses, even if the underlying transport is not HTTP. This
|
18
|
+
# is basically a collection of library-like routines useful to such
|
19
|
+
# classes and specifically excludes the part which actually makes
|
20
|
+
# an HTTP call (or AMQP call, or whatever) to a resource. That's up
|
21
|
+
# to the subclass.
|
22
|
+
#
|
23
|
+
# This must never be instantiated directly as an endpoint. Instead,
|
24
|
+
# instantiate a subclass such as Hoodoo::Client::Endpoint::HTTP or
|
25
|
+
# Hoodoo::Client::Endpoint::AMQP.
|
26
|
+
#
|
27
|
+
class HTTPBased < Hoodoo::Client::Endpoint
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
# Describe a request for HTTP-like endpoints.
|
32
|
+
#
|
33
|
+
class DescriptionOfRequest
|
34
|
+
|
35
|
+
# The action to perform - a Symbol from
|
36
|
+
# Hoodoo::Services::Middleware::ALLOWED_ACTIONS.
|
37
|
+
#
|
38
|
+
attr_accessor :action
|
39
|
+
|
40
|
+
# A Hoodoo::Services::Discovery "For..." family member instance
|
41
|
+
# giving information required to 'find' the target resource. The
|
42
|
+
# required class instance depends upon the endpoint in use.
|
43
|
+
#
|
44
|
+
attr_accessor :discovery_result
|
45
|
+
|
46
|
+
# The full HTTP URI (or equivalent HTTP URI for HTTP-like, but
|
47
|
+
# non-HTTP systems like AMQP) at which the endpoint is found.
|
48
|
+
# Excludes any query string or resource identifier portion (it
|
49
|
+
# is the "list" action URI without query data, in essence)
|
50
|
+
#
|
51
|
+
attr_accessor :endpoint_uri
|
52
|
+
|
53
|
+
# Full URI (as a URI object) of an HTTP proxy to use as an
|
54
|
+
# override to <tt>ENV['HTTP_PROXY']</tt> which Ruby itself
|
55
|
+
# will otherwise read. Will be +nil+ for no proxy override.
|
56
|
+
#
|
57
|
+
attr_accessor :proxy_uri
|
58
|
+
|
59
|
+
# An optional String indicating a relative or absolute file
|
60
|
+
# path to the location of a .pem format Certificate
|
61
|
+
# Authority file (trust store), which may include multliple
|
62
|
+
# certificates. The certificates in the file will be used
|
63
|
+
# by Net::HTTP to validate the SSL Ceritificate Chain
|
64
|
+
# presented by remote servers, when calling endpoints over
|
65
|
+
# HTTPS with Hoodoo::Client.
|
66
|
+
#
|
67
|
+
# Default +nil+ value should be used in nearly all cases
|
68
|
+
# and uses Ruby OpenSSL defaults which are generally
|
69
|
+
# Operating System provided.
|
70
|
+
#
|
71
|
+
attr_accessor :ca_file
|
72
|
+
|
73
|
+
# Optional Float indicating the Net::HTTP read timeout value.
|
74
|
+
# This operates at the HTTP transport level and is independent
|
75
|
+
# of any timeouts set within the API providing server.
|
76
|
+
#
|
77
|
+
attr_accessor :http_timeout
|
78
|
+
|
79
|
+
# Optional Hash of query data.
|
80
|
+
#
|
81
|
+
attr_accessor :query_hash
|
82
|
+
|
83
|
+
# Optional Hash of body data for actions +:create+ and +:update+.
|
84
|
+
#
|
85
|
+
attr_accessor :body_hash
|
86
|
+
|
87
|
+
# Optional resource identifier for actions +:show+, +:update+ and
|
88
|
+
# +:delete+:
|
89
|
+
#
|
90
|
+
attr_accessor :ident
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
# Description of data that will be used for request - essentially a
|
95
|
+
# compilation of a DescriptionOfRequest instance produced via a call
|
96
|
+
# to #get_data_for_request.
|
97
|
+
#
|
98
|
+
class DataForRequest
|
99
|
+
|
100
|
+
# The full HTTP URI (or equivalent HTTP URI for HTTP-like, but
|
101
|
+
# non-HTTP systems like AMQP) for the call, including any resource
|
102
|
+
# identifier and query data.
|
103
|
+
#
|
104
|
+
attr_accessor :full_uri
|
105
|
+
|
106
|
+
# String of compiled body data for all actions (may be empty).
|
107
|
+
#
|
108
|
+
attr_accessor :body_string
|
109
|
+
|
110
|
+
# Hash of headers; keys are HTTP header names as a Strings (e.g.
|
111
|
+
# "Content-Type", "X-Interaction-ID"), values are header values
|
112
|
+
# as Strings.
|
113
|
+
#
|
114
|
+
attr_accessor :header_hash
|
115
|
+
|
116
|
+
# Hash of query; keys are query keys as Strings (e.g.
|
117
|
+
# "search"), values are query values as URL encoded Strings. (e.g.
|
118
|
+
# "outlet_id%3Dd32a0e15754a486989fdde2b0830fe12")
|
119
|
+
#
|
120
|
+
attr_accessor :query_hash
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
# Description of data describing an HTTP response. Used by
|
125
|
+
# #get_data_for_response to generate a response array or hash
|
126
|
+
# (see #response_class_for).
|
127
|
+
#
|
128
|
+
class DescriptionOfResponse
|
129
|
+
|
130
|
+
# The action that was performed - a Symbol from
|
131
|
+
# Hoodoo::Services::Middleware::ALLOWED_ACTIONS.
|
132
|
+
#
|
133
|
+
attr_accessor :action
|
134
|
+
|
135
|
+
# The HTTP status code _as an Integer_.
|
136
|
+
#
|
137
|
+
attr_accessor :http_status_code
|
138
|
+
|
139
|
+
# The raw ("unparsed") returned body data as a String.
|
140
|
+
#
|
141
|
+
attr_accessor :raw_body_data
|
142
|
+
|
143
|
+
# An object that will allow Hash-like lookup and iteration of
|
144
|
+
# key/value pairs via +each+. A raw Net::HTTPOK (sic.) instance
|
145
|
+
# from a successful response is an example of such an object.
|
146
|
+
#
|
147
|
+
# May be unset (+nil+) or empty, especially for error cases.
|
148
|
+
#
|
149
|
+
attr_accessor :http_headers
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
# Preprocess a high level request description, returning HTTP
|
154
|
+
# orientated compiled data as a DataForRequest instance.
|
155
|
+
#
|
156
|
+
# +description_of_request+:: DescriptionOfRequest instance.
|
157
|
+
#
|
158
|
+
def get_data_for_request( description_of_request )
|
159
|
+
body_hash = Hoodoo::Utilities.stringify( description_of_request.body_hash )
|
160
|
+
query_hash = Hoodoo::Utilities.stringify( description_of_request.query_hash )
|
161
|
+
ident = description_of_request.ident.to_s
|
162
|
+
|
163
|
+
body_data = body_hash.nil? ? '' : ::JSON.generate( body_hash )
|
164
|
+
|
165
|
+
# Amazingly, there's no fast way to deep clone a URI. Long story
|
166
|
+
# short - Marshal.load(Marshal.dump(uri)) takes, astonishingly,
|
167
|
+
# twice as long to execute as URI.parse(uri.to_s). I have no idea
|
168
|
+
# how that's possible. The Addressable gem is even slower.
|
169
|
+
#
|
170
|
+
# require 'benchmark'
|
171
|
+
# require 'addressable/uri' # Assuming gem is present
|
172
|
+
#
|
173
|
+
# s='http://user:password@pond.org.uk:9924/foo/bar.baz?thing=that'
|
174
|
+
# u=URI.parse(s)
|
175
|
+
# a=Addressable::URI.parse(s)
|
176
|
+
#
|
177
|
+
# Benchmark.realtime { 1000000.times { u2=URI.parse(u.to_s) } }
|
178
|
+
# # => 14.110195
|
179
|
+
# Benchmark.realtime { 1000000.times { a2=a.dup } }
|
180
|
+
# # => 26.530487
|
181
|
+
# Benchmark.realtime { 1000000.times { u2=Marshal.load(Marshal.dump(u)) } }
|
182
|
+
# # => 22.048637
|
183
|
+
#
|
184
|
+
# ...repeatably.
|
185
|
+
#
|
186
|
+
# TODO: Is it possible to improve this? It's truly awful, to the
|
187
|
+
# extent I'm almost motivated to write a URI handler gem.
|
188
|
+
# The core library URI API is tragically bad.
|
189
|
+
|
190
|
+
remote_uri = URI.parse( description_of_request.endpoint_uri.to_s )
|
191
|
+
|
192
|
+
# Now we've a copy, we can use high level URI methods to manipulate
|
193
|
+
# it to form the full request URI.
|
194
|
+
|
195
|
+
remote_uri.path << "/#{ URI::escape( ident ) }" unless ident.nil?
|
196
|
+
|
197
|
+
# Grey area over whether this encodes spaces as "%20" or "+", but
|
198
|
+
# so long as the middleware consistently uses the URI encode/decode
|
199
|
+
# calls, it should work out in the end anyway.
|
200
|
+
|
201
|
+
unless query_hash.nil?
|
202
|
+
query_hash = query_hash.dup
|
203
|
+
query_hash[ 'search' ] = URI.encode_www_form( query_hash[ 'search' ] ) if ( query_hash[ 'search' ].is_a?( ::Hash ) )
|
204
|
+
query_hash[ 'filter' ] = URI.encode_www_form( query_hash[ 'filter' ] ) if ( query_hash[ 'filter' ].is_a?( ::Hash ) )
|
205
|
+
|
206
|
+
query_hash[ '_embed' ] = query_hash[ '_embed' ].join( ',' ) if ( query_hash[ '_embed' ].is_a?( ::Array ) )
|
207
|
+
query_hash[ '_reference' ] = query_hash[ '_reference' ].join( ',' ) if ( query_hash[ '_reference' ].is_a?( ::Array ) )
|
208
|
+
|
209
|
+
query_hash.delete( 'search' ) if query_hash[ 'search' ].nil? || query_hash[ 'search' ].empty?
|
210
|
+
query_hash.delete( 'filter' ) if query_hash[ 'filter' ].nil? || query_hash[ 'filter' ].empty?
|
211
|
+
query_hash.delete( '_embed' ) if query_hash[ '_embed' ].nil? || query_hash[ '_embed' ].empty?
|
212
|
+
query_hash.delete( '_reference' ) if query_hash[ '_reference' ].nil? || query_hash[ '_reference' ].empty?
|
213
|
+
end
|
214
|
+
|
215
|
+
remote_uri.query = URI.encode_www_form( query_hash ) unless query_hash.nil? || query_hash.empty?
|
216
|
+
|
217
|
+
headers = {
|
218
|
+
'Content-Type' => 'application/json; charset=utf-8',
|
219
|
+
'Content-Language' => self.locale() || 'en-nz', # Locale comes from Endpoint superclass
|
220
|
+
'Accept-Language' => self.locale() || 'en-nz'
|
221
|
+
}
|
222
|
+
|
223
|
+
# Interaction comes from Endpoint superclass.
|
224
|
+
#
|
225
|
+
# TODO: Can anything be done about inbound X-Interaction-ID
|
226
|
+
# headers or interaction ID values specified by the
|
227
|
+
# calling client which would be stripped by an Alchemy
|
228
|
+
# architecture but not by conventional HTTP servers?
|
229
|
+
#
|
230
|
+
unless self.interaction().nil?
|
231
|
+
headers[ 'X-Interaction-ID' ] = self.interaction().interaction_id
|
232
|
+
end
|
233
|
+
|
234
|
+
# Session ID comes from Endpoint superclass.
|
235
|
+
#
|
236
|
+
unless self.session_id().nil?
|
237
|
+
headers[ 'X-Session-ID'] = self.session_id()
|
238
|
+
end
|
239
|
+
|
240
|
+
# A suite of options is defined by a constant in the Endpoint
|
241
|
+
# superclass.
|
242
|
+
#
|
243
|
+
Hoodoo::Client::Headers::HEADER_TO_PROPERTY.each do | rack_header, description |
|
244
|
+
header_name = description[ :header ]
|
245
|
+
header_proc = description[ :header_proc ]
|
246
|
+
property = description[ :property ]
|
247
|
+
|
248
|
+
property_value = self.send( property )
|
249
|
+
|
250
|
+
unless property_value.nil?
|
251
|
+
headers[ header_name ] = header_proc.call( property_value )
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
data = DataForRequest.new
|
256
|
+
data.full_uri = remote_uri
|
257
|
+
data.body_string = body_data
|
258
|
+
data.header_hash = headers
|
259
|
+
data.query_hash = query_hash
|
260
|
+
|
261
|
+
return data
|
262
|
+
end
|
263
|
+
|
264
|
+
# Process a raw HTTP response description, returning an instance of
|
265
|
+
# Hoodoo::Client::AugmentedArray or Hoodoo::Client::AugmentedHash
|
266
|
+
# with either processed body data inside, or error data associated.
|
267
|
+
#
|
268
|
+
# +description_of_response+:: DescriptionOfResponse instance.
|
269
|
+
#
|
270
|
+
def get_data_for_response( description_of_response )
|
271
|
+
code = description_of_response.http_status_code
|
272
|
+
body = description_of_response.raw_body_data
|
273
|
+
|
274
|
+
begin
|
275
|
+
parsed = ::JSON.parse(
|
276
|
+
body,
|
277
|
+
:object_class => Hoodoo::Client::AugmentedHash,
|
278
|
+
:array_class => Hoodoo::Client::AugmentedArray
|
279
|
+
)
|
280
|
+
|
281
|
+
rescue => e
|
282
|
+
data = response_class_for( description_of_response.action ).new
|
283
|
+
data.response_options = Hoodoo::Client::Headers.x_header_to_options(
|
284
|
+
description_of_response.http_headers
|
285
|
+
)
|
286
|
+
|
287
|
+
case code
|
288
|
+
when 404
|
289
|
+
return generate_404_response_for( description_of_response.action )
|
290
|
+
when 408
|
291
|
+
data.platform_errors.add_error( 'platform.timeout' )
|
292
|
+
when 200
|
293
|
+
data.platform_errors.add_error(
|
294
|
+
'platform.fault',
|
295
|
+
:message => 'Could not parse body data returned from inter-resource call despite receiving HTTP status code 200',
|
296
|
+
:reference => { :exception => RuntimeError.new( "#{ body }" ) }
|
297
|
+
)
|
298
|
+
when 204
|
299
|
+
if data.response_options[ 'deja_vu' ] != 'confirmed'
|
300
|
+
data.platform_errors.add_error(
|
301
|
+
'platform.fault',
|
302
|
+
:message => "Unexpected raw HTTP status code 204 with 'X-Deja-Vu: confirmed' not present",
|
303
|
+
:reference => { :exception => RuntimeError.new( '204' ) }
|
304
|
+
)
|
305
|
+
end # Else do nothing; keep the empty 'data'
|
306
|
+
else
|
307
|
+
data.platform_errors.add_error(
|
308
|
+
'platform.fault',
|
309
|
+
:message => "Unexpected raw HTTP status code #{ code } with non-JSON response",
|
310
|
+
:reference => { :exception => RuntimeError.new( "#{ body }" ) }
|
311
|
+
)
|
312
|
+
end
|
313
|
+
|
314
|
+
return data
|
315
|
+
end
|
316
|
+
|
317
|
+
# Just in case someone changes JSON parsers under us and the
|
318
|
+
# replacement doesn't support the options used above...
|
319
|
+
|
320
|
+
unless parsed.is_a?( Hoodoo::Client::AugmentedHash )
|
321
|
+
raise "Hoodoo::Services::Middleware: Incompatible JSON implementation in use which doesn't understand 'object_class' or 'array_class' options"
|
322
|
+
end
|
323
|
+
|
324
|
+
# If the parsed data wrapped an array, extract just the array
|
325
|
+
# part, else the hash part.
|
326
|
+
|
327
|
+
if ( parsed[ '_data' ].is_a?( ::Array ) )
|
328
|
+
size = parsed[ '_dataset_size' ]
|
329
|
+
parsed = parsed[ '_data' ]
|
330
|
+
parsed.dataset_size = size
|
331
|
+
|
332
|
+
elsif ( parsed[ 'kind' ] == 'Errors' )
|
333
|
+
|
334
|
+
# This isn't an array, it's an AugmentedHash describing errors.
|
335
|
+
# Turn this into a formal errors collection.
|
336
|
+
|
337
|
+
errors_from_resource = Hoodoo::Errors.new()
|
338
|
+
|
339
|
+
parsed[ 'errors' ].each do | error |
|
340
|
+
errors_from_resource.add_precompiled_error(
|
341
|
+
error[ 'code' ],
|
342
|
+
error[ 'message' ],
|
343
|
+
error[ 'reference' ],
|
344
|
+
code
|
345
|
+
)
|
346
|
+
end
|
347
|
+
|
348
|
+
# Use a 'clean' copy of the response class rather than keeping
|
349
|
+
# the originating data. People will not make assumptions about
|
350
|
+
# error payloads and trip over with the early return 404 stuff
|
351
|
+
# etc. this way.
|
352
|
+
|
353
|
+
parsed = response_class_for( description_of_response.action ).new
|
354
|
+
parsed.set_platform_errors( errors_from_resource )
|
355
|
+
|
356
|
+
end
|
357
|
+
|
358
|
+
parsed.response_options = Hoodoo::Client::Headers.x_header_to_options(
|
359
|
+
description_of_response.http_headers
|
360
|
+
)
|
361
|
+
|
362
|
+
return parsed
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|