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,73 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: rack_monkey_patch.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: Heyre Be Dragyns.
|
6
|
+
#
|
7
|
+
# For local development, the service middleware needs to know
|
8
|
+
# where other resource endpoints are in terms of HTTP host and
|
9
|
+
# port, so that remote inter-resource calls can work without
|
10
|
+
# up-front static configuration of service host/port data. To
|
11
|
+
# have to manage a fixed list of local development ports in
|
12
|
+
# the face of arbitrary resource endpoint divisions would be a
|
13
|
+
# big pain and cause developers much frustration.
|
14
|
+
#
|
15
|
+
# This means that whenever a service starts up, it needs to
|
16
|
+
# know the HTTP host and port under which it is running, then
|
17
|
+
# tell the middleware about it.
|
18
|
+
#
|
19
|
+
# In the absence of a formal interface in Rack for this, then
|
20
|
+
# we could do still that relatively nicely by looking up the
|
21
|
+
# Rack server in ObjectSpace and asking it for its options -
|
22
|
+
# except some of the web server adapters do really dumb things
|
23
|
+
# like "options.delete(:Host)" to read items out, destroying
|
24
|
+
# the info we need.
|
25
|
+
#
|
26
|
+
# So instead, we have to monkey patch :-(
|
27
|
+
# ----------------------------------------------------------------------
|
28
|
+
# 11-Nov-2014 (ADH): Split out from service_middleware.rb.
|
29
|
+
########################################################################
|
30
|
+
|
31
|
+
if defined?( Rack ) && defined?( Rack::Server )
|
32
|
+
|
33
|
+
# Part of the Rack monkey patch. See file
|
34
|
+
# "rack_monkey_path.rb"'s documentation for details.
|
35
|
+
#
|
36
|
+
module Rack
|
37
|
+
|
38
|
+
# Part of the Rack monkey patch. See file
|
39
|
+
# "rack_monkey_path.rb"'s documentation for details.
|
40
|
+
#
|
41
|
+
class Server
|
42
|
+
|
43
|
+
class << self
|
44
|
+
|
45
|
+
# Part of the Rack monkey patch. See file
|
46
|
+
# "rack_monkey_path.rb"'s documentation for details.
|
47
|
+
#
|
48
|
+
# This method is aliased in place of Rack::Server::start and reads
|
49
|
+
# the passed-in options hash to attempt to determine the host name
|
50
|
+
# and port number under which a Rack based service is running. It
|
51
|
+
# then calls through to Rack's original ::start implementation.
|
52
|
+
#
|
53
|
+
# +options+:: Options (see original Rack::Server documentation).
|
54
|
+
#
|
55
|
+
def start_and_record_host_and_port( options = nil )
|
56
|
+
Hoodoo::Services::Middleware.record_host_and_port( options )
|
57
|
+
racks_original_start( options )
|
58
|
+
end
|
59
|
+
|
60
|
+
# Part of the Rack monkey patch. Alias for the original
|
61
|
+
# Rack::Server::start.
|
62
|
+
#
|
63
|
+
alias racks_original_start start
|
64
|
+
|
65
|
+
# Part of the Rack monkey patch. See ::start_and_record_host_and_port.
|
66
|
+
#
|
67
|
+
# +options+:: See ::start_and_record_host_and_port.
|
68
|
+
#
|
69
|
+
alias start start_and_record_host_and_port
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: context.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: Container for information about the context of a call to
|
6
|
+
# a service, including session, request and response.
|
7
|
+
# ----------------------------------------------------------------------
|
8
|
+
# 03-Oct-2014 (ADH): Created.
|
9
|
+
########################################################################
|
10
|
+
|
11
|
+
module Hoodoo; module Services
|
12
|
+
|
13
|
+
# A collection of objects which describe the context in which a service is
|
14
|
+
# being called. The service reads session and request information and returns
|
15
|
+
# results of its processing via the associated response object.
|
16
|
+
#
|
17
|
+
class Context
|
18
|
+
|
19
|
+
public
|
20
|
+
|
21
|
+
# The Hoodoo::Services::Session instance describing the authorised call
|
22
|
+
# context. If a resource implementation is handling a public action this
|
23
|
+
# may be +nil+, else it will be a valid instance.
|
24
|
+
#
|
25
|
+
attr_reader :session
|
26
|
+
|
27
|
+
# The Hoodoo::Services::Request instance giving details about the
|
28
|
+
# inbound request. Relevant information will depend upon the endpoint
|
29
|
+
# service implementation action being addressed.
|
30
|
+
#
|
31
|
+
attr_reader :request
|
32
|
+
|
33
|
+
# The Hoodoo::Services::Response instance that a service implementation
|
34
|
+
# updates with results of its processing.
|
35
|
+
#
|
36
|
+
attr_reader :response
|
37
|
+
|
38
|
+
# The Hoodoo::Services::Middleware::Interaction instance for which this
|
39
|
+
# context exists (the 'owning' instance). Generally speaking this is
|
40
|
+
# only needed internally as part of the inter-resource call mechanism.
|
41
|
+
#
|
42
|
+
attr_reader :owning_interaction
|
43
|
+
|
44
|
+
# Create a new instance. There is almost certainly never any need to
|
45
|
+
# call this unless you're the Hoodoo::Services::Middleware::Interaction
|
46
|
+
# constructor! If you want to build a context for (say) test purposes,
|
47
|
+
# it's probably best to construct an interaction instance and use the
|
48
|
+
# context instance this provides.
|
49
|
+
#
|
50
|
+
# +session+:: See #session.
|
51
|
+
# +request+:: See #request.
|
52
|
+
# +response+:: See #response.
|
53
|
+
# +owning_interaction+:: See #interaction.
|
54
|
+
#
|
55
|
+
def initialize( session, request, response, owning_interaction )
|
56
|
+
@session = session
|
57
|
+
@request = request
|
58
|
+
@response = response
|
59
|
+
@owning_interaction = owning_interaction
|
60
|
+
end
|
61
|
+
|
62
|
+
# Request (and lazy-initialize) a new resource endpoint instance for
|
63
|
+
# talking to a resource's interface. See Hoodoo::Client::Endpoint.
|
64
|
+
#
|
65
|
+
# You can request an endpoint for any resource name, whether or not an
|
66
|
+
# implementation actually exists for it. Until you try and talk to the
|
67
|
+
# interface through the endpoint instance, you won't know if it is
|
68
|
+
# there. All endpoint methods return instances of classes that mix in
|
69
|
+
# Hoodoo::Client::AugmentedBase; these
|
70
|
+
# mixin methods provide error handling options to detect a "not found"
|
71
|
+
# error (equivanent to HTTP status code 404) returned when a resource
|
72
|
+
# implementation turns out to not actually be present.
|
73
|
+
#
|
74
|
+
# The idiomatic call sequence is something like the following, where
|
75
|
+
# you get hold of an endpoint, make a call and handle the response:
|
76
|
+
#
|
77
|
+
# clock = context.resource( :Clock, 2 ) # v2 of 'Clock' resource
|
78
|
+
# time = clock.show( 'now' )
|
79
|
+
#
|
80
|
+
# return if time.adds_errors_to?( context.response.errors )
|
81
|
+
#
|
82
|
+
# ...or alternatively:
|
83
|
+
#
|
84
|
+
# clock = context.resource( :Clock, 2 ) # v2 of 'Clock' resource
|
85
|
+
# time = clock.show( 'now' )
|
86
|
+
#
|
87
|
+
# context.response.add_errors( time.platform_errors )
|
88
|
+
# return if context.response.halt_processing?
|
89
|
+
#
|
90
|
+
# The return value of calls made to the endpoint is an Array or Hash
|
91
|
+
# that mixes in Hoodoo::Client::AugmentedBase;
|
92
|
+
# see this class's documentation for details of the two alternative
|
93
|
+
# error handling approaches shown above.
|
94
|
+
#
|
95
|
+
# +resource+:: Resource name for the endpoint, e.g. +:Purchase+. String
|
96
|
+
# or symbol.
|
97
|
+
#
|
98
|
+
# +version+:: Optional required implemented version for the endpoint,
|
99
|
+
# as an Integer - defaults to 1.
|
100
|
+
#
|
101
|
+
# +options+:: Optional options Hash (see below).
|
102
|
+
#
|
103
|
+
# The options Hash key/values are as follows:
|
104
|
+
#
|
105
|
+
# +locale+:: Locale string for request/response, e.g. "en-gb". Optional.
|
106
|
+
# If omitted, defaults to the locale set in this Client
|
107
|
+
# instance's constructor.
|
108
|
+
#
|
109
|
+
# Others:: See Hoodoo::Client::Headers' +HEADER_TO_PROPERTY+.
|
110
|
+
# For any options in that map which describe themselves as
|
111
|
+
# being automatically transferred from one endpoint to
|
112
|
+
# another, you can prevent this by explicitly pasisng a
|
113
|
+
# +nil+ value for the option; otherwise, _OMIT_ the option
|
114
|
+
# for normal behaviour. Non-auto-transfer properties can be
|
115
|
+
# specified as +nil+ or omitted with no change in behaviour.
|
116
|
+
#
|
117
|
+
def resource( resource, version = 1, options = {} )
|
118
|
+
middleware = @owning_interaction.owning_middleware_instance
|
119
|
+
endpoint = middleware.inter_resource_endpoint_for(
|
120
|
+
resource,
|
121
|
+
version,
|
122
|
+
@owning_interaction
|
123
|
+
)
|
124
|
+
|
125
|
+
endpoint.locale = options[ :locale ] unless options[ :locale ].nil?
|
126
|
+
|
127
|
+
Hoodoo::Client::Headers::HEADER_TO_PROPERTY.each do | rack_header, description |
|
128
|
+
property = description[ :property ]
|
129
|
+
property_writer = description[ :property_writer ]
|
130
|
+
auto_transfer = description[ :auto_transfer ]
|
131
|
+
|
132
|
+
# For automatically transferred options there's no way to stop the
|
133
|
+
# auto transfer unless explicitly stating 'nil' to overwrite any
|
134
|
+
# existing value, so here, only write the value into the endpoint if
|
135
|
+
# the property specifically exists in the inbound options hash.
|
136
|
+
#
|
137
|
+
# For other properties, 'nil' has no meaning and there's no need to
|
138
|
+
# override anything, so use "unless nil?" in that case.
|
139
|
+
|
140
|
+
value = options[ property ]
|
141
|
+
|
142
|
+
if auto_transfer == true
|
143
|
+
endpoint.send( property_writer, value ) if options.has_key?( property )
|
144
|
+
else
|
145
|
+
endpoint.send( property_writer, value ) unless value.nil?
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
return endpoint
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
end; end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: implementation.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: Service authors create subclasses of
|
6
|
+
# Hoodoo::Services::Service, which lists the one or more
|
7
|
+
# subclasses of Hoodoo::Services::Interface the service author
|
8
|
+
# writes; each of those declares an interface which refers,
|
9
|
+
# for each interface endpoint, to a subclass of the class
|
10
|
+
# described here. Service authors create the body of their
|
11
|
+
# service implementation within the subclass.
|
12
|
+
#
|
13
|
+
# This file, then, does very little beyond describing the
|
14
|
+
# method framework that service authors use.
|
15
|
+
# ----------------------------------------------------------------------
|
16
|
+
# 24-Sep-2014 (ADH): Created.
|
17
|
+
########################################################################
|
18
|
+
|
19
|
+
module Hoodoo; module Services
|
20
|
+
|
21
|
+
# Service authors subclass this to produce the body of their service
|
22
|
+
# interface implementation. It defines a series of methods that must be
|
23
|
+
# implemented in order to service requests.
|
24
|
+
#
|
25
|
+
# A Hoodoo::Services::Implementation subclass is selected by the platform
|
26
|
+
# middleware because a Hoodoo::Services::Interface subclass tells it about
|
27
|
+
# the implementation class through the Hoodoo::Services::Interface::interface
|
28
|
+
# DSL; the interface class is referenced from an
|
29
|
+
# Hoodoo::Services::Service subclass through the
|
30
|
+
# Hoodoo::Services::Service::comprised_of DSL; and the application class
|
31
|
+
# is run by Rack by being passed to a call to +run+ in +config.ru+.
|
32
|
+
#
|
33
|
+
class Implementation
|
34
|
+
|
35
|
+
# Implement a "list" action (paginated, sorted list of resources).
|
36
|
+
#
|
37
|
+
# +context+:: Hoodoo::Services::Context instance describing authorised
|
38
|
+
# session information, inbound request information and holding
|
39
|
+
# the response object that the service updates with the results
|
40
|
+
# of its processing of this action.
|
41
|
+
#
|
42
|
+
def list( context )
|
43
|
+
raise "Hoodoo::Services::Implementation subclasses must implement 'list'"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Implement a "show" action (represent one existing resource instance).
|
47
|
+
#
|
48
|
+
# +context+:: Hoodoo::Services::Context instance describing authorised
|
49
|
+
# session information, inbound request information and holding
|
50
|
+
# the response object that the service updates with the results
|
51
|
+
# of its processing of this action.
|
52
|
+
#
|
53
|
+
def show( context )
|
54
|
+
raise "Hoodoo::Services::Implementation subclasses must implement 'show'"
|
55
|
+
end
|
56
|
+
|
57
|
+
# Implement a "create" action (store one new resource instance).
|
58
|
+
#
|
59
|
+
# +context+:: Hoodoo::Services::Context instance describing authorised
|
60
|
+
# session information, inbound request information and holding
|
61
|
+
# the response object that the service updates with the results
|
62
|
+
# of its processing of this action.
|
63
|
+
#
|
64
|
+
def create( context )
|
65
|
+
raise "Hoodoo::Services::Implementation subclasses must implement 'create'"
|
66
|
+
end
|
67
|
+
|
68
|
+
# Implement a "update" action (modify one existing resource instance).
|
69
|
+
#
|
70
|
+
# +context+:: Hoodoo::Services::Context instance describing authorised
|
71
|
+
# session information, inbound request information and holding
|
72
|
+
# the response object that the service updates with the results
|
73
|
+
# of its processing of this action.
|
74
|
+
#
|
75
|
+
def update( context )
|
76
|
+
raise "Hoodoo::Services::Implementation subclasses must implement 'update'"
|
77
|
+
end
|
78
|
+
|
79
|
+
# Implement a "delete" action (delete one existing resource instance).
|
80
|
+
#
|
81
|
+
# +context+:: Hoodoo::Services::Context instance describing authorised
|
82
|
+
# session information, inbound request information and holding
|
83
|
+
# the response object that the service updates with the results
|
84
|
+
# of its processing of this action.
|
85
|
+
#
|
86
|
+
def delete( context )
|
87
|
+
raise "Hoodoo::Services::Implementation subclasses must implement 'delete'"
|
88
|
+
end
|
89
|
+
|
90
|
+
# Optional verification to allow or deny authorisation for a particular
|
91
|
+
# action on a call-by-call basis.
|
92
|
+
#
|
93
|
+
# The middleware calls this method if a session
|
94
|
+
# (Hoodoo::Services::Session) has associated permissions
|
95
|
+
# (Hoodoo::Services::Permissions) which say that the resource's
|
96
|
+
# implementation should be asked via constant
|
97
|
+
# (Hoodoo::Services::Permissions::ASK).
|
98
|
+
#
|
99
|
+
# +context+:: Hoodoo::Services::Context instance as for action methods
|
100
|
+
# such as #show, #list and so forth.
|
101
|
+
#
|
102
|
+
# +action+:: The action that the caller is trying to perform, as a
|
103
|
+
# Symbol from the list in
|
104
|
+
# Hoodoo::Services::Middleware::ALLOWED_ACTIONS.
|
105
|
+
#
|
106
|
+
# Your implementation *MUST* return either
|
107
|
+
# Hoodoo::Services::Permissions::ALLOW, to allow the action, or
|
108
|
+
# Hoodoo::Services::Permissions::DENY, to block the action.
|
109
|
+
#
|
110
|
+
# * If a session's permissions indicate that a resource endpoint should
|
111
|
+
# be asked, but that interface does not define its own #verify method,
|
112
|
+
# then the default implementation herein will _deny_ the request.
|
113
|
+
#
|
114
|
+
# * If a buggy verification method returns an unexpected value, the
|
115
|
+
# middleware will ignore it and again _deny_ the request.
|
116
|
+
#
|
117
|
+
# Whether or not any of your implementations ever need to write a custom
|
118
|
+
# verification method will depend entirely upon your API, whether or not
|
119
|
+
# it has a meaningful definition of per-request assessment to allow or
|
120
|
+
# deny access and whether or not any sessions can exist with an 'ask'
|
121
|
+
# permission inside in the first place. If using the Hoodoo authorisation
|
122
|
+
# and authentication mechanism, this would come down to whether or not
|
123
|
+
# any Hoodoo::Data::Resources::Caller instances existed with the
|
124
|
+
# relevant permission value defined somewhere inside.
|
125
|
+
#
|
126
|
+
def verify( context, action )
|
127
|
+
return Hoodoo::Services::Permissions::DENY
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
end; end
|
@@ -0,0 +1,934 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: interface.rb
|
3
|
+
# (C):: Loyalty New Zealand 2014
|
4
|
+
#
|
5
|
+
# Purpose:: Define a class (and some namespace-nested related support
|
6
|
+
# classes) that are subclassed by service authors and used to
|
7
|
+
# declare the nature of the interface the service implements
|
8
|
+
# via a small DSL.
|
9
|
+
# ----------------------------------------------------------------------
|
10
|
+
# 23-Sep-2014 (ADH): Created.
|
11
|
+
########################################################################
|
12
|
+
|
13
|
+
require 'set'
|
14
|
+
|
15
|
+
module Hoodoo; module Services
|
16
|
+
|
17
|
+
# Service implementation authors subclass this to describe the interface that
|
18
|
+
# they implement for a particular Resource, as documented in the Loyalty
|
19
|
+
# Platform API.
|
20
|
+
#
|
21
|
+
# See class method ::interface for details.
|
22
|
+
#
|
23
|
+
class Interface
|
24
|
+
|
25
|
+
###########################################################################
|
26
|
+
|
27
|
+
# A class containing a series of accessors that describe allowed parameters
|
28
|
+
# in a "list" call for a service implementation. The middleware uses this
|
29
|
+
# validate incoming query strings for lists and reject requests that ask
|
30
|
+
# for unsupported things. When instantiated the class sets itself up with
|
31
|
+
# defaults that match those described by the your platform's API. When
|
32
|
+
# passed to a Hoodoo::Services::Interface::ToListDSL instance, the DSL
|
33
|
+
# methods, if called, update the values stored herein.
|
34
|
+
#
|
35
|
+
class ToList
|
36
|
+
|
37
|
+
# Limit value; an integer that limits page size in lists.
|
38
|
+
#
|
39
|
+
attr_reader :limit
|
40
|
+
|
41
|
+
# Sort hash. Keys are supported sort fields, values are arrays of
|
42
|
+
# supported sort directions. The first array entry is the default sort
|
43
|
+
# order for the sort field.
|
44
|
+
#
|
45
|
+
attr_reader :sort
|
46
|
+
|
47
|
+
# Default sort key.
|
48
|
+
#
|
49
|
+
attr_reader :default_sort_key
|
50
|
+
|
51
|
+
# Default sort direction.
|
52
|
+
#
|
53
|
+
def default_sort_direction
|
54
|
+
@sort[ default_sort_key() ].first
|
55
|
+
end
|
56
|
+
|
57
|
+
# Array of supported search keys as Strings; empty for none defined.
|
58
|
+
#
|
59
|
+
attr_reader :search
|
60
|
+
|
61
|
+
# Array of supported filter keys as Strings; empty for none defined.
|
62
|
+
#
|
63
|
+
attr_reader :filter
|
64
|
+
|
65
|
+
# Create an instance with default settings.
|
66
|
+
#
|
67
|
+
def initialize
|
68
|
+
|
69
|
+
# Remember, these are defaults for the "to_list" object of an
|
70
|
+
# interface only. For interface-wide top level defaults, use the
|
71
|
+
# embedded calls to the DSL in Interface::interface.
|
72
|
+
|
73
|
+
@limit = 50
|
74
|
+
@sort = { 'created_at' => Set.new( [ 'desc', 'asc' ] ) }
|
75
|
+
@default_sort_key = 'created_at'
|
76
|
+
@search = []
|
77
|
+
@filter = []
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# Private writer - see #limit - but there's a special contract with
|
83
|
+
# Hoodoo::Services::Interface::ToListDSL which permits it to call here
|
84
|
+
# bypassing +private+ via +send()+.
|
85
|
+
#
|
86
|
+
attr_writer :limit
|
87
|
+
|
88
|
+
# Private writer - see #sort - but there's a special contract with
|
89
|
+
# Hoodoo::Services::Interface::ToListDSL which permits it to call here
|
90
|
+
# bypassing +private+ via +send()+.
|
91
|
+
#
|
92
|
+
attr_writer :sort
|
93
|
+
|
94
|
+
# Private writer - see #default_sort_key - but there's a special
|
95
|
+
# contract with Hoodoo::Services::Interface::ToListDSL which permits it
|
96
|
+
# to call here bypassing +private+ via +send()+.
|
97
|
+
#
|
98
|
+
attr_writer :default_sort_key
|
99
|
+
|
100
|
+
# Private writer - see #search - but there's a special contract with
|
101
|
+
# Hoodoo::Services::Interface::ToListDSL which permits it to call here
|
102
|
+
# bypassing +private+ via +send()+.
|
103
|
+
#
|
104
|
+
attr_writer :search
|
105
|
+
|
106
|
+
# Private writer - see #filter - but there's a special contract with
|
107
|
+
# Hoodoo::Services::Interface::ToListDSL which permits it to call here
|
108
|
+
# bypassing +private+ via +send()+.
|
109
|
+
#
|
110
|
+
attr_writer :filter
|
111
|
+
|
112
|
+
end # 'class ToList'
|
113
|
+
|
114
|
+
###########################################################################
|
115
|
+
|
116
|
+
# Implementation of the DSL that's written inside a block passed to
|
117
|
+
# Hoodoo::Services::Interface#to_list. This is an internal implementation
|
118
|
+
# class. Instantiate with a Hoodoo::Services::Interface::ToList instance,
|
119
|
+
# the data in which is updated as the DSL methods run.
|
120
|
+
#
|
121
|
+
class ToListDSL
|
122
|
+
|
123
|
+
# Initialize an instance and run the DSL methods.
|
124
|
+
#
|
125
|
+
# +hoodoo_interface_to_list_instance+:: Instance of
|
126
|
+
# Hoodoo::Services::Interface::ToList
|
127
|
+
# to update with data
|
128
|
+
# from DSL method calls.
|
129
|
+
#
|
130
|
+
# &block:: Block of code that makes calls to the DSL herein.
|
131
|
+
#
|
132
|
+
# On exit, the DSL is run and the Hoodoo::Services::Interface::ToList has
|
133
|
+
# been updated.
|
134
|
+
#
|
135
|
+
def initialize( hoodoo_interface_to_list_instance, &block )
|
136
|
+
@tl = hoodoo_interface_to_list_instance # Shorthand!
|
137
|
+
|
138
|
+
unless @tl.instance_of?( Hoodoo::Services::Interface::ToList )
|
139
|
+
raise "Hoodoo::Services::Interface::ToListDSL\#initialize requires a Hoodoo::Services::Interface::ToList instance - got '#{ @tl.class }'"
|
140
|
+
end
|
141
|
+
|
142
|
+
self.instance_eval( &block )
|
143
|
+
end
|
144
|
+
|
145
|
+
# Specify the page size (limit) for lists.
|
146
|
+
#
|
147
|
+
# +limit+:: Page size (integer).
|
148
|
+
#
|
149
|
+
# Example:
|
150
|
+
#
|
151
|
+
# limit 100
|
152
|
+
#
|
153
|
+
def limit( limit )
|
154
|
+
unless limit.is_a?( ::Integer )
|
155
|
+
raise "Hoodoo::Services::Interface::ToListDSL\#limit requires an Integer - got '#{ limit.class }'"
|
156
|
+
end
|
157
|
+
|
158
|
+
@tl.send( :limit=, limit )
|
159
|
+
end
|
160
|
+
|
161
|
+
# Specify extra sort keys and orders that add with whatever platform
|
162
|
+
# common defaults are already in place.
|
163
|
+
#
|
164
|
+
# +sort+:: Hash of sort keys, with values that are an array of supported
|
165
|
+
# sort directions. The first array entry is used as the default
|
166
|
+
# direction if no direction is specified in the client caller's
|
167
|
+
# query string. Use strings or symbols.
|
168
|
+
#
|
169
|
+
# To specify that a sort key should be the new default for the
|
170
|
+
# interface in question, wrap it in a call to the #default
|
171
|
+
# DSL method.
|
172
|
+
#
|
173
|
+
# Example - add sort key '+code+' with directions +:asc+ and +:desc+,
|
174
|
+
# plus sort key +:member+ which only supports direction +:asc+.
|
175
|
+
#
|
176
|
+
# sort :code => [ :asc, :desc ],
|
177
|
+
# :member => [ :asc ]
|
178
|
+
#
|
179
|
+
def sort( sort )
|
180
|
+
unless sort.is_a?( ::Hash )
|
181
|
+
raise "Hoodoo::Services::Interface::ToListDSL\#sort requires a Hash - got '#{ sort.class }'"
|
182
|
+
end
|
183
|
+
|
184
|
+
# Convert Hash keys to Strings and Arrays to Sets of Strings too.
|
185
|
+
|
186
|
+
sort = sort.inject( {} ) do | memo, ( k, v ) |
|
187
|
+
memo[ k.to_s ] = Set.new( v.map do | entry |
|
188
|
+
entry.to_s
|
189
|
+
end )
|
190
|
+
memo
|
191
|
+
end
|
192
|
+
|
193
|
+
merged = @tl.sort().merge( sort )
|
194
|
+
@tl.send( :sort=, merged )
|
195
|
+
end
|
196
|
+
|
197
|
+
# Used in conjunction with #sort. Specifies that a sort key should be
|
198
|
+
# the default sort order for the interface.
|
199
|
+
#
|
200
|
+
# Example - add sort key '+code+' with directions +:asc+ and +:desc+,
|
201
|
+
# plus sort key +:member+ which only supports direction +:asc+. Say that
|
202
|
+
# '+code+' is to be the default sort order.
|
203
|
+
#
|
204
|
+
# sort default( :code ) => [ :asc, :desc ],
|
205
|
+
# :member => [ :asc ]
|
206
|
+
#
|
207
|
+
def default( sort_key )
|
208
|
+
unless sort_key.is_a?( ::String ) || sort_key.is_a?( ::Symbol )
|
209
|
+
raise "Hoodoo::Services::Interface::ToListDSL\#default requires a String or Symbol - got '#{ sort_key.class }'"
|
210
|
+
end
|
211
|
+
|
212
|
+
@tl.send( :default_sort_key=, sort_key.to_s )
|
213
|
+
return sort_key
|
214
|
+
end
|
215
|
+
|
216
|
+
# Specify supported search keys in an array. The middleware will make
|
217
|
+
# sure the interface implementation is only called with search keys in
|
218
|
+
# that list. If a client attempts a search on an unsupported key, their
|
219
|
+
# request will be rejected by the middleware.
|
220
|
+
#
|
221
|
+
# If a service wants to do its own search validation, it should not list
|
222
|
+
# call here. Note also that only the keys are specified and validated;
|
223
|
+
# value escaping and validation, if necessary, is up to the service
|
224
|
+
# implementation.
|
225
|
+
#
|
226
|
+
# +search+:: Array of permitted search keys, as symbols or strings.
|
227
|
+
# The order of array entries is arbitrary.
|
228
|
+
#
|
229
|
+
# Example - allow searches specifying +first_name+ and +last_name+ keys:
|
230
|
+
#
|
231
|
+
# search :first_name, :last_name
|
232
|
+
#
|
233
|
+
def search( *search )
|
234
|
+
@tl.send( :search=, search.map { | item | item.to_s } )
|
235
|
+
end
|
236
|
+
|
237
|
+
# As #search, but for filtering.
|
238
|
+
#
|
239
|
+
# +filter+:: Array of permitted filter keys, as symbols or strings.
|
240
|
+
# The order of array entries is arbitrary.
|
241
|
+
#
|
242
|
+
def filter( *filter )
|
243
|
+
@tl.send( :filter=, filter.map { | item | item.to_s } )
|
244
|
+
end
|
245
|
+
end # 'class ToListDSL'
|
246
|
+
|
247
|
+
###########################################################################
|
248
|
+
|
249
|
+
# Mandatory part of the interface DSL. Declare the interface's URL endpoint
|
250
|
+
# and the Hoodoo::Services::Implementation subclass to be invoked when
|
251
|
+
# client requests are sent to a URL matching the endpoint.
|
252
|
+
#
|
253
|
+
# No two interfaces can use the same endpoint within a service application,
|
254
|
+
# unless the describe a different interface version - see #version.
|
255
|
+
#
|
256
|
+
# Example:
|
257
|
+
#
|
258
|
+
# endpoint :estimations, PurchaseImplementation
|
259
|
+
#
|
260
|
+
# +uri_path_fragment+:: Path fragment to match at the start of a URL path,
|
261
|
+
# as a symbol or string, excluding leading "/". The
|
262
|
+
# URL path matches the fragment if the path starts
|
263
|
+
# with a "/", then matches the fragment exactly, then
|
264
|
+
# is followed by either ".", another "/", or the
|
265
|
+
# end of the path string. For example, a fragment of
|
266
|
+
# +:products+ matches all paths out of +/products+,
|
267
|
+
# +/products.json+ or +/products/22+, but does not
|
268
|
+
# match +/products_and_things+.
|
269
|
+
#
|
270
|
+
# +implementation_class+:: The Hoodoo::Services::Implementation subclass
|
271
|
+
# (the class itself, not an instance of it) that
|
272
|
+
# should be used when a request matching the
|
273
|
+
# path fragment is received.
|
274
|
+
#
|
275
|
+
def endpoint( uri_path_fragment, implementation_class )
|
276
|
+
|
277
|
+
# http://www.ruby-doc.org/core-2.2.3/Module.html#method-i-3C
|
278
|
+
#
|
279
|
+
unless implementation_class < Hoodoo::Services::Implementation
|
280
|
+
raise "Hoodoo::Services::Interface#endpoint must provide Hoodoo::Services::Implementation subclasses, but '#{ implementation_class }' was given instead"
|
281
|
+
end
|
282
|
+
|
283
|
+
self.class.send( :endpoint=, uri_path_fragment )
|
284
|
+
self.class.send( :implementation=, implementation_class )
|
285
|
+
end
|
286
|
+
|
287
|
+
# Declare the _major_ version of the interface being implemented. All
|
288
|
+
# service endpoints appear at "/v{version}/{endpoint}" relative to whatever
|
289
|
+
# root an edge layer defines. If a service interface does not specifiy its
|
290
|
+
# version, +1+ is assumed.
|
291
|
+
#
|
292
|
+
# Two interfaces can exist on the same endpoint provided their versions are
|
293
|
+
# different since the resulting route to reach them will be different too.
|
294
|
+
#
|
295
|
+
# +version+:: Integer major version number, e.g +2+.
|
296
|
+
#
|
297
|
+
def version( major_version )
|
298
|
+
self.class.send( :version=, major_version.to_s.to_i )
|
299
|
+
end
|
300
|
+
|
301
|
+
# List the actions that the service implementation supports. If you don't
|
302
|
+
# call this, the middleware assumes that all actions are available; else it
|
303
|
+
# only calls for supported actions. If you declared an empty array, your
|
304
|
+
# implementation would never be called.
|
305
|
+
#
|
306
|
+
# *supported_actions:: One or more from +:list+, +:show+, +:create+,
|
307
|
+
# +:update+ and +:delete+. Always use symbols, not
|
308
|
+
# strings. An exception is raised if unrecognised
|
309
|
+
# actions are given.
|
310
|
+
#
|
311
|
+
# Example:
|
312
|
+
#
|
313
|
+
# actions :list, :show
|
314
|
+
#
|
315
|
+
def actions( *supported_actions )
|
316
|
+
supported_actions.map! { | item | item.to_sym }
|
317
|
+
invalid = supported_actions - Hoodoo::Services::Middleware::ALLOWED_ACTIONS
|
318
|
+
|
319
|
+
unless invalid.empty?
|
320
|
+
raise "Hoodoo::Services::Interface#actions does not recognise one or more actions: '#{ invalid.join( ', ' ) }'"
|
321
|
+
end
|
322
|
+
|
323
|
+
self.class.send( :actions=, Set.new( supported_actions ) )
|
324
|
+
end
|
325
|
+
|
326
|
+
# List any actions which are public - NOT PROTECTED BY SESSIONS. For
|
327
|
+
# public actions, no X-Session-ID or similar header is consulted and
|
328
|
+
# no session data will be associated with your
|
329
|
+
# Hoodoo::Services::Context instance when action methods are called.
|
330
|
+
#
|
331
|
+
# Use with great care!
|
332
|
+
#
|
333
|
+
# Note that if the implementation of a public action needs to call
|
334
|
+
# other resources, it can only ever call them if those actions in
|
335
|
+
# those other resources are also public. The implementation of a
|
336
|
+
# public action is prohibited from making calls to protected actions
|
337
|
+
# in other resources.
|
338
|
+
#
|
339
|
+
# *public_actions:: One or more from +:list+, +:show+, +:create+,
|
340
|
+
# +:update+ and +:delete+. Always use symbols, not
|
341
|
+
# strings. An exception is raised if unrecognised
|
342
|
+
# actions are given.
|
343
|
+
#
|
344
|
+
def public_actions( *public_actions )
|
345
|
+
public_actions.map! { | item | item.to_sym }
|
346
|
+
invalid = public_actions - Hoodoo::Services::Middleware::ALLOWED_ACTIONS
|
347
|
+
|
348
|
+
unless invalid.empty?
|
349
|
+
raise "Hoodoo::Services::Interface#public_actions does not recognise one or more actions: '#{ invalid.join( ', ' ) }'"
|
350
|
+
end
|
351
|
+
|
352
|
+
self.class.send( :public_actions=, Set.new( public_actions ) )
|
353
|
+
end
|
354
|
+
|
355
|
+
# Set secure log actions.
|
356
|
+
#
|
357
|
+
# +secure_log_actions+:: A Hash, described below.
|
358
|
+
#
|
359
|
+
# The given Hash keys are names of actions as Symbols: +:list+,
|
360
|
+
# +:show+, +:create+, +:update+ or +:delete+. Values are +:request+,
|
361
|
+
# +:response+ or +:both+. For a given action targeted at this resource:
|
362
|
+
#
|
363
|
+
# * A key of +:request+ means that API call-related Hoodoo automatic
|
364
|
+
# logging will _exclude_ body data for the _inbound_ _request_, but
|
365
|
+
# still include body data in the response. Example: A POST to a Login
|
366
|
+
# resource includes a password which you don't want logged, but the
|
367
|
+
# response data doesn't quote the password back so is "safe". The
|
368
|
+
# secure log actions Hash for the Login resource's interface would
|
369
|
+
# include <tt>:create => :request</tt>.
|
370
|
+
#
|
371
|
+
# * A key of +:response+ means that API call-related Hoodoo automatic
|
372
|
+
# logging will _exclude_ body data for the _outbound_ _response_,
|
373
|
+
# but still include body data in the request. Example: A POST to a
|
374
|
+
# Caller resource creates a Caller with a generated authentication
|
375
|
+
# secret that's only exposed in the POST's response. The inbound
|
376
|
+
# data used to create that Caller can be safely logged, but the
|
377
|
+
# authentication secret is sensitive and shouldn't be recorded. The
|
378
|
+
# secure log actions Hash for the Caller resource's interface would
|
379
|
+
# include <tt>:create => :response</tt>.
|
380
|
+
#
|
381
|
+
# _ERROR_ _RESPONSES_ _ARE_ _STILL_ _LOGGED_ because that's useful data;
|
382
|
+
# so make sure that if you generate any custom errors in your service
|
383
|
+
# that secure data is not contained within them.
|
384
|
+
#
|
385
|
+
# * A key of +both+ has the same result as both +:request+ and
|
386
|
+
# +:response+, so body data is never logged. It's hard to come up
|
387
|
+
# with good examples of resources where both the incoming data is
|
388
|
+
# sensitive and the outgoing data is sensitive but the option is
|
389
|
+
# included for competion, as someone out there will need it.
|
390
|
+
#
|
391
|
+
# Example: The request body data sent by a caller into a resource's
|
392
|
+
# +:create+ action will not be logged:
|
393
|
+
#
|
394
|
+
# secure_log_for( { :create => :request } )
|
395
|
+
#
|
396
|
+
# Example: Neither the request data sent by a caller, nor the
|
397
|
+
# response data sent back, will be logged for an +:update+ action:
|
398
|
+
#
|
399
|
+
# secure_log_for( { :update => :both } )
|
400
|
+
#
|
401
|
+
# The default is an empty Hash; all actions have both inbound request
|
402
|
+
# body data and outbound response body data logged by Hoodoo.
|
403
|
+
#
|
404
|
+
def secure_log_for( secure_log_actions = {} )
|
405
|
+
secure_log_actions = Hoodoo::Utilities.symbolize( secure_log_actions )
|
406
|
+
invalid = secure_log_actions.keys - Hoodoo::Services::Middleware::ALLOWED_ACTIONS
|
407
|
+
|
408
|
+
unless invalid.empty?
|
409
|
+
raise "Hoodoo::Services::Interface#secure_log_for does not recognise one or more actions: '#{ invalid.join( ', ' ) }'"
|
410
|
+
end
|
411
|
+
|
412
|
+
self.class.send( :secure_log_for=, secure_log_actions )
|
413
|
+
end
|
414
|
+
|
415
|
+
# An array of supported embed keys (as per documentation, so singular or
|
416
|
+
# plural as per resource interface descriptions in the Loyalty Platform
|
417
|
+
# API). Things which can be embedded can also be referenced, via the
|
418
|
+
# <tt>_embed</tt> and <tt>_reference</tt> query string keys.
|
419
|
+
#
|
420
|
+
# The middleware uses the list to reject requests from clients which
|
421
|
+
# ask for embedded or referenced entities that were not listed by the
|
422
|
+
# interface. If you don't call here, or call here with an empty array,
|
423
|
+
# no embedding or referencing will be allowed for calls to the service
|
424
|
+
# implementation.
|
425
|
+
#
|
426
|
+
# +embed+:: Array of permitted embeddable entity names, as symbols or
|
427
|
+
# strings. The order of array entries is arbitrary.
|
428
|
+
#
|
429
|
+
# Example: An interface permits lists that request embedding or
|
430
|
+
# referencing of "vouchers", "balances" and "member":
|
431
|
+
#
|
432
|
+
# embed :vouchers, :balances, :member
|
433
|
+
#
|
434
|
+
# As a result, #embeds would return:
|
435
|
+
#
|
436
|
+
# [ 'vouchers', 'balances', 'member' ]
|
437
|
+
#
|
438
|
+
def embeds( *embeds )
|
439
|
+
self.class.send( :embeds=, embeds.map { | item | item.to_s } )
|
440
|
+
end
|
441
|
+
|
442
|
+
# Specify parameters related to common index parameters. The block contains
|
443
|
+
# calls to the DSL described by Hoodoo::Services::Interface::ToListDSL. The
|
444
|
+
# default values should be described by your platform's API - hard-coded at
|
445
|
+
# the time of writing as:
|
446
|
+
#
|
447
|
+
# limit 50
|
448
|
+
# sort :created_at => [ :desc, :asc ]
|
449
|
+
# search nil
|
450
|
+
# filter nil
|
451
|
+
#
|
452
|
+
def to_list( &block )
|
453
|
+
Hoodoo::Services::Interface::ToListDSL.new(
|
454
|
+
self.class.instance_variable_get( '@to_list' ),
|
455
|
+
&block
|
456
|
+
)
|
457
|
+
end
|
458
|
+
|
459
|
+
# Optional description of the JSON parameters (schema) that the interface's
|
460
|
+
# implementation requires for calls creating resource instances. The block
|
461
|
+
# uses the DSL from Hoodoo::Presenters::Object, so you can specify
|
462
|
+
# basic object things like +string+, or higher level things like +type+ or
|
463
|
+
# +resource+.
|
464
|
+
#
|
465
|
+
# If a call comes into the middleware from a client which contains body
|
466
|
+
# data that doesn't validate according to your schema, it'll be rejected
|
467
|
+
# before even getting as far as your interface implementation.
|
468
|
+
#
|
469
|
+
# Default values for fields where present are for _rendering_ _only_; they
|
470
|
+
# are not injected into the inbound body for (say) persistence at database
|
471
|
+
# levels. A returned, rendered representation based on the same schema
|
472
|
+
# would have the default values present only. If you need default values
|
473
|
+
# at the persistence layer too, define them there too with whatever
|
474
|
+
# mechanism is most appropriate for your chosen persistence approach.
|
475
|
+
#
|
476
|
+
# The Hoodoo::Presenters::Object#internationalised DSL method can be
|
477
|
+
# called within your block harmlessly, but it has no side effects. Any
|
478
|
+
# resource interface that can take internationalised data for creation (or
|
479
|
+
# modification) must already have an internationalised representation, so
|
480
|
+
# the standard resources in the Hoodoo::Data::Resources collection will
|
481
|
+
# already have declared that internationalisation applies.
|
482
|
+
#
|
483
|
+
# Example 1:
|
484
|
+
#
|
485
|
+
# to_create do
|
486
|
+
# string :name, :length => 32, :required => true
|
487
|
+
# text :description
|
488
|
+
# end
|
489
|
+
#
|
490
|
+
# Example 2: With a resource
|
491
|
+
#
|
492
|
+
# to_create do
|
493
|
+
# resource Product # Fields are *inline*
|
494
|
+
# end
|
495
|
+
#
|
496
|
+
# &block:: Block, passed to Hoodoo::Presenters::Object, describing
|
497
|
+
# the fields used for resource creation.
|
498
|
+
#
|
499
|
+
def to_create( &block )
|
500
|
+
obj = Class.new( Hoodoo::Presenters::Base )
|
501
|
+
obj.schema( &block )
|
502
|
+
|
503
|
+
self.class.send( :to_create=, obj )
|
504
|
+
end
|
505
|
+
|
506
|
+
# As #to_create, but applies when modifying existing resource instances.
|
507
|
+
# To avoid repeating yourself, if your modification and creation parameter
|
508
|
+
# requirements are identical, call #update_same_as_create.
|
509
|
+
#
|
510
|
+
# The "required" flag is ignored for updates, because an omitted field for
|
511
|
+
# an update to an existing resource instance simply means "do not change
|
512
|
+
# the current value". As with #to_create, default values have relevance
|
513
|
+
# to the rendering stage only and have no effect here.
|
514
|
+
#
|
515
|
+
# &block:: Block, passed to Hoodoo::Presenters::Object, describing
|
516
|
+
# the fields used for resource modification.
|
517
|
+
#
|
518
|
+
def to_update( &block )
|
519
|
+
obj = Class.new( Hoodoo::Presenters::Base )
|
520
|
+
obj.schema( &block )
|
521
|
+
|
522
|
+
# When updating, 'required' fields in schema aren't required; you just
|
523
|
+
# omit a field to avoid changing its value. Walk the to-update schema
|
524
|
+
# graph stripping out any such problematic attributes.
|
525
|
+
#
|
526
|
+
obj.walk do | property |
|
527
|
+
property.required = false
|
528
|
+
end
|
529
|
+
|
530
|
+
self.class.send( :to_update=, obj )
|
531
|
+
end
|
532
|
+
|
533
|
+
# Declares that the expected JSON fields described in a #to_create call are
|
534
|
+
# the same as those required for modifying resources too.
|
535
|
+
#
|
536
|
+
# Example:
|
537
|
+
#
|
538
|
+
# update_same_as_create
|
539
|
+
#
|
540
|
+
# ...and that's all. There are no parameters or blocks needed.
|
541
|
+
#
|
542
|
+
def update_same_as_create
|
543
|
+
self.send( :to_update, & self.class.to_create().get_schema_definition() )
|
544
|
+
end
|
545
|
+
|
546
|
+
# Declares custom errors that are part of this defined interface. This
|
547
|
+
# calls directly through to Hoodoo::ErrorDescriptions#errors_for, so
|
548
|
+
# see that for details.
|
549
|
+
#
|
550
|
+
# A service should usually define only a single domain of error using one
|
551
|
+
# call to #errors_for, but techncially can make as many calls for as many
|
552
|
+
# domains as required. Definitions are merged.
|
553
|
+
#
|
554
|
+
# +domain+:: Domain, e.g. 'purchase', 'transaction' - see
|
555
|
+
# Hoodoo::ErrorDescriptions#errors_for for details.
|
556
|
+
#
|
557
|
+
# &block:: Code block making Hoodoo::ErrorDescriptions DSL calls.
|
558
|
+
#
|
559
|
+
# Example:
|
560
|
+
#
|
561
|
+
# errors_for 'transaction' do
|
562
|
+
# error 'duplicate_transaction', status: 409, message: 'Duplicate transaction', :required => [ :client_uid ]
|
563
|
+
# end
|
564
|
+
#
|
565
|
+
def errors_for( domain, &block )
|
566
|
+
descriptions = self.class.errors_for
|
567
|
+
|
568
|
+
if descriptions.nil?
|
569
|
+
descriptions = self.class.send( :errors_for=, Hoodoo::ErrorDescriptions.new )
|
570
|
+
end
|
571
|
+
|
572
|
+
descriptions.errors_for( domain, &block )
|
573
|
+
end
|
574
|
+
|
575
|
+
# Declare additional permissions that you require for a given action.
|
576
|
+
#
|
577
|
+
# If the implementation of a resource endpoint involves making calls out
|
578
|
+
# to other resources, then you need to consider how authorisation is
|
579
|
+
# granted to those other resources.
|
580
|
+
#
|
581
|
+
# The Hoodoo::Services::Session instance for the inbound external caller
|
582
|
+
# carries a Hoodoo::Services::Permission instance describing the actions
|
583
|
+
# that the caller is permitted to do. The middleware enforces these
|
584
|
+
# permissions, so that a resource implementation won't be called at all
|
585
|
+
# unless the caller has permission to do so.
|
586
|
+
#
|
587
|
+
# These permissions continue to apply during inter-resource calls. The
|
588
|
+
# wider session context is always applied. So, if one resource calls
|
589
|
+
# another resource, either:
|
590
|
+
#
|
591
|
+
# * The inbound API caller's session must have all necessary permissions
|
592
|
+
# for both the resource it is actually directly calling, and for any
|
593
|
+
# actions in any resources that the called resource in turn calls (and
|
594
|
+
# so-on, for any chain of resources).
|
595
|
+
#
|
596
|
+
# ...or...
|
597
|
+
#
|
598
|
+
# * The resource uses this +additional_permissions_for+ method to declare
|
599
|
+
# up-front that it will require the described permissions when a
|
600
|
+
# particular action is performed on it. When an inter-resource call is
|
601
|
+
# made, a temporary internal-only session is constructed that merges
|
602
|
+
# the permissions of the inbound caller with the additional permissions
|
603
|
+
# requested by the resource. The downstream called resource needs no
|
604
|
+
# special case code at all - it just sees a valid session with valid
|
605
|
+
# permissions and does what the upstream resource asked of it.
|
606
|
+
#
|
607
|
+
# For example, suppose a resource Clock returns both a time and a date,
|
608
|
+
# by calling out to the Time and Date resources. One option is that the
|
609
|
+
# inbound caller must have +show+ action permissions for all of Clock,
|
610
|
+
# Time and Date; if any of those are missing, then an attempt to call
|
611
|
+
# +show+ on the Clock resource would result in a 403 response.
|
612
|
+
#
|
613
|
+
# The other option is for Clock's interface to declare its requirements:
|
614
|
+
#
|
615
|
+
# additional_permissions_for( :show ) do | p |
|
616
|
+
# p.set_resource( :Time, :show, Hoodoo::Services::Permissions::ALLOW )
|
617
|
+
# p.set_resource( :Date, :show, Hoodoo::Services::Permissions::ALLOW )
|
618
|
+
# end
|
619
|
+
#
|
620
|
+
# Suppose you could create Clock instances for some reason, but there
|
621
|
+
# was an audit trail for this; Clock must create an Audit entry itself,
|
622
|
+
# but you don't want to expose this ability to external callers through
|
623
|
+
# their session permissions; so, just declare your additional
|
624
|
+
# permissions for that specific inter-service case:
|
625
|
+
#
|
626
|
+
# additional_permissions_for( :create ) do | p |
|
627
|
+
# p.set_resource( :Audit, :create, Hoodoo::Services::Permissions::ALLOW )
|
628
|
+
# end
|
629
|
+
#
|
630
|
+
# The call says which action in _the_ _declaring_ _interface's_ _resource_
|
631
|
+
# is a target. The block takes a single parameter; this is a default
|
632
|
+
# initialisation Hoodoo::Services::Permissions instance. Use that
|
633
|
+
# object's methods to set up whatever permissions you need in other
|
634
|
+
# resources, to successfully process the action in question. You only
|
635
|
+
# need to describe the resources you immediately call, not the whole
|
636
|
+
# chain - if "this" resource calls another, then it's up to the other
|
637
|
+
# resource to in turn describe additional permissions should it make its
|
638
|
+
# own set of downstream calls to further resource endpoints.
|
639
|
+
#
|
640
|
+
# Setting default permissions or especially the default permission
|
641
|
+
# fallback inside the block is possible but *VERY* *STRONGLY*
|
642
|
+
# *DISCOURAGED*. Instead, precisely describe the downstream resources,
|
643
|
+
# actions and permissions that are required.
|
644
|
+
#
|
645
|
+
# Note an important restriction - public actions (see ::public_actions)
|
646
|
+
# cannot be augmented in this way. A public action in one resource can
|
647
|
+
# only ever call public actions in other resources. This is because no
|
648
|
+
# session is needed _at_ _all_ to call a public action; calling into a
|
649
|
+
# protected action in another resource from this context would require
|
650
|
+
# invention of a full caller context which would be entirely invented
|
651
|
+
# and could represent an accidental (and significant) security hole.
|
652
|
+
#
|
653
|
+
# If you call this method for the same action more than once, the last
|
654
|
+
# call will be the one that takes effect - each call overwrites the
|
655
|
+
# results of any previous call made for the same action.
|
656
|
+
#
|
657
|
+
# Parameters are:
|
658
|
+
#
|
659
|
+
# +action+:: The action in this interface which will require the
|
660
|
+
# additional permissions to be described. Pass a Symbol or
|
661
|
+
# equivalent String from the list in
|
662
|
+
# Hoodoo::Services::Middleware::ALLOWED_ACTIONS.
|
663
|
+
#
|
664
|
+
# &block:: Block which is passed a new, default state
|
665
|
+
# Hoodoo::Services::Permissions instance; make method calls
|
666
|
+
# on this instance to describe the required permissions.
|
667
|
+
#
|
668
|
+
def additional_permissions_for( action, &block )
|
669
|
+
action = action.to_s
|
670
|
+
|
671
|
+
unless block_given?
|
672
|
+
raise 'Hoodoo::Services::Interface#additional_permissions_for must be passed a block'
|
673
|
+
end
|
674
|
+
|
675
|
+
p = Hoodoo::Services::Permissions.new
|
676
|
+
yield( p )
|
677
|
+
|
678
|
+
additional_permissions = self.class.additional_permissions() || {}
|
679
|
+
additional_permissions[ action ] = p
|
680
|
+
self.class.send( :additional_permissions=, additional_permissions )
|
681
|
+
end
|
682
|
+
|
683
|
+
protected
|
684
|
+
|
685
|
+
# Define the subclass Service's interface. A DSL is used with methods
|
686
|
+
# documented in the Hoodoo::Services::InterfaceDSL class.
|
687
|
+
#
|
688
|
+
# The absolute bare minimum interface description just states that a
|
689
|
+
# particular implementation class is used when requests are made to a
|
690
|
+
# particular URL endpoint, which is implementing an interface for a
|
691
|
+
# particular given resource. For a hypothetical Magic resource interface:
|
692
|
+
#
|
693
|
+
# class MagicImplementation < Hoodoo::Services::Implementation
|
694
|
+
# # ...implementation code goes here...
|
695
|
+
# end
|
696
|
+
#
|
697
|
+
# class MagicInterface < Hoodoo::Services::Interface
|
698
|
+
# interface :Magic do
|
699
|
+
# endpoint :paul_daniels, MagicImplementation
|
700
|
+
# end
|
701
|
+
# end
|
702
|
+
#
|
703
|
+
# This would cause all calls to URLs at '/paul_daniels[...]' to be routed to
|
704
|
+
# an instance of the MagicImplementation class.
|
705
|
+
#
|
706
|
+
# Addtional DSL facilities allow the interface to say what HTTP methods
|
707
|
+
# it supports (in terms of the action methods that it supports inside its
|
708
|
+
# implementation class), describe any extra sort, search or filter data it
|
709
|
+
# allows beyond the common fields and describe the expected JSON fields for
|
710
|
+
# creation and/or modification actions. By specifing these, the service
|
711
|
+
# middleware code is able to do extra validation and sanitisation of client
|
712
|
+
# requests, but they're entirely optional if the implementation class wants
|
713
|
+
# to take over all of that itself.
|
714
|
+
#
|
715
|
+
# +resource+:: Name of the resource that the interface is for, as a String
|
716
|
+
# or Symbol (e.g. +:Purchase+).
|
717
|
+
#
|
718
|
+
# &block:: Block that calls the Hoodoo::Services::InterfaceDSL methods;
|
719
|
+
# #endpoint is the only mandatory call.
|
720
|
+
#
|
721
|
+
def self.interface( resource, &block )
|
722
|
+
|
723
|
+
if @to_list.nil?
|
724
|
+
@to_list = Hoodoo::Services::Interface::ToList.new
|
725
|
+
else
|
726
|
+
raise "Hoodoo::Services::Interface subclass unexpectedly ran ::interface more than once"
|
727
|
+
end
|
728
|
+
|
729
|
+
self.resource = resource.to_sym
|
730
|
+
|
731
|
+
interface = self.new
|
732
|
+
interface.instance_eval do
|
733
|
+
version 1
|
734
|
+
embeds # Nothing
|
735
|
+
actions *Hoodoo::Services::Middleware::ALLOWED_ACTIONS
|
736
|
+
public_actions # None
|
737
|
+
secure_log_for # None
|
738
|
+
end
|
739
|
+
|
740
|
+
interface.instance_eval( &block )
|
741
|
+
|
742
|
+
if self.endpoint.nil?
|
743
|
+
raise "Hoodoo::Services::Interface subclasses must always call the 'endpoint' DSL method in their interface descriptions"
|
744
|
+
end
|
745
|
+
|
746
|
+
end
|
747
|
+
|
748
|
+
# Define various class instance variable (sic.) accessors.
|
749
|
+
#
|
750
|
+
# * Instance variable: things set on individual "Foo" instances ("Foo.new")
|
751
|
+
# * Class instance variables: things set on the "Foo" class only
|
752
|
+
# * Class variables: things set on the "Foo" class _and_ _all_ _subclasses_
|
753
|
+
#
|
754
|
+
class << self
|
755
|
+
|
756
|
+
public
|
757
|
+
|
758
|
+
# Endpoint path as declared by service, without preceding "/", possibly
|
759
|
+
# as a symbol - e.g. +:products+ for "/products[...]" as an implied
|
760
|
+
# endpoint.
|
761
|
+
#
|
762
|
+
attr_reader :endpoint
|
763
|
+
|
764
|
+
# Major version of interface as an integer. All service endpoint routes
|
765
|
+
# have "v{version}/" as a prefix, e.g. "/v1/products[...]".
|
766
|
+
#
|
767
|
+
attr_reader :version
|
768
|
+
|
769
|
+
# Name of the resource the interface addresses as a symbol, e.g.
|
770
|
+
# +:Product+.
|
771
|
+
#
|
772
|
+
attr_reader :resource
|
773
|
+
|
774
|
+
# Implementation class for the service. An
|
775
|
+
# Hoodoo::Services::Implementation subclass - the class, not an
|
776
|
+
# instance of it.
|
777
|
+
#
|
778
|
+
attr_reader :implementation
|
779
|
+
|
780
|
+
# Supported action methods as a Set of symbols with one or more of
|
781
|
+
# +:list+, +:show+, +:create+, +:update+ or +:delete+. The presence of
|
782
|
+
# a Symbol indicates a supported action. If empty, no actions are
|
783
|
+
# supported. The default is for all actions to be present in the Set.
|
784
|
+
#
|
785
|
+
attr_reader :actions
|
786
|
+
|
787
|
+
# Public action methods as a Set of symbols with one or more of
|
788
|
+
# +:list+, +:show+, +:create+, +:update+ or +:delete+. The presence
|
789
|
+
# of a Symbol indicates an action open to the public and not subject
|
790
|
+
# to session security. If empty, all actions are protected by session
|
791
|
+
# security. The default is an empty Set.
|
792
|
+
#
|
793
|
+
attr_reader :public_actions
|
794
|
+
|
795
|
+
# Secure log actions set by #secure_log_for - see that call for
|
796
|
+
# details. The default is an empty Hash.
|
797
|
+
#
|
798
|
+
attr_reader :secure_log_for
|
799
|
+
|
800
|
+
# Array of strings listing allowed embeddable things. Each string
|
801
|
+
# matches the split up comma-separated value for query string
|
802
|
+
# <tt>_embed</tt> or <tt>_reference</tt> keys. For example:
|
803
|
+
#
|
804
|
+
# ...&_embed=foo,bar
|
805
|
+
#
|
806
|
+
# ...would be valid provided there was an embedding declaration
|
807
|
+
# such as:
|
808
|
+
#
|
809
|
+
# embeds :foo, :bar
|
810
|
+
#
|
811
|
+
# ...which would in turn lead this accessor to return:
|
812
|
+
#
|
813
|
+
# [ 'foo', 'bar' ]
|
814
|
+
#
|
815
|
+
attr_reader :embeds
|
816
|
+
|
817
|
+
# A Hoodoo::Services::Interface::ToList instance describing the list
|
818
|
+
# parameters for the interface as a Set of Strings. See also
|
819
|
+
# Hoodoo::Services::Interface::ToListDSL.
|
820
|
+
#
|
821
|
+
def to_list
|
822
|
+
@to_list ||= Hoodoo::Services::Interface::ToList.new
|
823
|
+
@to_list
|
824
|
+
end
|
825
|
+
|
826
|
+
# A Hoodoo::Presenters::Object instance describing the schema
|
827
|
+
# for client JSON coming in for calls that create instances of the
|
828
|
+
# resource that the service's interface is addressing. If +nil+,
|
829
|
+
# arbitrary data is acceptable (the implementation becomes entirely
|
830
|
+
# responsible for data validation).
|
831
|
+
#
|
832
|
+
attr_reader :to_create
|
833
|
+
|
834
|
+
# A Hoodoo::Presenters::Object instance describing the schema
|
835
|
+
# for client JSON coming in for calls that modify instances of the
|
836
|
+
# resource that the service's interface is addressing. If +nil+,
|
837
|
+
# arbitrary data is acceptable (the implementation becomes entirely
|
838
|
+
# responsible for data validation).
|
839
|
+
#
|
840
|
+
attr_reader :to_update
|
841
|
+
|
842
|
+
# A Hoodoo::ErrorDescriptions instance describing all errors that
|
843
|
+
# the interface might return, including the default set of platform
|
844
|
+
# and generic errors. If nil, there are no additional error codes
|
845
|
+
# beyond the default set.
|
846
|
+
#
|
847
|
+
attr_reader :errors_for
|
848
|
+
|
849
|
+
# A Hash, keyed by String equivalents of the Symbols in
|
850
|
+
# Hoodoo::Services::Middleware::ALLOWED_ACTIONS, where the values
|
851
|
+
# are Hoodoo::Services::Permissions instances describing extended
|
852
|
+
# permissions for the related action. See
|
853
|
+
# ::additional_permissions_for.
|
854
|
+
#
|
855
|
+
attr_reader :additional_permissions
|
856
|
+
|
857
|
+
private
|
858
|
+
|
859
|
+
# Private property writer allows instances running the DSL to set
|
860
|
+
# values on the class for querying using the public readers.
|
861
|
+
# See ::endpoint.
|
862
|
+
#
|
863
|
+
attr_writer :endpoint
|
864
|
+
|
865
|
+
# Private property writer allows instances running the DSL to set
|
866
|
+
# values on the class for querying using the public readers.
|
867
|
+
# See ::version.
|
868
|
+
#
|
869
|
+
attr_writer :version
|
870
|
+
|
871
|
+
# Private property writer allows instances running the DSL to set
|
872
|
+
# values on the class for querying using the public readers.
|
873
|
+
# See ::resource.
|
874
|
+
#
|
875
|
+
attr_writer :resource
|
876
|
+
|
877
|
+
# Private property writer allows instances running the DSL to set
|
878
|
+
# values on the class for querying using the public readers.
|
879
|
+
# See ::implementation.
|
880
|
+
#
|
881
|
+
attr_writer :implementation
|
882
|
+
|
883
|
+
# Private property writer allows instances running the DSL to set
|
884
|
+
# values on the class for querying using the public readers.
|
885
|
+
# See ::actions.
|
886
|
+
#
|
887
|
+
attr_writer :actions
|
888
|
+
|
889
|
+
# Private property writer allows instances running the DSL to set
|
890
|
+
# values on the class for querying using the public readers.
|
891
|
+
# See ::public_actions.
|
892
|
+
#
|
893
|
+
attr_writer :public_actions
|
894
|
+
|
895
|
+
# Private property writer allows instances running the DSL to set
|
896
|
+
# values on the class for querying using the public readers.
|
897
|
+
# See ::secure_log_for.
|
898
|
+
#
|
899
|
+
attr_writer :secure_log_for
|
900
|
+
|
901
|
+
# Private property writer allows instances running the DSL to set
|
902
|
+
# values on the class for querying using the public readers.
|
903
|
+
# See ::embeds.
|
904
|
+
#
|
905
|
+
attr_writer :embeds
|
906
|
+
|
907
|
+
# Private property writer allows instances running the DSL to set
|
908
|
+
# values on the class for querying using the public readers.
|
909
|
+
# See ::to_create.
|
910
|
+
#
|
911
|
+
attr_writer :to_create
|
912
|
+
|
913
|
+
# Private property writer allows instances running the DSL to set
|
914
|
+
# values on the class for querying using the public readers.
|
915
|
+
# See ::to_update.
|
916
|
+
#
|
917
|
+
attr_writer :to_update
|
918
|
+
|
919
|
+
# Private property writer allows instances running the DSL to set
|
920
|
+
# values on the class for querying using the public readers.
|
921
|
+
# See ::errors_for.
|
922
|
+
#
|
923
|
+
attr_writer :errors_for
|
924
|
+
|
925
|
+
# Private property writer allows instances running the DSL to set
|
926
|
+
# values on the class for querying using the public readers.
|
927
|
+
# See ::additional_permissions.
|
928
|
+
#
|
929
|
+
attr_writer :additional_permissions
|
930
|
+
|
931
|
+
end # 'class << self'
|
932
|
+
end # 'class Interface'
|
933
|
+
|
934
|
+
end; end # 'module Hoodoo; module Services'
|