hoodoo 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/hoodoo +5 -0
- data/lib/hoodoo.rb +27 -0
- data/lib/hoodoo/active.rb +32 -0
- data/lib/hoodoo/active/active_model/uuid_validator.rb +45 -0
- data/lib/hoodoo/active/active_record/base.rb +81 -0
- data/lib/hoodoo/active/active_record/creator.rb +134 -0
- data/lib/hoodoo/active/active_record/dated.rb +343 -0
- data/lib/hoodoo/active/active_record/error_mapping.rb +351 -0
- data/lib/hoodoo/active/active_record/finder.rb +606 -0
- data/lib/hoodoo/active/active_record/search_helper.rb +189 -0
- data/lib/hoodoo/active/active_record/secure.rb +431 -0
- data/lib/hoodoo/active/active_record/support.rb +106 -0
- data/lib/hoodoo/active/active_record/translated.rb +87 -0
- data/lib/hoodoo/active/active_record/uuid.rb +80 -0
- data/lib/hoodoo/active/active_record/writer.rb +321 -0
- data/lib/hoodoo/client.rb +23 -0
- data/lib/hoodoo/client/augmented_array.rb +29 -0
- data/lib/hoodoo/client/augmented_base.rb +168 -0
- data/lib/hoodoo/client/augmented_hash.rb +23 -0
- data/lib/hoodoo/client/client.rb +354 -0
- data/lib/hoodoo/client/endpoint/endpoint.rb +427 -0
- data/lib/hoodoo/client/endpoint/endpoints/amqp.rb +180 -0
- data/lib/hoodoo/client/endpoint/endpoints/auto_session.rb +194 -0
- data/lib/hoodoo/client/endpoint/endpoints/http.rb +203 -0
- data/lib/hoodoo/client/endpoint/endpoints/http_based.rb +367 -0
- data/lib/hoodoo/client/endpoint/endpoints/not_found.rb +59 -0
- data/lib/hoodoo/client/headers.rb +269 -0
- data/lib/hoodoo/communicators.rb +23 -0
- data/lib/hoodoo/communicators/fast.rb +44 -0
- data/lib/hoodoo/communicators/pool.rb +601 -0
- data/lib/hoodoo/communicators/slow.rb +84 -0
- data/lib/hoodoo/data.rb +51 -0
- data/lib/hoodoo/data/resources/caller.rb +39 -0
- data/lib/hoodoo/data/resources/errors.rb +28 -0
- data/lib/hoodoo/data/resources/log.rb +31 -0
- data/lib/hoodoo/data/resources/session.rb +26 -0
- data/lib/hoodoo/data/types/error_primitive.rb +27 -0
- data/lib/hoodoo/data/types/permissions.rb +40 -0
- data/lib/hoodoo/data/types/permissions_defaults.rb +32 -0
- data/lib/hoodoo/data/types/permissions_full.rb +28 -0
- data/lib/hoodoo/data/types/permissions_resources.rb +31 -0
- data/lib/hoodoo/discovery.rb +20 -0
- data/lib/hoodoo/errors.rb +19 -0
- data/lib/hoodoo/errors/error_descriptions.rb +229 -0
- data/lib/hoodoo/errors/errors.rb +322 -0
- data/lib/hoodoo/generator.rb +139 -0
- data/lib/hoodoo/logger.rb +23 -0
- data/lib/hoodoo/logger/fast_writer.rb +27 -0
- data/lib/hoodoo/logger/flattener_mixin.rb +36 -0
- data/lib/hoodoo/logger/logger.rb +387 -0
- data/lib/hoodoo/logger/slow_writer.rb +49 -0
- data/lib/hoodoo/logger/writer_mixin.rb +52 -0
- data/lib/hoodoo/logger/writers/file_writer.rb +45 -0
- data/lib/hoodoo/logger/writers/log_entries_dot_com_writer.rb +64 -0
- data/lib/hoodoo/logger/writers/stream_writer.rb +43 -0
- data/lib/hoodoo/middleware.rb +33 -0
- data/lib/hoodoo/presenters.rb +45 -0
- data/lib/hoodoo/presenters/base.rb +281 -0
- data/lib/hoodoo/presenters/base_dsl.rb +519 -0
- data/lib/hoodoo/presenters/common_resource_fields.rb +31 -0
- data/lib/hoodoo/presenters/embedding.rb +232 -0
- data/lib/hoodoo/presenters/types/array.rb +118 -0
- data/lib/hoodoo/presenters/types/boolean.rb +26 -0
- data/lib/hoodoo/presenters/types/date.rb +26 -0
- data/lib/hoodoo/presenters/types/date_time.rb +26 -0
- data/lib/hoodoo/presenters/types/decimal.rb +47 -0
- data/lib/hoodoo/presenters/types/enum.rb +55 -0
- data/lib/hoodoo/presenters/types/field.rb +158 -0
- data/lib/hoodoo/presenters/types/float.rb +26 -0
- data/lib/hoodoo/presenters/types/hash.rb +361 -0
- data/lib/hoodoo/presenters/types/integer.rb +26 -0
- data/lib/hoodoo/presenters/types/object.rb +117 -0
- data/lib/hoodoo/presenters/types/string.rb +53 -0
- data/lib/hoodoo/presenters/types/tags.rb +24 -0
- data/lib/hoodoo/presenters/types/text.rb +26 -0
- data/lib/hoodoo/presenters/types/uuid.rb +54 -0
- data/lib/hoodoo/services.rb +34 -0
- data/lib/hoodoo/services/discovery/discoverers/by_consul.rb +66 -0
- data/lib/hoodoo/services/discovery/discoverers/by_convention.rb +173 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/by_drb.rb +195 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server.rb +166 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server_start.rb +37 -0
- data/lib/hoodoo/services/discovery/discovery.rb +186 -0
- data/lib/hoodoo/services/discovery/results/for_amqp.rb +58 -0
- data/lib/hoodoo/services/discovery/results/for_http.rb +85 -0
- data/lib/hoodoo/services/discovery/results/for_local.rb +85 -0
- data/lib/hoodoo/services/discovery/results/for_remote.rb +57 -0
- data/lib/hoodoo/services/middleware/amqp_log_message.rb +186 -0
- data/lib/hoodoo/services/middleware/amqp_log_writer.rb +119 -0
- data/lib/hoodoo/services/middleware/endpoints/inter_resource_local.rb +130 -0
- data/lib/hoodoo/services/middleware/endpoints/inter_resource_remote.rb +202 -0
- data/lib/hoodoo/services/middleware/exception_reporting/base_reporter.rb +105 -0
- data/lib/hoodoo/services/middleware/exception_reporting/exception_reporting.rb +115 -0
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/airbrake_reporter.rb +64 -0
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/raygun_reporter.rb +63 -0
- data/lib/hoodoo/services/middleware/interaction.rb +127 -0
- data/lib/hoodoo/services/middleware/middleware.rb +2705 -0
- data/lib/hoodoo/services/middleware/rack_monkey_patch.rb +73 -0
- data/lib/hoodoo/services/services/context.rb +153 -0
- data/lib/hoodoo/services/services/implementation.rb +132 -0
- data/lib/hoodoo/services/services/interface.rb +934 -0
- data/lib/hoodoo/services/services/permissions.rb +250 -0
- data/lib/hoodoo/services/services/request.rb +189 -0
- data/lib/hoodoo/services/services/response.rb +316 -0
- data/lib/hoodoo/services/services/service.rb +141 -0
- data/lib/hoodoo/services/services/session.rb +729 -0
- data/lib/hoodoo/utilities.rb +12 -0
- data/lib/hoodoo/utilities/string_inquirer.rb +54 -0
- data/lib/hoodoo/utilities/utilities.rb +380 -0
- data/lib/hoodoo/utilities/uuid.rb +44 -0
- data/lib/hoodoo/version.rb +17 -0
- data/spec/active/active_record/base_spec.rb +57 -0
- data/spec/active/active_record/creator_spec.rb +88 -0
- data/spec/active/active_record/dated_spec.rb +248 -0
- data/spec/active/active_record/error_mapping_spec.rb +360 -0
- data/spec/active/active_record/finder_spec.rb +744 -0
- data/spec/active/active_record/search_helper_spec.rb +384 -0
- data/spec/active/active_record/secure_spec.rb +435 -0
- data/spec/active/active_record/support_spec.rb +225 -0
- data/spec/active/active_record/translated_spec.rb +19 -0
- data/spec/active/active_record/uuid_spec.rb +72 -0
- data/spec/active/active_record/writer_spec.rb +272 -0
- data/spec/alchemy/alchemy-amq.rb +33 -0
- data/spec/client/augmented_array_spec.rb +15 -0
- data/spec/client/augmented_base_spec.rb +50 -0
- data/spec/client/augmented_hash_spec.rb +15 -0
- data/spec/client/client_spec.rb +955 -0
- data/spec/client/endpoint/endpoint_spec.rb +70 -0
- data/spec/client/endpoint/endpoints/amqp_spec.rb +16 -0
- data/spec/client/endpoint/endpoints/auto_session_spec.rb +9 -0
- data/spec/client/endpoint/endpoints/http_based_spec.rb +9 -0
- data/spec/client/endpoint/endpoints/http_spec.rb +103 -0
- data/spec/client/endpoint/endpoints/not_found_spec.rb +35 -0
- data/spec/client/headers_spec.rb +172 -0
- data/spec/communicators/fast_spec.rb +9 -0
- data/spec/communicators/pool_spec.rb +339 -0
- data/spec/communicators/slow_spec.rb +15 -0
- data/spec/data/resources/caller_spec.rb +156 -0
- data/spec/data/resources/errors_spec.rb +22 -0
- data/spec/data/resources/log_spec.rb +20 -0
- data/spec/data/resources/session_spec.rb +15 -0
- data/spec/data/types/error_primitive_spec.rb +15 -0
- data/spec/data/types/permissions_defaults_spec.rb +25 -0
- data/spec/data/types/permissions_full_spec.rb +44 -0
- data/spec/data/types/permissions_resources_spec.rb +34 -0
- data/spec/data/types/permissions_spec.rb +37 -0
- data/spec/errors/error_descriptions_spec.rb +98 -0
- data/spec/errors/errors_spec.rb +346 -0
- data/spec/integration/service_actions_spec.rb +112 -0
- data/spec/logger/fast_writer_spec.rb +18 -0
- data/spec/logger/logger_spec.rb +259 -0
- data/spec/logger/slow_writer_spec.rb +144 -0
- data/spec/logger/writers/file_writer_spec.rb +37 -0
- data/spec/logger/writers/log_entries_dot_com_writer_spec.rb +29 -0
- data/spec/logger/writers/stream_writer_spec.rb +38 -0
- data/spec/presenters/base_dsl_spec.rb +111 -0
- data/spec/presenters/base_spec.rb +871 -0
- data/spec/presenters/common_resource_fields_spec.rb +30 -0
- data/spec/presenters/embedding_spec.rb +87 -0
- data/spec/presenters/types/array_spec.rb +249 -0
- data/spec/presenters/types/boolean_spec.rb +51 -0
- data/spec/presenters/types/date_spec.rb +57 -0
- data/spec/presenters/types/date_time_spec.rb +59 -0
- data/spec/presenters/types/decimal_spec.rb +58 -0
- data/spec/presenters/types/enum_spec.rb +71 -0
- data/spec/presenters/types/field_spec.rb +77 -0
- data/spec/presenters/types/float_spec.rb +50 -0
- data/spec/presenters/types/hash_spec.rb +1069 -0
- data/spec/presenters/types/integer_spec.rb +50 -0
- data/spec/presenters/types/object_spec.rb +177 -0
- data/spec/presenters/types/string_spec.rb +65 -0
- data/spec/presenters/types/tags_spec.rb +56 -0
- data/spec/presenters/types/text_spec.rb +50 -0
- data/spec/presenters/types/uuid_spec.rb +46 -0
- data/spec/presenters/walk_spec.rb +198 -0
- data/spec/services/discovery/discoverers/by_consul_spec.rb +29 -0
- data/spec/services/discovery/discoverers/by_convention_spec.rb +67 -0
- data/spec/services/discovery/discoverers/by_drb/by_drb_spec.rb +80 -0
- data/spec/services/discovery/discoverers/by_drb/drb_server_spec.rb +205 -0
- data/spec/services/discovery/discovery_spec.rb +73 -0
- data/spec/services/discovery/results/for_amqp_spec.rb +17 -0
- data/spec/services/discovery/results/for_http_spec.rb +37 -0
- data/spec/services/discovery/results/for_local_spec.rb +21 -0
- data/spec/services/discovery/results/for_remote_spec.rb +15 -0
- data/spec/services/middleware/amqp_log_message_spec.rb +60 -0
- data/spec/services/middleware/amqp_log_writer_spec.rb +95 -0
- data/spec/services/middleware/endpoints/inter_resource_local_spec.rb +9 -0
- data/spec/services/middleware/endpoints/inter_resource_remote_spec.rb +9 -0
- data/spec/services/middleware/exception_reporting/base_reporter_spec.rb +16 -0
- data/spec/services/middleware/exception_reporting/exception_reporting_spec.rb +92 -0
- data/spec/services/middleware/exception_reporting/reporters/airbrake_reporter_spec.rb +24 -0
- data/spec/services/middleware/exception_reporting/reporters/raygun_reporter_spec.rb +23 -0
- data/spec/services/middleware/middleware_cors_spec.rb +93 -0
- data/spec/services/middleware/middleware_create_update_spec.rb +489 -0
- data/spec/services/middleware/middleware_dated_at_spec.rb +186 -0
- data/spec/services/middleware/middleware_exotic_communication_spec.rb +560 -0
- data/spec/services/middleware/middleware_logging_spec.rb +356 -0
- data/spec/services/middleware/middleware_multi_local_spec.rb +1094 -0
- data/spec/services/middleware/middleware_multi_remote_spec.rb +1440 -0
- data/spec/services/middleware/middleware_permissions_spec.rb +1014 -0
- data/spec/services/middleware/middleware_public_spec.rb +238 -0
- data/spec/services/middleware/middleware_spec.rb +1569 -0
- data/spec/services/middleware/string_inquirer_spec.rb +30 -0
- data/spec/services/services/application_spec.rb +74 -0
- data/spec/services/services/context_spec.rb +48 -0
- data/spec/services/services/implementation_spec.rb +45 -0
- data/spec/services/services/interface_spec.rb +262 -0
- data/spec/services/services/permissions_spec.rb +249 -0
- data/spec/services/services/request_spec.rb +95 -0
- data/spec/services/services/response_spec.rb +250 -0
- data/spec/services/services/session_spec.rb +432 -0
- data/spec/spec_helper.rb +298 -0
- data/spec/utilities/utilities_spec.rb +537 -0
- data/spec/utilities/uuid_spec.rb +20 -0
- metadata +615 -0
@@ -0,0 +1,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'
|