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,1440 @@
|
|
1
|
+
###############################################################################
|
2
|
+
# Remote inter-resource calls
|
3
|
+
#
|
4
|
+
# Start multiple HTTP server instances in threads and have them talk to each
|
5
|
+
# other in order to test remote inter-resource calls.
|
6
|
+
###############################################################################
|
7
|
+
|
8
|
+
require 'spec_helper'
|
9
|
+
require 'json'
|
10
|
+
|
11
|
+
# First, a test service comprised of a couple of 'echo' variants which we use
|
12
|
+
# to make sure they're both correctly stored in the DRb registry.
|
13
|
+
|
14
|
+
class TestEchoImplementation < Hoodoo::Services::Implementation
|
15
|
+
|
16
|
+
public
|
17
|
+
|
18
|
+
def list( context )
|
19
|
+
|
20
|
+
# Deliberate error generation hook.
|
21
|
+
#
|
22
|
+
if context.request.list.offset == 42
|
23
|
+
context.response.add_error( 'platform.malformed' )
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
context.response.set_resources(
|
28
|
+
[
|
29
|
+
{ 'list0' => TestEchoImplementation.to_h( context ) },
|
30
|
+
{ 'list1' => TestEchoImplementation.to_h( context ) },
|
31
|
+
{ 'list2' => TestEchoImplementation.to_h( context ) }
|
32
|
+
],
|
33
|
+
49
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def show( context )
|
38
|
+
if context.request.uri_path_components[ 0 ] == 'return_error'
|
39
|
+
context.response.add_error(
|
40
|
+
'generic.invalid_string',
|
41
|
+
:message => 'Returning error as requested',
|
42
|
+
:reference => { :another => 'no other ident', :field_name => 'no ident' }
|
43
|
+
)
|
44
|
+
elsif context.request.uri_path_components[ 0 ] == 'return_invalid_json'
|
45
|
+
context.response.body = 'Hello, world'
|
46
|
+
else
|
47
|
+
context.response.body = { 'show' => TestEchoImplementation.to_h( context ) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def create( context )
|
52
|
+
|
53
|
+
# Deliberate error generation hook.
|
54
|
+
#
|
55
|
+
if context.request.body.has_key?( 'return_error' )
|
56
|
+
context.response.add_error( 'platform.malformed' )
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
# If asked to add another error to the response, we're testing
|
61
|
+
# the deja-vu stuff including making sure that errors are still returned
|
62
|
+
# if any non-"invalid duplication" stuff exists. As long as the error
|
63
|
+
# code requires no reference data or only requires a "field_name", this
|
64
|
+
# will work OK.
|
65
|
+
#
|
66
|
+
if context.request.deja_vu
|
67
|
+
additional_error_code = context.request.body[ 'additional_error' ] || 'generic.invalid_duplication'
|
68
|
+
reference = { :reference => { :field_name => 'deja_vu' } }
|
69
|
+
|
70
|
+
context.response.add_error( 'generic.invalid_duplication', reference )
|
71
|
+
context.response.add_error( additional_error_code, reference )
|
72
|
+
|
73
|
+
return
|
74
|
+
end
|
75
|
+
|
76
|
+
context.response.set_resource( { 'create' => TestEchoImplementation.to_h( context ) } )
|
77
|
+
end
|
78
|
+
|
79
|
+
def update( context )
|
80
|
+
|
81
|
+
# Deliberate error generation hook.
|
82
|
+
#
|
83
|
+
if context.request.ident == 'return_error'
|
84
|
+
context.response.add_error( 'platform.malformed' )
|
85
|
+
return
|
86
|
+
end
|
87
|
+
|
88
|
+
context.response.add_header( 'X-Example-Header', 'example' )
|
89
|
+
context.response.body = { 'update' => TestEchoImplementation.to_h( context ) }
|
90
|
+
end
|
91
|
+
|
92
|
+
def delete( context )
|
93
|
+
return context.response.not_found( context.request.ident ) if context.request.ident == 'simulate_404'
|
94
|
+
context.response.body = { 'delete' => TestEchoImplementation.to_h( context ) }
|
95
|
+
end
|
96
|
+
|
97
|
+
# Used by both this class and later code in this file.
|
98
|
+
#
|
99
|
+
def self.to_h( context )
|
100
|
+
{
|
101
|
+
'locale' => context.request.locale,
|
102
|
+
'dated_at' => context.request.dated_at.to_s,
|
103
|
+
'dated_from' => context.request.dated_from.to_s,
|
104
|
+
'deja_vu' => context.request.deja_vu.to_s,
|
105
|
+
'resource_uuid' => context.request.resource_uuid.to_s,
|
106
|
+
'body' => context.request.body,
|
107
|
+
'uri_path_components' => context.request.uri_path_components,
|
108
|
+
'uri_path_extension' => context.request.uri_path_extension,
|
109
|
+
'list_offset' => context.request.list.offset,
|
110
|
+
'list_limit' => context.request.list.limit,
|
111
|
+
'list_sort_data' => context.request.list.sort_data,
|
112
|
+
'list_search_data' => context.request.list.search_data,
|
113
|
+
'list_filter_data' => context.request.list.filter_data,
|
114
|
+
'embeds' => context.request.embeds,
|
115
|
+
'references' => context.request.references
|
116
|
+
}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class TestEchoInterface < Hoodoo::Services::Interface
|
121
|
+
interface :TestEcho do
|
122
|
+
version 2
|
123
|
+
endpoint :test_some_echoes, TestEchoImplementation
|
124
|
+
|
125
|
+
embeds :embed_one, :embed_two
|
126
|
+
|
127
|
+
to_list do
|
128
|
+
search :search_one, :search_two
|
129
|
+
filter :filter_one, :filter_two
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class TestEchoQuietImplementation < Hoodoo::Services::Implementation
|
135
|
+
|
136
|
+
public
|
137
|
+
|
138
|
+
def show( context )
|
139
|
+
context.response.body = { 'show' => TestEchoImplementation.to_h( context ) }
|
140
|
+
end
|
141
|
+
|
142
|
+
def list( context )
|
143
|
+
expectable_hook( context )
|
144
|
+
context.response.set_resources( [ context.request.headers ], 1 )
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def expectable_hook( context )
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class TestEchoQuietInterface < Hoodoo::Services::Interface
|
154
|
+
interface :TestEchoQuiet do
|
155
|
+
endpoint :test_echo_quiet, TestEchoQuietImplementation
|
156
|
+
actions :show, :list
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
class TestEchoService < Hoodoo::Services::Service
|
161
|
+
comprised_of TestEchoInterface, TestEchoQuietInterface
|
162
|
+
end
|
163
|
+
|
164
|
+
# Now the calling service that'll call over to the echo services above.
|
165
|
+
|
166
|
+
class TestCallImplementation < Hoodoo::Services::Implementation
|
167
|
+
def list( context )
|
168
|
+
resource = context.resource( :TestEcho, 2 )
|
169
|
+
result = resource.list(
|
170
|
+
{
|
171
|
+
'offset' => context.request.list.offset,
|
172
|
+
'limit' => context.request.list.limit,
|
173
|
+
'sort' => context.request.list.sort_data.keys.first,
|
174
|
+
'direction' => context.request.list.sort_data.values.first,
|
175
|
+
'search' => context.request.list.search_data,
|
176
|
+
'filter' => context.request.list.filter_data,
|
177
|
+
'_embed' => context.request.embeds,
|
178
|
+
'_reference' => context.request.references
|
179
|
+
}
|
180
|
+
)
|
181
|
+
|
182
|
+
expectable_result_hook( result )
|
183
|
+
|
184
|
+
return if result.adds_errors_to?( context.response.errors )
|
185
|
+
|
186
|
+
context.response.set_resources(
|
187
|
+
[
|
188
|
+
{ 'listA' => result },
|
189
|
+
{ 'listB' => result },
|
190
|
+
{ 'listC' => result },
|
191
|
+
{ 'options' => result.response_options }
|
192
|
+
],
|
193
|
+
( result.dataset_size || 0 ) + 2
|
194
|
+
)
|
195
|
+
end
|
196
|
+
|
197
|
+
def show( context )
|
198
|
+
|
199
|
+
# (Exercise 'uri_path_components' array vs 'ident' in passing).
|
200
|
+
|
201
|
+
repeat = context.request.ident == 'ensure_repeated_use_works' ? 10 : 1
|
202
|
+
|
203
|
+
1.upto( repeat ) do
|
204
|
+
|
205
|
+
resource = if ( context.request.uri_path_components[ 0 ] == 'generate_404' )
|
206
|
+
context.resource( :NotFound, 42 )
|
207
|
+
else
|
208
|
+
context.resource( :TestEcho, 2 )
|
209
|
+
end
|
210
|
+
|
211
|
+
result = resource.show(
|
212
|
+
context.request.uri_path_components.join( ',' ),
|
213
|
+
{
|
214
|
+
'_embed' => context.request.embeds,
|
215
|
+
'_reference' => context.request.references
|
216
|
+
}
|
217
|
+
)
|
218
|
+
|
219
|
+
expectable_result_hook( result )
|
220
|
+
|
221
|
+
context.response.add_errors( result.platform_errors )
|
222
|
+
context.response.body = { 'show' => result, 'options' => result.response_options }
|
223
|
+
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def create( context )
|
228
|
+
endpoint = context.resource( :TestEcho, 2, dated_from: context.request.dated_from )
|
229
|
+
|
230
|
+
if context.request.body[ 'foo' ] == 'specify_uuid'
|
231
|
+
|
232
|
+
# If given the magic string "specify_uuid" in the mandatory resource
|
233
|
+
# text field "foo", then specifically make an inter-resource call with
|
234
|
+
# a resource UUID specified for the inner resource. This checks the
|
235
|
+
# permissions handling on "internal" inter-resource calls.
|
236
|
+
#
|
237
|
+
# This is for *inter-resource* calls *from* this resource to the target
|
238
|
+
# resource and has nothing to do with anything the top-level API caller
|
239
|
+
# specified.
|
240
|
+
#
|
241
|
+
endpoint.resource_uuid = Hoodoo::UUID.generate()
|
242
|
+
|
243
|
+
elsif context.request.body.has_key?( 'deja_vu_in_other_resource' )
|
244
|
+
|
245
|
+
# This tests an inter-resource call specifying deja-vu and dealing with
|
246
|
+
# responses.
|
247
|
+
#
|
248
|
+
endpoint.deja_vu = true
|
249
|
+
|
250
|
+
end
|
251
|
+
|
252
|
+
result = endpoint.create(
|
253
|
+
context.request.body,
|
254
|
+
{
|
255
|
+
'_embed' => context.request.embeds,
|
256
|
+
'_reference' => context.request.references
|
257
|
+
}
|
258
|
+
)
|
259
|
+
|
260
|
+
expectable_result_hook( result )
|
261
|
+
|
262
|
+
context.response.add_errors( result.platform_errors )
|
263
|
+
context.response.body = { 'create' => result, 'options' => result.response_options }
|
264
|
+
end
|
265
|
+
|
266
|
+
def update( context )
|
267
|
+
resource = context.resource( :TestEcho, 2 )
|
268
|
+
result = resource.update(
|
269
|
+
context.request.uri_path_components.join( ',' ),
|
270
|
+
context.request.body,
|
271
|
+
{
|
272
|
+
'_embed' => context.request.embeds,
|
273
|
+
'_reference' => context.request.references
|
274
|
+
}
|
275
|
+
)
|
276
|
+
|
277
|
+
expectable_result_hook( result )
|
278
|
+
|
279
|
+
context.response.add_errors( result.platform_errors )
|
280
|
+
context.response.body = { 'update' => result, 'options' => result.response_options }
|
281
|
+
end
|
282
|
+
|
283
|
+
def delete( context )
|
284
|
+
resource = context.resource( :TestEcho, 2 )
|
285
|
+
result = resource.delete(
|
286
|
+
context.request.uri_path_components.join( ',' ),
|
287
|
+
{
|
288
|
+
'_embed' => context.request.embeds,
|
289
|
+
'_reference' => context.request.references
|
290
|
+
}
|
291
|
+
)
|
292
|
+
|
293
|
+
expectable_result_hook( result )
|
294
|
+
|
295
|
+
context.response.add_errors( result.platform_errors )
|
296
|
+
context.response.body = { 'delete' => result, 'options' => result.response_options }
|
297
|
+
end
|
298
|
+
|
299
|
+
# ...So we can expect any instance of this class to receive this message
|
300
|
+
# and check on the data it was given.
|
301
|
+
#
|
302
|
+
def expectable_result_hook( result )
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
class TestCallInterface < Hoodoo::Services::Interface
|
307
|
+
interface :TestCall do
|
308
|
+
endpoint :test_call, TestCallImplementation
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
class TestCallService < Hoodoo::Services::Service
|
313
|
+
comprised_of TestCallInterface
|
314
|
+
end
|
315
|
+
|
316
|
+
# A helper method for a set of common expectations.
|
317
|
+
|
318
|
+
def expect_response_headers_on( response, should_not_have_called_service = false )
|
319
|
+
expect( response[ 'X-Interaction-ID' ] ).to_not be_nil
|
320
|
+
expect( Hoodoo::UUID.valid?( response[ 'X-Interaction-ID' ] ) ).to eq( true )
|
321
|
+
|
322
|
+
if should_not_have_called_service
|
323
|
+
expect( response[ 'X-Service-Response-Time' ] ).to be_nil
|
324
|
+
else
|
325
|
+
expect( response[ 'X-Service-Response-Time' ] ).to_not be_nil
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
# And Finally - the tests.
|
330
|
+
|
331
|
+
describe 'DRb start timeout' do
|
332
|
+
context 'for test coverage purposes' do
|
333
|
+
it 'checks for timeouts' do
|
334
|
+
expect( DRbObject ).to receive( :new_with_uri ).once.and_raise( DRb::DRbConnError )
|
335
|
+
expect( DRbObject ).to receive( :new_with_uri ).at_least( :once ).and_raise( Timeout::Error )
|
336
|
+
|
337
|
+
spec_helper_http(
|
338
|
+
port: spec_helper_start_svc_app_in_thread_for( TestEchoService ),
|
339
|
+
path: '/v2/test_some_echoes'
|
340
|
+
)
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
describe Hoodoo::Services::Middleware do
|
346
|
+
|
347
|
+
before :each do
|
348
|
+
@test_uuid = Hoodoo::UUID.generate()
|
349
|
+
@old_test_session = Hoodoo::Services::Middleware.test_session()
|
350
|
+
@test_session = @old_test_session.dup
|
351
|
+
permissions = Hoodoo::Services::Permissions.new # (this is "default-else-deny")
|
352
|
+
permissions.set_default_fallback( Hoodoo::Services::Permissions::ALLOW )
|
353
|
+
@test_session.permissions = permissions
|
354
|
+
@test_session.scoping = @test_session.scoping.dup
|
355
|
+
@test_session.scoping.authorised_http_headers = [] # (no secured headers allowed to start with)
|
356
|
+
Hoodoo::Services::Middleware.set_test_session( @test_session )
|
357
|
+
end
|
358
|
+
|
359
|
+
after :each do
|
360
|
+
Hoodoo::Services::Middleware.set_test_session( @old_test_session )
|
361
|
+
end
|
362
|
+
|
363
|
+
before :all do
|
364
|
+
@port = spec_helper_start_svc_app_in_thread_for( TestEchoService )
|
365
|
+
end
|
366
|
+
|
367
|
+
# Although tests can run in random order so we can't force this set to come
|
368
|
+
# first, at least having this set present validates all of the HTTP behaviour
|
369
|
+
# we expect to work in the echo service. If these tests fail, all bets are
|
370
|
+
# off for anything else tested here.
|
371
|
+
#
|
372
|
+
context 'with in-thread HTTP service' do
|
373
|
+
|
374
|
+
before :example, :check_callbacks => true do
|
375
|
+
expect_any_instance_of( TestEchoImplementation ).to receive( :before ).once
|
376
|
+
expect_any_instance_of( TestEchoImplementation ).to receive( :after ).once
|
377
|
+
end
|
378
|
+
|
379
|
+
before :example, :check_quiet_callbacks => true do
|
380
|
+
expect_any_instance_of( TestEchoQuietImplementation ).to receive( :before ).once
|
381
|
+
expect_any_instance_of( TestEchoQuietImplementation ).to receive( :after ).once
|
382
|
+
end
|
383
|
+
|
384
|
+
def list_things( locale: nil, dated_at: nil, deja_vu: nil )
|
385
|
+
headers = {}
|
386
|
+
headers[ 'Accept-Language' ] = locale unless locale.nil?
|
387
|
+
headers[ 'X-Dated-At' ] = Hoodoo::Utilities.nanosecond_iso8601( dated_at ) unless dated_at.nil?
|
388
|
+
headers[ 'X-Deja-Vu' ] = 'yes' if deja_vu == true
|
389
|
+
|
390
|
+
response = spec_helper_http(
|
391
|
+
port: @port,
|
392
|
+
path: '/v2/test_some_echoes.tar.gz?limit=25&offset=75&_reference=embed_one,embed_two',
|
393
|
+
headers: headers
|
394
|
+
)
|
395
|
+
|
396
|
+
expect( response.code ).to eq( '200' )
|
397
|
+
expect_response_headers_on( response )
|
398
|
+
|
399
|
+
parsed = JSON.parse( response.body )
|
400
|
+
|
401
|
+
expect( parsed[ '_data' ]).to_not be_nil
|
402
|
+
expect( parsed[ '_data' ][ 0 ]).to_not be_nil
|
403
|
+
expect( parsed[ '_data' ][ 0 ][ 'list0' ] ).to eq(
|
404
|
+
{
|
405
|
+
'locale' => locale.nil? ? 'en-nz' : locale,
|
406
|
+
'dated_at' => dated_at.to_s,
|
407
|
+
'dated_from' => '',
|
408
|
+
'deja_vu' => deja_vu.to_s,
|
409
|
+
'resource_uuid' => '',
|
410
|
+
'body' => nil,
|
411
|
+
'uri_path_components' => [],
|
412
|
+
'uri_path_extension' => 'tar.gz',
|
413
|
+
'list_offset' => 75,
|
414
|
+
'list_limit' => 25,
|
415
|
+
'list_sort_data' => { 'created_at' => 'desc' },
|
416
|
+
'list_search_data' => {},
|
417
|
+
'list_filter_data' => {},
|
418
|
+
'embeds' => [],
|
419
|
+
'references' => [ 'embed_one', 'embed_two' ]
|
420
|
+
}
|
421
|
+
)
|
422
|
+
expect( parsed[ '_dataset_size' ] ).to eq( 49 )
|
423
|
+
end
|
424
|
+
|
425
|
+
it 'lists things with callbacks', :check_callbacks => true do
|
426
|
+
list_things()
|
427
|
+
end
|
428
|
+
|
429
|
+
it 'list things without callbacks' do
|
430
|
+
list_things()
|
431
|
+
end
|
432
|
+
|
433
|
+
it 'lists things with a custom locale and dated-at time' do
|
434
|
+
list_things( locale: 'foo', dated_at: DateTime.now )
|
435
|
+
end
|
436
|
+
|
437
|
+
it 'should be able to list quiet things too, reporting HTTP headers', :check_quiet_callbacks => true do
|
438
|
+
|
439
|
+
# Did this test give you a 500? Chances are it's the "raise" below,
|
440
|
+
# but checking 'test.log' or adding "puts response.body.inspect" a
|
441
|
+
# bit further down will let you know for sure.
|
442
|
+
|
443
|
+
expect_any_instance_of( TestEchoQuietImplementation ).to receive( :expectable_hook ) { | ignored, context |
|
444
|
+
raise 'Test failed as context.request.headers not frozen' unless context.request.headers.frozen?
|
445
|
+
}
|
446
|
+
|
447
|
+
response = spec_helper_http(
|
448
|
+
port: @port,
|
449
|
+
path: '/v1/test_echo_quiet'
|
450
|
+
)
|
451
|
+
|
452
|
+
expect( response.code ).to eq( '200' )
|
453
|
+
expect_response_headers_on( response )
|
454
|
+
|
455
|
+
parsed = JSON.parse( response.body )
|
456
|
+
|
457
|
+
expect( parsed[ '_data' ] ).to eq( [ {
|
458
|
+
'CONTENT_TYPE' => 'application/json; charset=utf-8',
|
459
|
+
'HTTP_CONNECTION' => 'close',
|
460
|
+
'HTTP_VERSION' => 'HTTP/1.1',
|
461
|
+
'HTTP_HOST' => "127.0.0.1:#{ @port }"
|
462
|
+
} ] )
|
463
|
+
end
|
464
|
+
|
465
|
+
# Code coverage: At present the middleware always calls its "remove
|
466
|
+
# expected errors" method if deja-vu is on and there are errors in the
|
467
|
+
# response. This method then filters based on action, with an early
|
468
|
+
# exit for non-create, non-delete cases. We're testing that here.
|
469
|
+
#
|
470
|
+
it 'lists things without worrying about deja-vu' do
|
471
|
+
expect_any_instance_of( TestEchoImplementation ).to receive( :list ).once do | ignored_rspec_mock_instance, context |
|
472
|
+
context.response.not_found( 'some_error' )
|
473
|
+
end
|
474
|
+
|
475
|
+
expect_any_instance_of( Hoodoo::Services::Middleware ).to receive( :remove_expected_errors_when_experiencing_deja_vu ).once.and_call_original
|
476
|
+
|
477
|
+
response = spec_helper_http(
|
478
|
+
port: @port,
|
479
|
+
path: '/v2/test_some_echoes',
|
480
|
+
headers: { 'X-Deja-Vu' => 'yes' }
|
481
|
+
)
|
482
|
+
|
483
|
+
expect( response.code ).to eq( '404' )
|
484
|
+
expect_response_headers_on( response )
|
485
|
+
end
|
486
|
+
|
487
|
+
def show_things( locale: nil, dated_at: nil )
|
488
|
+
headers = {}
|
489
|
+
headers[ 'Accept-Language' ] = locale unless locale.nil?
|
490
|
+
headers[ 'X-Dated-At' ] = Hoodoo::Utilities.nanosecond_iso8601( dated_at ) unless dated_at.nil?
|
491
|
+
|
492
|
+
response = spec_helper_http(
|
493
|
+
port: @port,
|
494
|
+
path: '/v2/test_some_echoes/one/two.tar.gz?_reference=embed_one,embed_two',
|
495
|
+
headers: headers
|
496
|
+
)
|
497
|
+
|
498
|
+
expect( response.code ).to eq( '200' )
|
499
|
+
expect_response_headers_on( response )
|
500
|
+
|
501
|
+
parsed = JSON.parse( response.body )
|
502
|
+
|
503
|
+
expect( parsed[ 'show' ] ).to_not be_nil
|
504
|
+
expect( parsed[ 'show' ] ).to eq(
|
505
|
+
{
|
506
|
+
'locale' => locale.nil? ? 'en-nz' : locale,
|
507
|
+
'dated_at' => dated_at.to_s,
|
508
|
+
'dated_from' => '',
|
509
|
+
'deja_vu' => '',
|
510
|
+
'resource_uuid' => '',
|
511
|
+
'body' => nil,
|
512
|
+
'uri_path_components' => [ 'one', 'two' ],
|
513
|
+
'uri_path_extension' => 'tar.gz',
|
514
|
+
'list_offset' => 0,
|
515
|
+
'list_limit' => 50,
|
516
|
+
'list_sort_data' => { 'created_at' => 'desc' },
|
517
|
+
'list_search_data' => {},
|
518
|
+
'list_filter_data' => {},
|
519
|
+
'embeds' => [],
|
520
|
+
'references' => [ 'embed_one', 'embed_two' ]
|
521
|
+
}
|
522
|
+
)
|
523
|
+
end
|
524
|
+
|
525
|
+
it 'shows_things_with_callbacks', :check_callbacks => true do
|
526
|
+
show_things( locale: 'fr' )
|
527
|
+
end
|
528
|
+
|
529
|
+
it 'shows_things_without_callbacks' do
|
530
|
+
show_things()
|
531
|
+
end
|
532
|
+
|
533
|
+
it 'shows things with a custom locale and dated-at time' do
|
534
|
+
show_things( locale: 'bar', dated_at: DateTime.now )
|
535
|
+
end
|
536
|
+
|
537
|
+
it 'should be able to show quiet things too', :check_quiet_callbacks => true do
|
538
|
+
|
539
|
+
response = spec_helper_http(
|
540
|
+
port: @port,
|
541
|
+
path: '/v1/test_echo_quiet/some_uuid'
|
542
|
+
)
|
543
|
+
|
544
|
+
expect( response.code ).to eq( '200' )
|
545
|
+
expect_response_headers_on( response )
|
546
|
+
|
547
|
+
parsed = JSON.parse( response.body )
|
548
|
+
|
549
|
+
expect( parsed[ 'show' ] ).to_not be_nil
|
550
|
+
expect( parsed[ 'show' ] ).to eq(
|
551
|
+
{
|
552
|
+
'locale' => 'en-nz',
|
553
|
+
'dated_at' => '',
|
554
|
+
'dated_from' => '',
|
555
|
+
'deja_vu' => '',
|
556
|
+
'resource_uuid' => '',
|
557
|
+
'body' => nil,
|
558
|
+
'uri_path_components' => [ 'some_uuid' ],
|
559
|
+
'uri_path_extension' => '',
|
560
|
+
'list_offset' => 0,
|
561
|
+
'list_limit' => 50,
|
562
|
+
'list_sort_data' => { 'created_at' => 'desc' },
|
563
|
+
'list_search_data' => {},
|
564
|
+
'list_filter_data' => {},
|
565
|
+
'embeds' => [],
|
566
|
+
'references' => []
|
567
|
+
}
|
568
|
+
)
|
569
|
+
end
|
570
|
+
|
571
|
+
def create_things( locale: nil, dated_from: nil, deja_vu: nil, resource_uuid: nil )
|
572
|
+
headers = {}
|
573
|
+
headers[ 'Accept-Language' ] = locale unless locale.nil?
|
574
|
+
headers[ 'X-Resource-UUID' ] = resource_uuid unless resource_uuid.nil?
|
575
|
+
headers[ 'X-Dated-From' ] = Hoodoo::Utilities.nanosecond_iso8601( dated_from ) unless dated_from.nil?
|
576
|
+
headers[ 'X-Deja-Vu' ] = 'yes' if deja_vu == true
|
577
|
+
|
578
|
+
response = spec_helper_http(
|
579
|
+
klass: Net::HTTP::Post,
|
580
|
+
port: @port,
|
581
|
+
path: '/v2/test_some_echoes.json?_embed=embed_one,embed_two',
|
582
|
+
body: { 'foo' => 'bar', 'baz' => 'boo' }.to_json,
|
583
|
+
headers: headers
|
584
|
+
)
|
585
|
+
|
586
|
+
if deja_vu
|
587
|
+
expect( response.code ).to eq( '204' )
|
588
|
+
expect( response.body ).to be_nil
|
589
|
+
expect_response_headers_on( response )
|
590
|
+
|
591
|
+
else
|
592
|
+
expect( response.code ).to eq( '200' )
|
593
|
+
expect_response_headers_on( response )
|
594
|
+
|
595
|
+
parsed = JSON.parse( response.body )
|
596
|
+
|
597
|
+
expected_body = { 'foo' => 'bar', 'baz' => 'boo' }
|
598
|
+
expected_body[ 'id' ] = resource_uuid unless resource_uuid.nil?
|
599
|
+
|
600
|
+
expect( parsed[ 'create' ] ).to_not be_nil
|
601
|
+
expect( parsed[ 'create' ] ).to eq(
|
602
|
+
{
|
603
|
+
'locale' => locale.nil? ? 'en-nz' : locale,
|
604
|
+
'dated_at' => '',
|
605
|
+
'dated_from' => dated_from.to_s,
|
606
|
+
'deja_vu' => deja_vu.to_s,
|
607
|
+
'resource_uuid' => resource_uuid.to_s,
|
608
|
+
'body' => expected_body,
|
609
|
+
'uri_path_components' => [],
|
610
|
+
'uri_path_extension' => 'json',
|
611
|
+
'list_offset' => 0,
|
612
|
+
'list_limit' => 50,
|
613
|
+
'list_sort_data' => { 'created_at' => 'desc' },
|
614
|
+
'list_search_data' => {},
|
615
|
+
'list_filter_data' => {},
|
616
|
+
'embeds' => [ 'embed_one', 'embed_two' ],
|
617
|
+
'references' => []
|
618
|
+
}
|
619
|
+
)
|
620
|
+
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
it 'creates things with callbacks', :check_callbacks => true do
|
625
|
+
create_things()
|
626
|
+
end
|
627
|
+
|
628
|
+
it 'creates things without callbacks' do
|
629
|
+
create_things()
|
630
|
+
end
|
631
|
+
|
632
|
+
it 'creates things with a custom locale, dated_from and deja_vu' do
|
633
|
+
create_things( locale: 'baz', dated_from: DateTime.now, deja_vu: true )
|
634
|
+
end
|
635
|
+
|
636
|
+
# Extra test coverage for this is present in middleware_create_update_spec.rb.
|
637
|
+
#
|
638
|
+
it 'creates things with a custom UUID given permission' do
|
639
|
+
@test_session.scoping.authorised_http_headers = [ 'HTTP_X_RESOURCE_UUID' ]
|
640
|
+
create_things(resource_uuid: Hoodoo::UUID.generate())
|
641
|
+
end
|
642
|
+
|
643
|
+
# Extra test coverage for this is present in middleware_create_update_spec.rb.
|
644
|
+
#
|
645
|
+
it 'fails to create things with a custom UUID if not given permission' do
|
646
|
+
response = spec_helper_http(
|
647
|
+
klass: Net::HTTP::Post,
|
648
|
+
port: @port,
|
649
|
+
path: '/v2/test_some_echoes.json',
|
650
|
+
body: { 'foo' => 'bar', 'baz' => 'boo' }.to_json,
|
651
|
+
headers: { 'X-Resource-UUID' => Hoodoo::UUID.generate() }
|
652
|
+
)
|
653
|
+
|
654
|
+
expect( response.code ).to eq( '403' )
|
655
|
+
expect_response_headers_on( response, true ) # true => shouldn't have called service
|
656
|
+
|
657
|
+
parsed = JSON.parse( response.body )
|
658
|
+
|
659
|
+
expect( parsed[ 'errors' ].size ).to eq( 1 )
|
660
|
+
expect( parsed[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'platform.forbidden' )
|
661
|
+
expect( parsed[ 'errors' ][ 0 ][ 'reference' ] ).to be_nil # Ensure no information disclosure vulnerability
|
662
|
+
end
|
663
|
+
|
664
|
+
it 'gets a 204 with deja-vu and duplication' do
|
665
|
+
response = spec_helper_http(
|
666
|
+
klass: Net::HTTP::Post,
|
667
|
+
port: @port,
|
668
|
+
path: '/v2/test_some_echoes',
|
669
|
+
body: { 'foo' => 'bar' }.to_json,
|
670
|
+
headers: { 'X-Deja-Vu' => 'yes' }
|
671
|
+
)
|
672
|
+
|
673
|
+
expect( response.code ).to eq( '204' )
|
674
|
+
expect( response.body ).to be_nil
|
675
|
+
|
676
|
+
expect_response_headers_on( response )
|
677
|
+
expect( response[ 'X-Deja-Vu' ] ).to eq( 'confirmed' )
|
678
|
+
end
|
679
|
+
|
680
|
+
it 'gets non-204 with deja-vu and duplication plus other errors' do
|
681
|
+
response = spec_helper_http(
|
682
|
+
klass: Net::HTTP::Post,
|
683
|
+
port: @port,
|
684
|
+
path: '/v2/test_some_echoes',
|
685
|
+
body: { 'foo' => 'bar', 'additional_error' => 'generic.invalid_decimal' }.to_json,
|
686
|
+
headers: { 'X-Deja-Vu' => 'yes' }
|
687
|
+
)
|
688
|
+
|
689
|
+
expect( response.code ).to eq( '422' )
|
690
|
+
expect_response_headers_on( response )
|
691
|
+
|
692
|
+
parsed = JSON.parse( response.body )
|
693
|
+
|
694
|
+
expect( parsed[ 'errors' ].size ).to eq( 2 )
|
695
|
+
expect( parsed[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'generic.invalid_duplication' )
|
696
|
+
expect( parsed[ 'errors' ][ 0 ][ 'reference' ] ).to eq( 'deja_vu' )
|
697
|
+
expect( parsed[ 'errors' ][ 1 ][ 'code' ] ).to eq( 'generic.invalid_decimal' )
|
698
|
+
expect( parsed[ 'errors' ][ 1 ][ 'reference' ] ).to eq( 'deja_vu' )
|
699
|
+
end
|
700
|
+
|
701
|
+
def update_things( locale: nil )
|
702
|
+
headers = {}
|
703
|
+
headers[ 'Accept-Language' ] = locale unless locale.nil?
|
704
|
+
|
705
|
+
response = spec_helper_http(
|
706
|
+
klass: Net::HTTP::Patch,
|
707
|
+
port: @port,
|
708
|
+
path: '/v2/test_some_echoes/a/b.json?_embed=embed_one',
|
709
|
+
body: { 'foo' => 'boo', 'baz' => 'bar' }.to_json,
|
710
|
+
headers: headers
|
711
|
+
)
|
712
|
+
|
713
|
+
expect( response.code ).to eq( '200' )
|
714
|
+
expect_response_headers_on( response )
|
715
|
+
|
716
|
+
parsed = JSON.parse( response.body )
|
717
|
+
|
718
|
+
expect( parsed[ 'update' ] ).to_not be_nil
|
719
|
+
expect( parsed[ 'update' ] ).to eq(
|
720
|
+
{
|
721
|
+
'locale' => locale.nil? ? 'en-nz' : locale,
|
722
|
+
'dated_at' => '',
|
723
|
+
'dated_from' => '',
|
724
|
+
'deja_vu' => '',
|
725
|
+
'resource_uuid' => '',
|
726
|
+
'body' => { 'foo' => 'boo', 'baz' => 'bar' },
|
727
|
+
'uri_path_components' => [ 'a', 'b' ],
|
728
|
+
'uri_path_extension' => 'json',
|
729
|
+
'list_offset' => 0,
|
730
|
+
'list_limit' => 50,
|
731
|
+
'list_sort_data' => { 'created_at' => 'desc' },
|
732
|
+
'list_search_data' => {},
|
733
|
+
'list_filter_data' => {},
|
734
|
+
'embeds' => [ 'embed_one' ],
|
735
|
+
'references' => []
|
736
|
+
}
|
737
|
+
)
|
738
|
+
end
|
739
|
+
|
740
|
+
it 'updates things with callbacks', :check_callbacks => true do
|
741
|
+
update_things()
|
742
|
+
end
|
743
|
+
|
744
|
+
it 'updates things without callbacks' do
|
745
|
+
update_things()
|
746
|
+
end
|
747
|
+
|
748
|
+
it 'updates things with a custom locale' do
|
749
|
+
update_things( locale: 'boo' )
|
750
|
+
end
|
751
|
+
|
752
|
+
def delete_things( locale: nil, deja_vu: nil )
|
753
|
+
headers = {}
|
754
|
+
headers[ 'Accept-Language' ] = locale unless locale.nil?
|
755
|
+
headers[ 'X-Deja-Vu' ] = 'yes' if deja_vu == true
|
756
|
+
|
757
|
+
response = spec_helper_http(
|
758
|
+
klass: Net::HTTP::Delete,
|
759
|
+
port: @port,
|
760
|
+
path: '/v2/test_some_echoes/aa/bb.xml.gz?_embed=embed_two',
|
761
|
+
headers: headers
|
762
|
+
)
|
763
|
+
|
764
|
+
expect( response.code ).to eq( '200' )
|
765
|
+
expect_response_headers_on( response )
|
766
|
+
|
767
|
+
parsed = JSON.parse( response.body )
|
768
|
+
|
769
|
+
expect( parsed[ 'delete' ] ).to_not be_nil
|
770
|
+
expect( parsed[ 'delete' ] ).to eq(
|
771
|
+
{
|
772
|
+
'locale' => locale.nil? ? 'en-nz' : locale,
|
773
|
+
'dated_at' => '',
|
774
|
+
'dated_from' => '',
|
775
|
+
'deja_vu' => deja_vu.to_s,
|
776
|
+
'resource_uuid' => '',
|
777
|
+
'body' => nil,
|
778
|
+
'uri_path_components' => [ 'aa', 'bb' ],
|
779
|
+
'uri_path_extension' => 'xml.gz',
|
780
|
+
'list_offset' => 0,
|
781
|
+
'list_limit' => 50,
|
782
|
+
'list_sort_data' => { 'created_at' => 'desc' },
|
783
|
+
'list_search_data' => {},
|
784
|
+
'list_filter_data' => {},
|
785
|
+
'embeds' => [ 'embed_two' ],
|
786
|
+
'references' => []
|
787
|
+
}
|
788
|
+
)
|
789
|
+
end
|
790
|
+
|
791
|
+
it 'deletes things with callbacks', :check_callbacks => true do
|
792
|
+
delete_things()
|
793
|
+
end
|
794
|
+
|
795
|
+
it 'deletes things without callbacks' do
|
796
|
+
delete_things()
|
797
|
+
end
|
798
|
+
|
799
|
+
it 'deletes things, passing through custom locale and deja_vu' do
|
800
|
+
delete_things( locale: 'bye', deja_vu: true )
|
801
|
+
end
|
802
|
+
|
803
|
+
it 'deletes things with a simulated 404' do
|
804
|
+
response = spec_helper_http(
|
805
|
+
klass: Net::HTTP::Delete,
|
806
|
+
port: @port,
|
807
|
+
path: '/v2/test_some_echoes/simulate_404' )
|
808
|
+
|
809
|
+
expect( response.code ).to eq( '404' )
|
810
|
+
expect_response_headers_on( response )
|
811
|
+
|
812
|
+
parsed = JSON.parse( response.body )
|
813
|
+
|
814
|
+
expect( parsed[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'generic.not_found' )
|
815
|
+
end
|
816
|
+
|
817
|
+
it 'deletes things with a 204 with deja vu' do
|
818
|
+
response = spec_helper_http(
|
819
|
+
klass: Net::HTTP::Delete,
|
820
|
+
port: @port,
|
821
|
+
path: '/v2/test_some_echoes/simulate_404',
|
822
|
+
headers: { 'X-Deja-Vu' => 'yes' }
|
823
|
+
)
|
824
|
+
|
825
|
+
expect( response.code ).to eq( '204' )
|
826
|
+
expect( response.body ).to be_nil
|
827
|
+
expect( response[ 'X-Deja-Vu' ] ).to eq( 'confirmed' )
|
828
|
+
expect_response_headers_on( response )
|
829
|
+
end
|
830
|
+
|
831
|
+
it 'should get 405 for bad requests' do
|
832
|
+
|
833
|
+
# Attempt a #create (POST with body data) - service only does "show"
|
834
|
+
# and "list".
|
835
|
+
|
836
|
+
response = spec_helper_http(
|
837
|
+
klass: Net::HTTP::Post,
|
838
|
+
port: @port,
|
839
|
+
path: '/v1/test_echo_quiet',
|
840
|
+
body: { 'foo' => 'bar', 'baz' => 'boo' }.to_json
|
841
|
+
)
|
842
|
+
|
843
|
+
expect( response.code ).to eq( '405' )
|
844
|
+
expect_response_headers_on( response, true ) # true => shouldn't have called service
|
845
|
+
end
|
846
|
+
|
847
|
+
it 'should be detect 404 OK' do
|
848
|
+
response = spec_helper_http(
|
849
|
+
port: @port,
|
850
|
+
path: '/v1/not_present'
|
851
|
+
)
|
852
|
+
|
853
|
+
expect( response.code ).to eq( '404' )
|
854
|
+
expect_response_headers_on( response, true ) # true => shouldn't have called service
|
855
|
+
end
|
856
|
+
end
|
857
|
+
|
858
|
+
#############################################################################
|
859
|
+
|
860
|
+
context 'remote inter-resource calls' do
|
861
|
+
def app
|
862
|
+
Rack::Builder.new do
|
863
|
+
use Hoodoo::Services::Middleware
|
864
|
+
run TestCallService.new
|
865
|
+
end
|
866
|
+
end
|
867
|
+
|
868
|
+
before :example, :check_callbacks => true do
|
869
|
+
expect_any_instance_of( TestCallImplementation ).to receive( :before ).once
|
870
|
+
expect_any_instance_of( TestEchoImplementation ).to receive( :before ).once
|
871
|
+
expect_any_instance_of( TestEchoImplementation ).to receive( :after ).once
|
872
|
+
expect_any_instance_of( TestCallImplementation ).to receive( :after ).once
|
873
|
+
end
|
874
|
+
|
875
|
+
def headers_for( locale: nil,
|
876
|
+
dated_at: nil,
|
877
|
+
dated_from: nil,
|
878
|
+
deja_vu: nil,
|
879
|
+
resource_uuid: nil )
|
880
|
+
|
881
|
+
headers = {
|
882
|
+
'HTTP_X_INTERACTION_ID' => @interaction_id,
|
883
|
+
'CONTENT_TYPE' => 'application/json; charset=utf-8'
|
884
|
+
}
|
885
|
+
|
886
|
+
headers[ 'HTTP_CONTENT_LANGUAGE' ] = locale unless locale.nil?
|
887
|
+
headers[ 'HTTP_ACCEPT_LANGUAGE' ] = locale unless locale.nil?
|
888
|
+
headers[ 'HTTP_X_RESOURCE_UUID' ] = resource_uuid unless resource_uuid.nil?
|
889
|
+
headers[ 'HTTP_X_DATED_AT' ] = Hoodoo::Utilities.nanosecond_iso8601( dated_at ) unless dated_at.nil?
|
890
|
+
headers[ 'HTTP_X_DATED_FROM' ] = Hoodoo::Utilities.nanosecond_iso8601( dated_from ) unless dated_from.nil?
|
891
|
+
headers[ 'HTTP_X_DEJA_VU' ] = 'yes' if deja_vu == true
|
892
|
+
|
893
|
+
return headers
|
894
|
+
end
|
895
|
+
|
896
|
+
# A helper method for a set of common expectations.
|
897
|
+
|
898
|
+
def expect_response_options_for( options )
|
899
|
+
expect( options ).to_not be_nil
|
900
|
+
expect( options[ 'service_response_time' ] ).to_not be_nil
|
901
|
+
expect( options[ 'interaction_id' ] ).to_not be_nil
|
902
|
+
expect( Hoodoo::UUID.valid?( options[ 'interaction_id' ] ) ).to eq( true )
|
903
|
+
end
|
904
|
+
|
905
|
+
def list_things( locale: nil, dated_at: nil )
|
906
|
+
get(
|
907
|
+
'/v1/test_call.tar.gz?limit=25&offset=75',
|
908
|
+
nil,
|
909
|
+
headers_for( locale: locale, dated_at: dated_at )
|
910
|
+
)
|
911
|
+
|
912
|
+
expect( last_response.status ).to eq( 200 )
|
913
|
+
parsed = JSON.parse( last_response.body )
|
914
|
+
|
915
|
+
# Outer calls wrap arrays in object with "_data" key for JSON (since we
|
916
|
+
# don't do JSON5 and only JSON5 allows outermost / top-level arrays), but
|
917
|
+
# the inter-resource calls unpack that for us, so we should see the outer
|
918
|
+
# service's "_data" array nesting directly the inner service's array if
|
919
|
+
# the middleware dereferenced it correctly for us.
|
920
|
+
|
921
|
+
expect( parsed[ '_data' ]).to_not be_nil
|
922
|
+
expect( parsed[ '_data' ][ 0 ]).to_not be_nil
|
923
|
+
expect( parsed[ '_data' ][ 0 ][ 'listA' ] ).to_not be_nil
|
924
|
+
expect( parsed[ '_data' ][ 0 ][ 'listA' ][ 0 ] ).to_not be_nil
|
925
|
+
expect( parsed[ '_data' ][ 0 ][ 'listA' ][ 0 ][ 'list0'] ).to eq(
|
926
|
+
{
|
927
|
+
'locale' => locale.nil? ? 'en-nz' : locale,
|
928
|
+
'dated_at' => dated_at.to_s,
|
929
|
+
'dated_from' => '',
|
930
|
+
'deja_vu' => '',
|
931
|
+
'resource_uuid' => '',
|
932
|
+
'body' => nil,
|
933
|
+
'uri_path_components' => [],
|
934
|
+
'uri_path_extension' => '',
|
935
|
+
'list_offset' => 75,
|
936
|
+
'list_limit' => 25,
|
937
|
+
'list_sort_data' => { 'created_at' => 'desc' },
|
938
|
+
'list_search_data' => {},
|
939
|
+
'list_filter_data' => {},
|
940
|
+
'embeds' => [],
|
941
|
+
'references' => []
|
942
|
+
}
|
943
|
+
)
|
944
|
+
|
945
|
+
expect( parsed[ '_dataset_size' ]).to eq( 51 )
|
946
|
+
|
947
|
+
expect( parsed[ '_data' ][ 3 ] ).to_not be_nil
|
948
|
+
expect_response_options_for( parsed[ '_data' ][ 3 ][ 'options' ] )
|
949
|
+
end
|
950
|
+
|
951
|
+
it 'list things in the remote service with callbacks', :check_callbacks => true do
|
952
|
+
list_things()
|
953
|
+
end
|
954
|
+
|
955
|
+
it 'list things in the remote service without callbacks' do
|
956
|
+
list_things()
|
957
|
+
end
|
958
|
+
|
959
|
+
it 'lists things with a custom locale and dated-at time' do
|
960
|
+
list_things( locale: 'foo', dated_at: DateTime.now )
|
961
|
+
end
|
962
|
+
|
963
|
+
it 'complains if the JSON implementation is not up to scratch' do
|
964
|
+
module JSON
|
965
|
+
class << self
|
966
|
+
def dumb_parse( data, ignored )
|
967
|
+
JSON.original_parse( data )
|
968
|
+
end
|
969
|
+
|
970
|
+
alias original_parse parse
|
971
|
+
alias parse dumb_parse
|
972
|
+
end
|
973
|
+
end
|
974
|
+
|
975
|
+
get(
|
976
|
+
'/v1/test_call.tar.gz?limit=25&offset=75',
|
977
|
+
nil,
|
978
|
+
headers_for( locale: 'de' )
|
979
|
+
)
|
980
|
+
|
981
|
+
module JSON
|
982
|
+
class << self
|
983
|
+
alias parse original_parse
|
984
|
+
end
|
985
|
+
end
|
986
|
+
|
987
|
+
expect( last_response.status ).to eq( 500 )
|
988
|
+
expect_response_headers_on( last_response, true ) # true => shouldn't have called service
|
989
|
+
|
990
|
+
parsed = JSON.parse( last_response.body )
|
991
|
+
|
992
|
+
expect( parsed[ 'errors' ][ 0 ][ 'message' ] ).to eq( "Hoodoo::Services::Middleware: Incompatible JSON implementation in use which doesn't understand 'object_class' or 'array_class' options" )
|
993
|
+
end
|
994
|
+
|
995
|
+
it 'gets the correct type back when an inter-resource remote call generates an error' do
|
996
|
+
expect_any_instance_of( TestCallImplementation ).to receive( :expectable_result_hook ) do | instance, result |
|
997
|
+
expect( result ).to be_a( Hoodoo::Client::AugmentedArray )
|
998
|
+
expect( result.platform_errors.has_errors? ).to eq( true )
|
999
|
+
end
|
1000
|
+
|
1001
|
+
get(
|
1002
|
+
'/v1/test_call.tar.gz?offset=42', # 42 -> magic -> inner service adds error
|
1003
|
+
nil,
|
1004
|
+
headers_for( locale: 'de' )
|
1005
|
+
)
|
1006
|
+
|
1007
|
+
result = JSON.parse(last_response.body)
|
1008
|
+
expect(result['errors'][0]['code']).to eq('platform.malformed')
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
def show_things( locale: nil, dated_at: nil )
|
1012
|
+
get(
|
1013
|
+
'/v1/test_call/one/two.tar.gz',
|
1014
|
+
nil,
|
1015
|
+
headers_for( locale: locale, dated_at: dated_at )
|
1016
|
+
)
|
1017
|
+
|
1018
|
+
expect( last_response.status ).to eq( 200 )
|
1019
|
+
parsed = JSON.parse( last_response.body )
|
1020
|
+
|
1021
|
+
expect( parsed[ 'show' ]).to_not be_nil
|
1022
|
+
expect( parsed[ 'show' ][ 'show' ] ).to eq(
|
1023
|
+
{
|
1024
|
+
'locale' => locale.nil? ? 'en-nz' : locale,
|
1025
|
+
'dated_at' => dated_at.to_s,
|
1026
|
+
'dated_from' => '',
|
1027
|
+
'deja_vu' => '',
|
1028
|
+
'resource_uuid' => '',
|
1029
|
+
'body' => nil,
|
1030
|
+
'uri_path_components' => [ 'one,two' ],
|
1031
|
+
'uri_path_extension' => '', # This is the *inner* inter-resource call's state and no filename extensions are used internally
|
1032
|
+
'list_offset' => 0,
|
1033
|
+
'list_limit' => 50,
|
1034
|
+
'list_sort_data' => { 'created_at' => 'desc' },
|
1035
|
+
'list_search_data' => {},
|
1036
|
+
'list_filter_data' => {},
|
1037
|
+
'embeds' => [],
|
1038
|
+
'references' => []
|
1039
|
+
}
|
1040
|
+
)
|
1041
|
+
|
1042
|
+
expect( parsed[ 'options' ] ).to_not be_nil
|
1043
|
+
expect_response_options_for( parsed[ 'options' ] )
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
it 'shows things in the remote service with callbacks', :check_callbacks => true do
|
1047
|
+
show_things()
|
1048
|
+
end
|
1049
|
+
|
1050
|
+
it 'shows things in the remote service without callbacks' do
|
1051
|
+
show_things()
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
it 'shows things with a custom locale and dated-at time' do
|
1055
|
+
show_things( locale: 'bar', dated_at: DateTime.now )
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
it 'gets the correct type back when an inter-resource remote call generates an error' do
|
1059
|
+
expect_any_instance_of( TestCallImplementation ).to receive( :expectable_result_hook ) do | instance, result |
|
1060
|
+
expect( result ).to be_a( Hoodoo::Client::AugmentedHash )
|
1061
|
+
expect( result.platform_errors.has_errors? ).to eq( true )
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
get( '/v1/test_call/return_error', nil, headers_for() )
|
1065
|
+
|
1066
|
+
result = JSON.parse(last_response.body)
|
1067
|
+
expect(result['errors'][0]['code']).to eq('generic.invalid_string')
|
1068
|
+
end
|
1069
|
+
|
1070
|
+
def create_things( locale: nil, dated_from: nil, deja_vu: nil, resource_uuid: nil )
|
1071
|
+
post(
|
1072
|
+
'/v1/test_call.tar.gz',
|
1073
|
+
{ 'foo' => 'bar', 'baz' => 'boo' }.to_json,
|
1074
|
+
headers_for( locale: locale, dated_from: dated_from, deja_vu: deja_vu, resource_uuid: resource_uuid )
|
1075
|
+
)
|
1076
|
+
|
1077
|
+
expect( last_response.status ).to eq( 200 )
|
1078
|
+
parsed = JSON.parse( last_response.body )
|
1079
|
+
|
1080
|
+
expect( parsed[ 'create' ]).to_not be_nil
|
1081
|
+
expect( parsed[ 'create' ][ 'create' ] ).to eq(
|
1082
|
+
{
|
1083
|
+
'locale' => locale.nil? ? 'en-nz' : locale,
|
1084
|
+
'dated_at' => '',
|
1085
|
+
'dated_from' => dated_from.to_s,
|
1086
|
+
'deja_vu' => '', # Not passed through
|
1087
|
+
'resource_uuid' => '', # Not passed through
|
1088
|
+
'body' => { 'foo' => 'bar', 'baz' => 'boo' },
|
1089
|
+
'uri_path_components' => [],
|
1090
|
+
'uri_path_extension' => '',
|
1091
|
+
'list_offset' => 0,
|
1092
|
+
'list_limit' => 50,
|
1093
|
+
'list_sort_data' => { 'created_at' => 'desc' },
|
1094
|
+
'list_search_data' => {},
|
1095
|
+
'list_filter_data' => {},
|
1096
|
+
'embeds' => [],
|
1097
|
+
'references' => []
|
1098
|
+
}
|
1099
|
+
)
|
1100
|
+
|
1101
|
+
expect( parsed[ 'options' ] ).to_not be_nil
|
1102
|
+
expect_response_options_for( parsed[ 'options' ] )
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
it 'creates things in the remote service with callbacks', :check_callbacks => true do
|
1106
|
+
create_things()
|
1107
|
+
end
|
1108
|
+
|
1109
|
+
it 'creates things in the remote service without callbacks' do
|
1110
|
+
create_things()
|
1111
|
+
end
|
1112
|
+
|
1113
|
+
it 'creates things with a custom locale, passes through dated_from but not deja_vu' do
|
1114
|
+
create_things( locale: 'baz', dated_from: DateTime.now, deja_vu: true )
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
it 'can specify a UUID via an inter-resource call if it has top-level permission' do
|
1118
|
+
@test_session.scoping.authorised_http_headers = [ 'HTTP_X_RESOURCE_UUID' ]
|
1119
|
+
|
1120
|
+
post(
|
1121
|
+
'/v1/test_call.tar.gz',
|
1122
|
+
{ :foo => 'specify_uuid' }.to_json,
|
1123
|
+
headers_for()
|
1124
|
+
)
|
1125
|
+
|
1126
|
+
expect( last_response.status ).to eq( 200 )
|
1127
|
+
parsed = JSON.parse( last_response.body )
|
1128
|
+
|
1129
|
+
expect( parsed[ 'create' ]).to_not be_nil
|
1130
|
+
resource_uuid = parsed[ 'create' ][ 'create' ][ 'resource_uuid' ]
|
1131
|
+
expect( Hoodoo::UUID.valid?( resource_uuid ) ).to eq( true )
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
it 'cannot specify a UUID via an inter-resource call if it does not have top-level permission' do
|
1135
|
+
post(
|
1136
|
+
'/v1/test_call.tar.gz',
|
1137
|
+
{ :foo => 'specify_uuid' }.to_json,
|
1138
|
+
headers_for()
|
1139
|
+
)
|
1140
|
+
|
1141
|
+
expect( last_response.status ).to eq( 403 )
|
1142
|
+
expect_response_headers_on( last_response ) # should call target service, but fails on inter-resource call
|
1143
|
+
|
1144
|
+
parsed = JSON.parse( last_response.body )
|
1145
|
+
|
1146
|
+
expect( parsed[ 'errors' ].size ).to eq( 1 )
|
1147
|
+
expect( parsed[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'platform.forbidden' )
|
1148
|
+
expect( parsed[ 'errors' ][ 0 ][ 'reference' ] ).to be_nil # Ensure no information disclosure vulnerability
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
# Remember this time the deja-vu stuff is happening one level down,
|
1152
|
+
# as an inter-resource call, not at the top level per the similar
|
1153
|
+
# tests that use spec_helper_http in the previous major "section"
|
1154
|
+
# of this file.
|
1155
|
+
#
|
1156
|
+
it 'gets a 204 with deja-vu and duplication' do
|
1157
|
+
post(
|
1158
|
+
'/v1/test_call.tar.gz',
|
1159
|
+
{ 'foo' => 'bar', 'deja_vu_in_other_resource' => 'yes' }.to_json,
|
1160
|
+
headers_for() # *not* requesting Deja-Vu at the top level
|
1161
|
+
)
|
1162
|
+
|
1163
|
+
# If the test passes, the inter-resource call returned the equivlant
|
1164
|
+
# of a 204, so the parsed payload giving us the result of that call
|
1165
|
+
# will show an empty Hash and a 'confirmed' option from the endpoint.
|
1166
|
+
|
1167
|
+
expect( last_response.status ).to eq( 200 )
|
1168
|
+
expect_response_headers_on( last_response )
|
1169
|
+
|
1170
|
+
parsed = JSON.parse( last_response.body )
|
1171
|
+
|
1172
|
+
expect( parsed[ 'create' ] ).to eq( {} )
|
1173
|
+
expect( parsed[ 'options' ][ 'deja_vu' ] ).to eq( 'confirmed' )
|
1174
|
+
end
|
1175
|
+
|
1176
|
+
it 'gets non-204 with deja-vu and duplication plus other errors' do
|
1177
|
+
post(
|
1178
|
+
'/v1/test_call.tar.gz',
|
1179
|
+
{
|
1180
|
+
'foo' => 'bar',
|
1181
|
+
'deja_vu_in_other_resource' => 'yes',
|
1182
|
+
'additional_error' => 'generic.invalid_decimal'
|
1183
|
+
}.to_json,
|
1184
|
+
headers_for() # *not* requesting Deja-Vu at the top level
|
1185
|
+
)
|
1186
|
+
|
1187
|
+
# If the test passes, the inter-resource call will add other errors
|
1188
|
+
# that aren't just duplication, so the call returns the full error
|
1189
|
+
# set normally rather than via a 204 response equivalent. Normal
|
1190
|
+
# error handling in the calling resource therefore takes place.
|
1191
|
+
|
1192
|
+
expect( last_response.status ).to eq( 422 )
|
1193
|
+
expect_response_headers_on( last_response )
|
1194
|
+
|
1195
|
+
parsed = JSON.parse( last_response.body )
|
1196
|
+
|
1197
|
+
expect( parsed[ 'errors' ].size ).to eq( 2 )
|
1198
|
+
expect( parsed[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'generic.invalid_duplication' )
|
1199
|
+
expect( parsed[ 'errors' ][ 0 ][ 'reference' ] ).to eq( 'deja_vu' )
|
1200
|
+
expect( parsed[ 'errors' ][ 1 ][ 'code' ] ).to eq( 'generic.invalid_decimal' )
|
1201
|
+
expect( parsed[ 'errors' ][ 1 ][ 'reference' ] ).to eq( 'deja_vu' )
|
1202
|
+
end
|
1203
|
+
|
1204
|
+
it 'rejects non-deja-vu 204 responses' do
|
1205
|
+
expect_any_instance_of( Hoodoo::Client::Endpoint::HTTPBased::DescriptionOfResponse ).to receive( :http_status_code ).and_return( 204 )
|
1206
|
+
expect_any_instance_of( Hoodoo::Client::Endpoint::HTTPBased::DescriptionOfResponse ).to receive( :raw_body_data ).and_return( 'not JSON' )
|
1207
|
+
|
1208
|
+
post(
|
1209
|
+
'/v1/test_call.tar.gz',
|
1210
|
+
{ 'foo' => 'bar' }.to_json,
|
1211
|
+
headers_for()
|
1212
|
+
)
|
1213
|
+
|
1214
|
+
expect( last_response.status ).to eq( 500 )
|
1215
|
+
expect_response_headers_on( last_response )
|
1216
|
+
|
1217
|
+
parsed = JSON.parse( last_response.body )
|
1218
|
+
|
1219
|
+
expect( parsed[ 'errors' ].size ).to eq( 1 )
|
1220
|
+
expect( parsed[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'platform.fault' )
|
1221
|
+
expect( parsed[ 'errors' ][ 0 ][ 'message' ] ).to eq( "Unexpected raw HTTP status code 204 with 'X-Deja-Vu: confirmed' not present" )
|
1222
|
+
expect( parsed[ 'errors' ][ 0 ][ 'reference' ] ).to eq( '204' )
|
1223
|
+
end
|
1224
|
+
|
1225
|
+
it 'gets the correct type back when an inter-resource remote call generates an error' do
|
1226
|
+
expect_any_instance_of( TestCallImplementation ).to receive( :expectable_result_hook ) do | instance, result |
|
1227
|
+
expect( result ).to be_a( Hoodoo::Client::AugmentedHash )
|
1228
|
+
expect( result.platform_errors.has_errors? ).to eq( true )
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
post(
|
1232
|
+
'/v1/test_call.tar.gz',
|
1233
|
+
{ :foo => 'bar', :return_error => 'yes' }.to_json,
|
1234
|
+
headers_for()
|
1235
|
+
)
|
1236
|
+
|
1237
|
+
result = JSON.parse(last_response.body)
|
1238
|
+
expect(result['errors'][0]['code']).to eq('platform.malformed')
|
1239
|
+
end
|
1240
|
+
|
1241
|
+
def update_things( locale: nil )
|
1242
|
+
patch(
|
1243
|
+
'/v1/test_call/aa/bb.tar.gz',
|
1244
|
+
{ 'foo' => 'boo', 'baz' => 'bar' }.to_json,
|
1245
|
+
headers_for( locale: locale )
|
1246
|
+
)
|
1247
|
+
|
1248
|
+
expect( last_response.status ).to eq( 200 )
|
1249
|
+
parsed = JSON.parse( last_response.body )
|
1250
|
+
|
1251
|
+
expect( parsed[ 'update' ]).to_not be_nil
|
1252
|
+
expect( parsed[ 'update' ][ 'update' ] ).to eq(
|
1253
|
+
{
|
1254
|
+
'locale' => locale.nil? ? 'en-nz' : locale,
|
1255
|
+
'dated_at' => '',
|
1256
|
+
'dated_from' => '',
|
1257
|
+
'deja_vu' => '',
|
1258
|
+
'resource_uuid' => '',
|
1259
|
+
'body' => { 'foo' => 'boo', 'baz' => 'bar' },
|
1260
|
+
'uri_path_components' => [ 'aa,bb' ],
|
1261
|
+
'uri_path_extension' => '',
|
1262
|
+
'list_offset' => 0,
|
1263
|
+
'list_limit' => 50,
|
1264
|
+
'list_sort_data' => { 'created_at' => 'desc' },
|
1265
|
+
'list_search_data' => {},
|
1266
|
+
'list_filter_data' => {},
|
1267
|
+
'embeds' => [],
|
1268
|
+
'references' => []
|
1269
|
+
}
|
1270
|
+
)
|
1271
|
+
|
1272
|
+
expect( parsed[ 'options' ] ).to_not be_nil
|
1273
|
+
expect_response_options_for( parsed[ 'options' ] )
|
1274
|
+
expect( parsed[ 'options' ][ 'example_header' ] ).to eq( 'example' )
|
1275
|
+
end
|
1276
|
+
|
1277
|
+
it 'updates things in the remote service with callbacks', :check_callbacks => true do
|
1278
|
+
update_things()
|
1279
|
+
end
|
1280
|
+
|
1281
|
+
it 'updates things in the remote service without callbacks' do
|
1282
|
+
update_things()
|
1283
|
+
end
|
1284
|
+
|
1285
|
+
it 'updates things with a custom locale' do
|
1286
|
+
update_things( locale: 'boo' )
|
1287
|
+
end
|
1288
|
+
|
1289
|
+
it 'gets the correct type back when an inter-resource remote call generates an error' do
|
1290
|
+
expect_any_instance_of( TestCallImplementation ).to receive( :expectable_result_hook ) do | instance, result |
|
1291
|
+
expect( result ).to be_a( Hoodoo::Client::AugmentedHash )
|
1292
|
+
expect( result.platform_errors.has_errors? ).to eq( true )
|
1293
|
+
end
|
1294
|
+
|
1295
|
+
patch( '/v1/test_call/return_error', '{}', headers_for() )
|
1296
|
+
|
1297
|
+
result = JSON.parse(last_response.body)
|
1298
|
+
expect(result['errors'][0]['code']).to eq('platform.malformed')
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
def delete_things( locale: nil, deja_vu: nil )
|
1302
|
+
delete(
|
1303
|
+
'/v1/test_call/aone/btwo.tar.gz',
|
1304
|
+
nil,
|
1305
|
+
headers_for( locale:locale, deja_vu: deja_vu )
|
1306
|
+
)
|
1307
|
+
|
1308
|
+
expect( last_response.status ).to eq( 200 )
|
1309
|
+
parsed = JSON.parse( last_response.body )
|
1310
|
+
|
1311
|
+
expect( parsed[ 'delete' ]).to_not be_nil
|
1312
|
+
expect( parsed[ 'delete' ][ 'delete' ] ).to eq(
|
1313
|
+
{
|
1314
|
+
'locale' => locale.nil? ? 'en-nz' : locale,
|
1315
|
+
'dated_at' => '',
|
1316
|
+
'dated_from' => '',
|
1317
|
+
'deja_vu' => '', # Not passed through
|
1318
|
+
'resource_uuid' => '', # Not passed through
|
1319
|
+
'body' => nil,
|
1320
|
+
'uri_path_components' => [ 'aone,btwo' ],
|
1321
|
+
'uri_path_extension' => '',
|
1322
|
+
'list_offset' => 0,
|
1323
|
+
'list_limit' => 50,
|
1324
|
+
'list_sort_data' => { 'created_at' => 'desc' },
|
1325
|
+
'list_search_data' => {},
|
1326
|
+
'list_filter_data' => {},
|
1327
|
+
'embeds' => [],
|
1328
|
+
'references' => []
|
1329
|
+
}
|
1330
|
+
)
|
1331
|
+
|
1332
|
+
expect( parsed[ 'options' ] ).to_not be_nil
|
1333
|
+
expect_response_options_for( parsed[ 'options' ] )
|
1334
|
+
end
|
1335
|
+
|
1336
|
+
it 'deletes things in the remote service with callbacks', :check_callbacks => true do
|
1337
|
+
delete_things()
|
1338
|
+
end
|
1339
|
+
|
1340
|
+
it 'deletes things in the remote service without callbacks' do
|
1341
|
+
delete_things()
|
1342
|
+
end
|
1343
|
+
|
1344
|
+
it 'deletes things, passing through custom locale but not deja_v' do
|
1345
|
+
delete_things( locale: 'bye', deja_vu: true )
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
it 'should receive errors from remote service as if from the local call' do
|
1349
|
+
get(
|
1350
|
+
'/v1/test_call/return_error',
|
1351
|
+
nil,
|
1352
|
+
{ 'CONTENT_TYPE' => 'application/json; charset=utf-8' }
|
1353
|
+
)
|
1354
|
+
|
1355
|
+
expect( last_response.status ).to eq( 422 )
|
1356
|
+
expect_response_headers_on( last_response )
|
1357
|
+
|
1358
|
+
parsed = JSON.parse( last_response.body )
|
1359
|
+
|
1360
|
+
expect( parsed[ 'errors' ] ).to_not be_nil
|
1361
|
+
expect( parsed[ 'errors' ].count ).to eq(1)
|
1362
|
+
expect( parsed[ 'errors' ][ 0 ] ).to eq({
|
1363
|
+
'code' => 'generic.invalid_string',
|
1364
|
+
'message' => 'Returning error as requested',
|
1365
|
+
'reference' => 'no ident,no other ident'
|
1366
|
+
})
|
1367
|
+
end
|
1368
|
+
|
1369
|
+
it 'gets a 404 for missing endpoints (and gets correct type back when inter-resource call generates an error)' do
|
1370
|
+
expect_any_instance_of( TestCallImplementation ).to receive( :expectable_result_hook ) do | instance, result |
|
1371
|
+
expect( result ).to be_a( Hoodoo::Client::AugmentedHash )
|
1372
|
+
expect( result.platform_errors.has_errors? ).to eq( true )
|
1373
|
+
end
|
1374
|
+
|
1375
|
+
get(
|
1376
|
+
'/v1/test_call/generate_404',
|
1377
|
+
nil,
|
1378
|
+
{ 'CONTENT_TYPE' => 'application/json; charset=utf-8' }
|
1379
|
+
)
|
1380
|
+
|
1381
|
+
expect( last_response.status ).to eq( 404 )
|
1382
|
+
expect_response_headers_on( last_response )
|
1383
|
+
end
|
1384
|
+
|
1385
|
+
it 'can reuse an endpoint' do
|
1386
|
+
get(
|
1387
|
+
'/v1/test_call/ensure_repeated_use_works',
|
1388
|
+
nil,
|
1389
|
+
{ 'CONTENT_TYPE' => 'application/json; charset=utf-8' }
|
1390
|
+
)
|
1391
|
+
|
1392
|
+
expect( last_response.status ).to eq( 200 )
|
1393
|
+
expect_response_headers_on( last_response )
|
1394
|
+
|
1395
|
+
parsed = JSON.parse( last_response.body )
|
1396
|
+
|
1397
|
+
expect( parsed[ 'show' ]).to_not be_nil
|
1398
|
+
expect( parsed[ 'show' ][ 'show' ][ 'uri_path_components' ] ).to eq( [ 'ensure_repeated_use_works' ] )
|
1399
|
+
|
1400
|
+
expect( parsed[ 'options' ] ).to_not be_nil
|
1401
|
+
expect_response_options_for( parsed[ 'options' ] )
|
1402
|
+
end
|
1403
|
+
|
1404
|
+
# Ruby can't kill off an "unresponsive" thread - there seems to be no
|
1405
|
+
# equivalent of "kill -9" and the likes of "#exit!" are long gone - so
|
1406
|
+
# the WEBrick server thread, which never returns to the Ruby interpreter
|
1407
|
+
# after the Rack::Server.start() call (or equivalent) can't die. Instead
|
1408
|
+
# we are forced to write a fragile test that simulates a connection
|
1409
|
+
# failure to the endpoint.
|
1410
|
+
#
|
1411
|
+
it 'should get a 404 for no-longer-running endpoints' do
|
1412
|
+
expect_any_instance_of( Net::HTTP ).to receive( :request ).once.and_raise( Errno::ECONNREFUSED )
|
1413
|
+
|
1414
|
+
get(
|
1415
|
+
'/v1/test_call/show_something',
|
1416
|
+
nil,
|
1417
|
+
{ 'CONTENT_TYPE' => 'application/json; charset=utf-8' }
|
1418
|
+
)
|
1419
|
+
|
1420
|
+
expect( last_response.status ).to eq( 404 )
|
1421
|
+
expect_response_headers_on( last_response ) # should call target service, but fails on inter-resource call
|
1422
|
+
end
|
1423
|
+
|
1424
|
+
# Similarly shaky test for simulating arbitrary other failure kinds.
|
1425
|
+
#
|
1426
|
+
it 'should get a 500 for arbitrary failures' do
|
1427
|
+
expect_any_instance_of( Net::HTTP ).to receive( :request ).once.and_raise( 'some connection error' )
|
1428
|
+
|
1429
|
+
get(
|
1430
|
+
'/v1/test_call/show_something',
|
1431
|
+
nil,
|
1432
|
+
{ 'CONTENT_TYPE' => 'application/json; charset=utf-8' }
|
1433
|
+
)
|
1434
|
+
|
1435
|
+
expect( last_response.status ).to eq( 500 )
|
1436
|
+
expect( last_response.body ).to include( 'some connection error' )
|
1437
|
+
expect_response_headers_on( last_response )
|
1438
|
+
end
|
1439
|
+
end
|
1440
|
+
end
|