configuration_service 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +36 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +41 -0
- data/README.md +30 -0
- data/README.rdoc +17 -0
- data/Rakefile +16 -0
- data/configuration_service.gemspec +24 -0
- data/lib/configuration_service/base.rb +82 -0
- data/lib/configuration_service/configuration.rb +29 -0
- data/lib/configuration_service/errors.rb +18 -0
- data/lib/configuration_service/provider/broken.rb +30 -0
- data/lib/configuration_service/provider/stub.rb +96 -0
- data/lib/configuration_service/provider/stub_store.rb +58 -0
- data/lib/configuration_service/provider.rb +13 -0
- data/lib/configuration_service/test/orchestration_provider.rb +252 -0
- data/lib/configuration_service/test/orchestration_provider_registry.rb +52 -0
- data/lib/configuration_service/test/orchestrator.rb +240 -0
- data/lib/configuration_service/test/orchestrator_environment_factory.rb +31 -0
- data/lib/configuration_service/test/response.rb +141 -0
- data/lib/configuration_service/test/stub_orchestration_provider.rb +61 -0
- data/lib/configuration_service/test.rb +25 -0
- data/lib/configuration_service.rb +17 -0
- metadata +121 -0
@@ -0,0 +1,252 @@
|
|
1
|
+
require 'configuration_service'
|
2
|
+
|
3
|
+
module ConfigurationService
|
4
|
+
|
5
|
+
module Test
|
6
|
+
|
7
|
+
##
|
8
|
+
# Abstract Orchestrator provider
|
9
|
+
#
|
10
|
+
# Extend this class if you want your test orchestration provider to
|
11
|
+
# constrain your implementation's interface to work as a configuration
|
12
|
+
# service provider. If you have no intention of plugging your
|
13
|
+
# implementation into ConfigurationService::Base, build your own test
|
14
|
+
# orchestration provider from scratch, using Orchestrator as a guide.
|
15
|
+
#
|
16
|
+
# Extensions should implement #service_provider, #broken_service_provider,
|
17
|
+
# #token_for and #delete_configuration.
|
18
|
+
#
|
19
|
+
class OrchestrationProvider
|
20
|
+
|
21
|
+
##
|
22
|
+
# * +:requesting_configurations+
|
23
|
+
# * +:publishing_configurations+
|
24
|
+
# * +:nothing+ (if possible, provide credentials that don't allow
|
25
|
+
# operations on the configuration +identifier+)
|
26
|
+
ACTIVITY_ROLE_MAP = {
|
27
|
+
:requesting_configurations => :consumer,
|
28
|
+
:publishing_configurations => :publisher,
|
29
|
+
:nothing => :none
|
30
|
+
} unless defined?(ACTIVITY_ROLE_MAP)
|
31
|
+
##
|
32
|
+
# * +:consumer+
|
33
|
+
# * +:publisher+
|
34
|
+
# * +:none+
|
35
|
+
#
|
36
|
+
ROLES = ACTIVITY_ROLE_MAP.values unless defined?(ROLES)
|
37
|
+
|
38
|
+
##
|
39
|
+
# Returns a new Orchestrator provider
|
40
|
+
#
|
41
|
+
# The provider is always initialized with the same configuration +identifier+
|
42
|
+
#
|
43
|
+
def initialize
|
44
|
+
@identifier = "acme"
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# See Orchestrator#authorize
|
49
|
+
#
|
50
|
+
def authorize(role)
|
51
|
+
@token = token_for(role)
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# See Orchestrator#deauthorize
|
56
|
+
#
|
57
|
+
def deauthorize
|
58
|
+
@token = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# See Orchestrator#given_metadata
|
63
|
+
#
|
64
|
+
def given_metadata
|
65
|
+
@metadata = {"version" => "1.0"}
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# See Orchestrator#given_metadata_filter
|
70
|
+
#
|
71
|
+
def given_metadata_filter
|
72
|
+
@metadata_filter = {"revision" => "d4d40eab-cf66-4af5-9a77-d956a11682de"}
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# See Orchestrator#given_existing_configuration
|
77
|
+
#
|
78
|
+
def given_existing_configuration
|
79
|
+
authorized_as(:publisher) do
|
80
|
+
@existing_configuration = publish_configuration
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# See Orchestrator#given_invalid_configuration
|
86
|
+
#
|
87
|
+
def given_invalid_configuration
|
88
|
+
@configuration = "This should be an object!"
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# See Orchestrator#given_missing_configuration
|
93
|
+
#
|
94
|
+
def given_missing_configuration
|
95
|
+
authorized_as(:publisher) do
|
96
|
+
delete_configuration
|
97
|
+
@existing_configuration = nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# See Orchestrator#given_existing_configuration_matching_metadata_filter
|
103
|
+
#
|
104
|
+
def given_existing_configuration_matching_metadata_filter
|
105
|
+
@metadata_matching_filter = (@metadata || {}).merge(@metadata_filter)
|
106
|
+
given_existing_configuration
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# See Orchestrator#given_existing_configuration_not_matching_metadata_filter
|
111
|
+
#
|
112
|
+
def given_existing_configuration_not_matching_metadata_filter
|
113
|
+
@metadata_matching_filter = {}
|
114
|
+
given_existing_configuration
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# See Orchestrator#existing_configuration
|
119
|
+
#
|
120
|
+
def existing_configuration
|
121
|
+
@existing_configuration.data
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# See Orchestrator#existing_revision
|
126
|
+
#
|
127
|
+
def existing_revision
|
128
|
+
@existing_configuration.revision
|
129
|
+
end
|
130
|
+
|
131
|
+
##
|
132
|
+
# Perform a consuming operation against the service under test
|
133
|
+
#
|
134
|
+
# The response from the service is wrapped in a test Response.
|
135
|
+
#
|
136
|
+
def request_configuration
|
137
|
+
wrap_response do
|
138
|
+
if @metadata_filter
|
139
|
+
service.request_configuration(@metadata_filter)
|
140
|
+
else
|
141
|
+
service.request_configuration
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# Perform a publishing operation against the service under test
|
148
|
+
#
|
149
|
+
# The response from the service is wrapped in a test Response.
|
150
|
+
#
|
151
|
+
def publish_configuration
|
152
|
+
wrap_response do
|
153
|
+
if @metadata
|
154
|
+
service.publish_configuration(configuration, @metadata)
|
155
|
+
elsif @metadata_matching_filter
|
156
|
+
service.publish_configuration(configuration, @metadata_matching_filter)
|
157
|
+
else
|
158
|
+
service.publish_configuration(configuration)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# Arrange for the next publication or consuming operation to fail
|
165
|
+
#
|
166
|
+
# This is done by using a #broken_service_provider to service the
|
167
|
+
# next operation.
|
168
|
+
#
|
169
|
+
def fail_next_request
|
170
|
+
@fail_next = true
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
##
|
176
|
+
# Return a ConfigurationService::Base provider
|
177
|
+
#
|
178
|
+
# The provider should use a consistent +identifier+.
|
179
|
+
#
|
180
|
+
def service_provider # :doc:
|
181
|
+
raise NotImplementedError, "#{self.class} must implement service_provider"
|
182
|
+
end
|
183
|
+
|
184
|
+
##
|
185
|
+
# Return a broken ConfigurationService::Base provider
|
186
|
+
#
|
187
|
+
# The provider's #publish_configuration and #request_configuration
|
188
|
+
# methods must raise an Error other than AuthorizationError.
|
189
|
+
#
|
190
|
+
def broken_service_provider # :doc:
|
191
|
+
raise NotImplementedError, "#{self.class} must implement broken_service_provider"
|
192
|
+
end
|
193
|
+
|
194
|
+
##
|
195
|
+
# Delete the configuration identified by the consistent +identifier+
|
196
|
+
#
|
197
|
+
# Deleting non-existent configuration should not produce an error.
|
198
|
+
#
|
199
|
+
def delete_configuration # :doc:
|
200
|
+
raise NotImplementedError, "#{self.class} must implement delete_configuration"
|
201
|
+
end
|
202
|
+
|
203
|
+
##
|
204
|
+
# Return a token that authorizes +role+
|
205
|
+
#
|
206
|
+
# Valid roles are:
|
207
|
+
#
|
208
|
+
# * +:consumer+
|
209
|
+
# * +:publisher+
|
210
|
+
# * +:nothing+
|
211
|
+
#
|
212
|
+
# Note that a token should be returned for +:nothing+, but the token should
|
213
|
+
# not be authorized to consume or publish to the +identifier+.
|
214
|
+
#
|
215
|
+
def token_for(role) # :doc:
|
216
|
+
raise NotImplementedError, "#{self.class} must implement token_for(role)"
|
217
|
+
end
|
218
|
+
|
219
|
+
def configuration
|
220
|
+
@configuration ||= {"verbose" => true}
|
221
|
+
end
|
222
|
+
|
223
|
+
def service
|
224
|
+
if @fail_next
|
225
|
+
@fail_next = false
|
226
|
+
ConfigurationService.new(broken_service_provider)
|
227
|
+
else
|
228
|
+
ConfigurationService.new(service_provider)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def authorized_as(role)
|
233
|
+
restore_token = @token
|
234
|
+
authorize(role)
|
235
|
+
yield.tap do
|
236
|
+
@token = restore_token
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def wrap_response # :nodoc:
|
241
|
+
begin
|
242
|
+
ConfigurationService::Test::Response::Success.new(yield)
|
243
|
+
rescue ConfigurationService::Error => e
|
244
|
+
ConfigurationService::Test::Response::Failure.new(e)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "singleton"
|
2
|
+
|
3
|
+
module ConfigurationService
|
4
|
+
|
5
|
+
module Test
|
6
|
+
|
7
|
+
# The Singleton module deletes the instance on include!
|
8
|
+
unless defined?(OrchestrationProviderRegistry)
|
9
|
+
|
10
|
+
##
|
11
|
+
# Singleton registry of Orchestrator providers
|
12
|
+
#
|
13
|
+
class OrchestrationProviderRegistry
|
14
|
+
include Singleton
|
15
|
+
|
16
|
+
def initialize ## :nodoc:
|
17
|
+
@providers = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Register a +provider+ identified by the string +identifier+
|
22
|
+
#
|
23
|
+
# The +provider+ should be a class with a default (nullary) constructor.
|
24
|
+
#
|
25
|
+
def register(identifier, provider)
|
26
|
+
@providers[identifier] = provider
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Return the +provider+ identified by the string +identifier+
|
31
|
+
#
|
32
|
+
# The +provider must already have been registered with #register,
|
33
|
+
# and should be a class with a default (nullary) constructor.
|
34
|
+
#
|
35
|
+
# Returns +nil+ if no provider has been registered with the given
|
36
|
+
# +identifier+.
|
37
|
+
#
|
38
|
+
def lookup(identifier)
|
39
|
+
@providers[identifier]
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Return the singleton registry instance
|
44
|
+
# :singleton-method: instance
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
require 'configuration_service'
|
2
|
+
|
3
|
+
module ConfigurationService
|
4
|
+
|
5
|
+
module Test
|
6
|
+
|
7
|
+
##
|
8
|
+
# The declarative test orchestration API
|
9
|
+
#
|
10
|
+
# This is the declarative API that describes what must be done to test a
|
11
|
+
# configuration service provider. It catalogues all the services that the
|
12
|
+
# cucumber step definitions expect from a test orchestration provider.
|
13
|
+
#
|
14
|
+
# It keeps no domain state, because that would couple it to the
|
15
|
+
# service implementation. By making no assumptions at all about the API
|
16
|
+
# or data, it allows implementors to produce service implementations
|
17
|
+
# that do not adhere to the anticipated ConfigurationService::Base API,
|
18
|
+
# by writing their own OrchestrationProvider from scratch.
|
19
|
+
#
|
20
|
+
# However, implementors who are trying to produce ConfigurationService::Base
|
21
|
+
# providers should extend OrchestrationProvider, which anticipates a
|
22
|
+
# compatible provider API.
|
23
|
+
#
|
24
|
+
# Note that the +response+ instance variable is not domain state; it is
|
25
|
+
# a test artifact (Response or similar) that orchestration providers use
|
26
|
+
# to wrap responses from the service provider.
|
27
|
+
#
|
28
|
+
class Orchestrator
|
29
|
+
|
30
|
+
##
|
31
|
+
# Return a new orchestrator initialized with a new instance of +provider_class+.
|
32
|
+
#
|
33
|
+
# The provider is expected to use a consistent configuration +identifier+ for
|
34
|
+
# all publishing and consuming operations.
|
35
|
+
#
|
36
|
+
def initialize(provider_class)
|
37
|
+
@provider = provider_class.new
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Include metadata in the next publishing operation
|
42
|
+
#
|
43
|
+
def given_metadata
|
44
|
+
@provider.given_metadata
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Include a metadata filter in the next consuming operation
|
49
|
+
#
|
50
|
+
def given_metadata_filter
|
51
|
+
@provider.given_metadata_filter
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Arrange a published configuration fixture
|
56
|
+
def given_existing_configuration
|
57
|
+
@provider.given_existing_configuration
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Use invalid configuration data in the next publishing operation
|
62
|
+
#
|
63
|
+
def given_invalid_configuration
|
64
|
+
@provider.given_invalid_configuration
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Delete any existing configuration
|
69
|
+
#
|
70
|
+
def given_missing_configuration
|
71
|
+
@provider.given_missing_configuration
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Arrange a published configuration fixture
|
76
|
+
#
|
77
|
+
# Use a metadata filter that matches the fixture's metadata in the next consuming operation.
|
78
|
+
#
|
79
|
+
def given_existing_configuration_matching_metadata_filter
|
80
|
+
@provider.given_existing_configuration_matching_metadata_filter
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Arrange a published configuration fixture
|
85
|
+
#
|
86
|
+
# Use a metadata filter that does not match the fixture's metadata in the next consuming operation.
|
87
|
+
#
|
88
|
+
def given_existing_configuration_not_matching_metadata_filter
|
89
|
+
@provider.given_existing_configuration_not_matching_metadata_filter
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Return a published configuration fixture
|
94
|
+
#
|
95
|
+
# E.g. as arranged by #given_existing_configuration.
|
96
|
+
#
|
97
|
+
# TODO remove; step definitions expect this to be Comparable
|
98
|
+
#
|
99
|
+
def existing_configuration
|
100
|
+
@provider.existing_configuration
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Return the revision of a published configuration fixture
|
105
|
+
#
|
106
|
+
# E.g. as arranged by #given_existing_configuration.
|
107
|
+
#
|
108
|
+
# TODO remove; step definitions expect this to be Comparable
|
109
|
+
#
|
110
|
+
def existing_revision
|
111
|
+
@provider.existing_revision
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Authorize the next consuming or publishing operation for +activity+
|
116
|
+
#
|
117
|
+
# Valid activities (as per ACTIVITY_ROLE_MAP in ConfigurationService::Test::OrchestrationProvider) are:
|
118
|
+
#
|
119
|
+
# * +:requesting_configurations+
|
120
|
+
# * +:publishing_configurations+
|
121
|
+
# * +:nothing+
|
122
|
+
#
|
123
|
+
# Where possible, the orchestration provider should authorize +:nothing+
|
124
|
+
# by providing valid credentials that don't allow operations on the
|
125
|
+
# configuration +identifier+ that it tests against.
|
126
|
+
#
|
127
|
+
def authorize(activity)
|
128
|
+
role = role_for(activity) or raise "unknown authorizable activity #{activity.inspect}"
|
129
|
+
@provider.authorize(role)
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# Remove any previous authorization
|
134
|
+
#
|
135
|
+
# E.g. as arranged by #authorize.
|
136
|
+
#
|
137
|
+
def deauthorize
|
138
|
+
@provider.deauthorize
|
139
|
+
end
|
140
|
+
|
141
|
+
##
|
142
|
+
# Perform a consuming operation against the service under test
|
143
|
+
#
|
144
|
+
# The provider is expected to wrap the response in a Response (or
|
145
|
+
# simimlar) and return that.
|
146
|
+
#
|
147
|
+
def request_configuration
|
148
|
+
@response = @provider.request_configuration
|
149
|
+
end
|
150
|
+
|
151
|
+
##
|
152
|
+
# Perform a publishing operation against the service under test
|
153
|
+
#
|
154
|
+
# The provider is expected to wrap the response in a Response (or
|
155
|
+
# simimlar) and return that.
|
156
|
+
#
|
157
|
+
def publish_configuration
|
158
|
+
@response = @provider.publish_configuration
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# True if the last consuming or publishing operation was allowed
|
163
|
+
#
|
164
|
+
def request_allowed?
|
165
|
+
@response.allowed?
|
166
|
+
end
|
167
|
+
|
168
|
+
##
|
169
|
+
# True if the last consuming or publishing operation failed
|
170
|
+
#
|
171
|
+
# Operations that were not allowed (as per #request_allowed?) or
|
172
|
+
# considered failed.
|
173
|
+
#
|
174
|
+
def request_failed?
|
175
|
+
@response.failed?
|
176
|
+
end
|
177
|
+
|
178
|
+
##
|
179
|
+
# True if the last consuming operation did not return data
|
180
|
+
#
|
181
|
+
def request_not_found?
|
182
|
+
not @response.found?
|
183
|
+
end
|
184
|
+
|
185
|
+
##
|
186
|
+
# True if the last consuming operation did not return data
|
187
|
+
#
|
188
|
+
# TODO: distinguish #request_not_matched? to mean "found data, but filtered out by metadata filter"
|
189
|
+
def request_not_matched?
|
190
|
+
not @response.found?
|
191
|
+
end
|
192
|
+
|
193
|
+
##
|
194
|
+
# The last published or consumed configuration data
|
195
|
+
#
|
196
|
+
# Note that this is the data itself, not a Configuration object.
|
197
|
+
#
|
198
|
+
def published_configuration
|
199
|
+
@response.data
|
200
|
+
end
|
201
|
+
alias :requested_configuration :published_configuration
|
202
|
+
|
203
|
+
##
|
204
|
+
# The revision of the last published or consumed configuration
|
205
|
+
#
|
206
|
+
def published_revision
|
207
|
+
@response.revision
|
208
|
+
end
|
209
|
+
|
210
|
+
##
|
211
|
+
# The last published metadata
|
212
|
+
#
|
213
|
+
def published_metadata
|
214
|
+
@response.metadata
|
215
|
+
end
|
216
|
+
|
217
|
+
##
|
218
|
+
# Arrange for the next publication operation to fail
|
219
|
+
#
|
220
|
+
def given_publication_failure
|
221
|
+
@provider.fail_next_request
|
222
|
+
end
|
223
|
+
|
224
|
+
##
|
225
|
+
# Arrange for the next consuming operation to fail
|
226
|
+
#
|
227
|
+
def given_request_failure
|
228
|
+
@provider.fail_next_request
|
229
|
+
end
|
230
|
+
|
231
|
+
private
|
232
|
+
|
233
|
+
def role_for(activity)
|
234
|
+
ConfigurationService::Test::OrchestrationProvider::ACTIVITY_ROLE_MAP[activity]
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ConfigurationService
|
2
|
+
|
3
|
+
module Test
|
4
|
+
|
5
|
+
##
|
6
|
+
# Builds an Orchestrator using a provider selected from the environment
|
7
|
+
#
|
8
|
+
module OrchestratorEnvironmentFactory
|
9
|
+
|
10
|
+
##
|
11
|
+
# Looks up the provider registered to the OrchestrationProviderRegistry
|
12
|
+
# with the name provided in the +TEST_ORCHESTRATION_PROVIDER+ environment
|
13
|
+
# variable, and returns a new Orchestrator initialized with that
|
14
|
+
# provider.
|
15
|
+
#
|
16
|
+
# Returns a new Orchestrator, or raises a +RuntimeError+ if the
|
17
|
+
# +TEST_ORCHESTRATION_PROVIDER+ environment variable does not name a
|
18
|
+
# provider known to the OrchestrationProviderRegistry.
|
19
|
+
#
|
20
|
+
def self.build
|
21
|
+
identifier = ENV["TEST_ORCHESTRATION_PROVIDER"] or raise "missing environment variable: TEST_ORCHESTRATION_PROVIDER"
|
22
|
+
registry = ConfigurationService::Test::OrchestrationProviderRegistry.instance
|
23
|
+
provider = registry.lookup(identifier) or raise "unknown test orchestration provider: #{identifier}"
|
24
|
+
@test = ConfigurationService::Test::Orchestrator.new(provider)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require "configuration_service"
|
2
|
+
|
3
|
+
module ConfigurationService
|
4
|
+
|
5
|
+
module Test
|
6
|
+
|
7
|
+
##
|
8
|
+
# Encapsulation of ConfigurationService::Base responses
|
9
|
+
#
|
10
|
+
# See Success and Failure.
|
11
|
+
#
|
12
|
+
module Response
|
13
|
+
|
14
|
+
##
|
15
|
+
# Encapsulates a non-error ConfigurationService::Base response
|
16
|
+
#
|
17
|
+
# This allows an OrchestrationProvider to decouple the Orchestrator
|
18
|
+
# from the implementation details of the service provider's responses.
|
19
|
+
#
|
20
|
+
class Success
|
21
|
+
|
22
|
+
##
|
23
|
+
# Initialize a new Success with a response
|
24
|
+
#
|
25
|
+
# The response should be a Configuration object or +nil+ (because
|
26
|
+
# the ConfigurationService::Base API communicates +not found+ as
|
27
|
+
# +nil+).
|
28
|
+
#
|
29
|
+
def initialize(response)
|
30
|
+
@response = response
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Always true
|
35
|
+
#
|
36
|
+
def allowed?
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Always false
|
42
|
+
#
|
43
|
+
def failed?
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# True if the +response+ was not +nil+
|
49
|
+
#
|
50
|
+
def found?
|
51
|
+
not @response.nil?
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# The configuration data dictionary of the response, or +nil+ if not #found?
|
56
|
+
#
|
57
|
+
def data
|
58
|
+
@response and @response.data
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# The revision from the response's metadata, or +nil+ if not #found?
|
63
|
+
#
|
64
|
+
def revision
|
65
|
+
@response and @response.revision
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# The metadata dictionary of the response, or +nil+ if not #found?
|
70
|
+
#
|
71
|
+
def metadata
|
72
|
+
@response and @response.metadata
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# Encapsulates an error ConfigurationService::Base response
|
79
|
+
#
|
80
|
+
# This allows an OrchestrationProvider to decouple the Orchestrator
|
81
|
+
# from the implementation details of the service provider's error
|
82
|
+
# handling.
|
83
|
+
#
|
84
|
+
class Failure
|
85
|
+
|
86
|
+
##
|
87
|
+
# Initialize a new Failure with an exception
|
88
|
+
#
|
89
|
+
def initialize(exception)
|
90
|
+
@exception = exception
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# True unless the exception was an AuthorizationError
|
95
|
+
#
|
96
|
+
def allowed?
|
97
|
+
!@exception.is_a?(ConfigurationService::AuthorizationError)
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# True if the exception was an Error but not an AuthorizationError
|
102
|
+
#
|
103
|
+
def failed?
|
104
|
+
allowed? and @exception.is_a?(ConfigurationService::Error)
|
105
|
+
end
|
106
|
+
|
107
|
+
##
|
108
|
+
# Always false
|
109
|
+
#
|
110
|
+
def found?
|
111
|
+
false
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Raises +NotImplementedError+
|
116
|
+
#
|
117
|
+
def data
|
118
|
+
raise NotImplementedError, "configuration not available after #{@exception.inspect}"
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# Raises +NotImplementedError+
|
123
|
+
#
|
124
|
+
def revision
|
125
|
+
raise NotImplementedError, "revision not available after #{@exception.inspect}"
|
126
|
+
end
|
127
|
+
|
128
|
+
##
|
129
|
+
# Raises +NotImplementedError+
|
130
|
+
#
|
131
|
+
def metadata
|
132
|
+
raise NotImplementedError, "metadata not available after #{@exception.inspect}"
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|