apiwork 0.0.0.pre → 0.1.1
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 +4 -4
- data/LICENSE.txt +2 -2
- data/README.md +117 -1
- data/Rakefile +5 -3
- data/app/controllers/apiwork/errors_controller.rb +13 -0
- data/app/controllers/apiwork/exports_controller.rb +22 -0
- data/lib/apiwork/abstractable.rb +26 -0
- data/lib/apiwork/adapter/base.rb +369 -0
- data/lib/apiwork/adapter/builder/api/base.rb +66 -0
- data/lib/apiwork/adapter/builder/contract/base.rb +86 -0
- data/lib/apiwork/adapter/capability/api/base.rb +51 -0
- data/lib/apiwork/adapter/capability/api/scope.rb +64 -0
- data/lib/apiwork/adapter/capability/base.rb +291 -0
- data/lib/apiwork/adapter/capability/contract/base.rb +37 -0
- data/lib/apiwork/adapter/capability/contract/scope.rb +110 -0
- data/lib/apiwork/adapter/capability/operation/base.rb +172 -0
- data/lib/apiwork/adapter/capability/operation/metadata_shape.rb +165 -0
- data/lib/apiwork/adapter/capability/result.rb +21 -0
- data/lib/apiwork/adapter/capability/runner.rb +56 -0
- data/lib/apiwork/adapter/capability/transformer/request/base.rb +72 -0
- data/lib/apiwork/adapter/capability/transformer/response/base.rb +45 -0
- data/lib/apiwork/adapter/registry.rb +16 -0
- data/lib/apiwork/adapter/serializer/error/base.rb +72 -0
- data/lib/apiwork/adapter/serializer/error/default/api_builder.rb +32 -0
- data/lib/apiwork/adapter/serializer/error/default.rb +37 -0
- data/lib/apiwork/adapter/serializer/resource/base.rb +84 -0
- data/lib/apiwork/adapter/serializer/resource/default/contract_builder.rb +209 -0
- data/lib/apiwork/adapter/serializer/resource/default.rb +39 -0
- data/lib/apiwork/adapter/standard/capability/filtering/api_builder.rb +75 -0
- data/lib/apiwork/adapter/standard/capability/filtering/constants.rb +37 -0
- data/lib/apiwork/adapter/standard/capability/filtering/contract_builder.rb +193 -0
- data/lib/apiwork/adapter/standard/capability/filtering/operation/filter/builder.rb +47 -0
- data/lib/apiwork/adapter/standard/capability/filtering/operation/filter/operator_builder.rb +36 -0
- data/lib/apiwork/adapter/standard/capability/filtering/operation/filter.rb +462 -0
- data/lib/apiwork/adapter/standard/capability/filtering/operation.rb +22 -0
- data/lib/apiwork/adapter/standard/capability/filtering/request_transformer.rb +47 -0
- data/lib/apiwork/adapter/standard/capability/filtering.rb +18 -0
- data/lib/apiwork/adapter/standard/capability/including/contract_builder.rb +169 -0
- data/lib/apiwork/adapter/standard/capability/including/operation.rb +20 -0
- data/lib/apiwork/adapter/standard/capability/including.rb +16 -0
- data/lib/apiwork/adapter/standard/capability/pagination/api_builder.rb +34 -0
- data/lib/apiwork/adapter/standard/capability/pagination/contract_builder.rb +35 -0
- data/lib/apiwork/adapter/standard/capability/pagination/operation/paginate/cursor.rb +84 -0
- data/lib/apiwork/adapter/standard/capability/pagination/operation/paginate/offset.rb +66 -0
- data/lib/apiwork/adapter/standard/capability/pagination/operation/paginate.rb +24 -0
- data/lib/apiwork/adapter/standard/capability/pagination/operation.rb +24 -0
- data/lib/apiwork/adapter/standard/capability/pagination.rb +21 -0
- data/lib/apiwork/adapter/standard/capability/sorting/api_builder.rb +19 -0
- data/lib/apiwork/adapter/standard/capability/sorting/contract_builder.rb +84 -0
- data/lib/apiwork/adapter/standard/capability/sorting/operation/sort.rb +83 -0
- data/lib/apiwork/adapter/standard/capability/sorting/operation.rb +22 -0
- data/lib/apiwork/adapter/standard/capability/sorting.rb +17 -0
- data/lib/apiwork/adapter/standard/capability/writing/constants.rb +15 -0
- data/lib/apiwork/adapter/standard/capability/writing/contract_builder.rb +253 -0
- data/lib/apiwork/adapter/standard/capability/writing/operation/issue_mapper.rb +210 -0
- data/lib/apiwork/adapter/standard/capability/writing/operation.rb +32 -0
- data/lib/apiwork/adapter/standard/capability/writing/request_transformer.rb +37 -0
- data/lib/apiwork/adapter/standard/capability/writing.rb +17 -0
- data/lib/apiwork/adapter/standard/includes_resolver.rb +106 -0
- data/lib/apiwork/adapter/standard.rb +22 -0
- data/lib/apiwork/adapter/wrapper/base.rb +70 -0
- data/lib/apiwork/adapter/wrapper/collection/base.rb +60 -0
- data/lib/apiwork/adapter/wrapper/collection/default.rb +47 -0
- data/lib/apiwork/adapter/wrapper/error/base.rb +30 -0
- data/lib/apiwork/adapter/wrapper/error/default.rb +34 -0
- data/lib/apiwork/adapter/wrapper/member/base.rb +58 -0
- data/lib/apiwork/adapter/wrapper/member/default.rb +40 -0
- data/lib/apiwork/adapter/wrapper/shape.rb +203 -0
- data/lib/apiwork/adapter.rb +50 -0
- data/lib/apiwork/api/base.rb +802 -0
- data/lib/apiwork/api/element.rb +110 -0
- data/lib/apiwork/api/enum_registry/definition.rb +51 -0
- data/lib/apiwork/api/enum_registry.rb +98 -0
- data/lib/apiwork/api/info/contact.rb +67 -0
- data/lib/apiwork/api/info/license.rb +50 -0
- data/lib/apiwork/api/info/server.rb +50 -0
- data/lib/apiwork/api/info.rb +221 -0
- data/lib/apiwork/api/object.rb +235 -0
- data/lib/apiwork/api/registry.rb +33 -0
- data/lib/apiwork/api/representation_registry.rb +76 -0
- data/lib/apiwork/api/resource/action.rb +41 -0
- data/lib/apiwork/api/resource.rb +648 -0
- data/lib/apiwork/api/router.rb +104 -0
- data/lib/apiwork/api/type_registry/definition.rb +117 -0
- data/lib/apiwork/api/type_registry.rb +99 -0
- data/lib/apiwork/api/union.rb +49 -0
- data/lib/apiwork/api.rb +85 -0
- data/lib/apiwork/configurable.rb +71 -0
- data/lib/apiwork/configuration/option.rb +125 -0
- data/lib/apiwork/configuration/validatable.rb +25 -0
- data/lib/apiwork/configuration.rb +95 -0
- data/lib/apiwork/configuration_error.rb +6 -0
- data/lib/apiwork/constraint_error.rb +20 -0
- data/lib/apiwork/contract/action/request.rb +79 -0
- data/lib/apiwork/contract/action/response.rb +87 -0
- data/lib/apiwork/contract/action.rb +258 -0
- data/lib/apiwork/contract/base.rb +714 -0
- data/lib/apiwork/contract/element.rb +130 -0
- data/lib/apiwork/contract/object/coercer.rb +194 -0
- data/lib/apiwork/contract/object/deserializer.rb +101 -0
- data/lib/apiwork/contract/object/transformer.rb +95 -0
- data/lib/apiwork/contract/object/validator/result.rb +27 -0
- data/lib/apiwork/contract/object/validator.rb +734 -0
- data/lib/apiwork/contract/object.rb +566 -0
- data/lib/apiwork/contract/request_parser/result.rb +25 -0
- data/lib/apiwork/contract/request_parser.rb +72 -0
- data/lib/apiwork/contract/response_parser/result.rb +25 -0
- data/lib/apiwork/contract/response_parser.rb +35 -0
- data/lib/apiwork/contract/union.rb +56 -0
- data/lib/apiwork/contract_error.rb +9 -0
- data/lib/apiwork/controller.rb +300 -0
- data/lib/apiwork/domain_error.rb +13 -0
- data/lib/apiwork/element.rb +386 -0
- data/lib/apiwork/engine.rb +20 -0
- data/lib/apiwork/error.rb +6 -0
- data/lib/apiwork/error_code/definition.rb +63 -0
- data/lib/apiwork/error_code/registry.rb +18 -0
- data/lib/apiwork/error_code.rb +132 -0
- data/lib/apiwork/export/base.rb +291 -0
- data/lib/apiwork/export/open_api.rb +600 -0
- data/lib/apiwork/export/pipeline/writer.rb +66 -0
- data/lib/apiwork/export/pipeline.rb +84 -0
- data/lib/apiwork/export/registry.rb +16 -0
- data/lib/apiwork/export/surface_resolver.rb +189 -0
- data/lib/apiwork/export/type_analysis.rb +170 -0
- data/lib/apiwork/export/type_script.rb +23 -0
- data/lib/apiwork/export/type_script_mapper.rb +349 -0
- data/lib/apiwork/export/zod.rb +39 -0
- data/lib/apiwork/export/zod_mapper.rb +421 -0
- data/lib/apiwork/export.rb +80 -0
- data/lib/apiwork/http_error.rb +16 -0
- data/lib/apiwork/introspection/action/request.rb +66 -0
- data/lib/apiwork/introspection/action/response.rb +57 -0
- data/lib/apiwork/introspection/action.rb +124 -0
- data/lib/apiwork/introspection/api/info/contact.rb +59 -0
- data/lib/apiwork/introspection/api/info/license.rb +49 -0
- data/lib/apiwork/introspection/api/info/server.rb +50 -0
- data/lib/apiwork/introspection/api/info.rb +107 -0
- data/lib/apiwork/introspection/api/resource.rb +83 -0
- data/lib/apiwork/introspection/api.rb +92 -0
- data/lib/apiwork/introspection/contract.rb +63 -0
- data/lib/apiwork/introspection/dump/action.rb +101 -0
- data/lib/apiwork/introspection/dump/api.rb +119 -0
- data/lib/apiwork/introspection/dump/contract.rb +129 -0
- data/lib/apiwork/introspection/dump/param.rb +486 -0
- data/lib/apiwork/introspection/dump/resource.rb +112 -0
- data/lib/apiwork/introspection/dump/type.rb +339 -0
- data/lib/apiwork/introspection/dump.rb +17 -0
- data/lib/apiwork/introspection/enum.rb +63 -0
- data/lib/apiwork/introspection/error_code.rb +44 -0
- data/lib/apiwork/introspection/param/array.rb +88 -0
- data/lib/apiwork/introspection/param/base.rb +285 -0
- data/lib/apiwork/introspection/param/binary.rb +73 -0
- data/lib/apiwork/introspection/param/boolean.rb +73 -0
- data/lib/apiwork/introspection/param/date.rb +73 -0
- data/lib/apiwork/introspection/param/date_time.rb +73 -0
- data/lib/apiwork/introspection/param/decimal.rb +121 -0
- data/lib/apiwork/introspection/param/integer.rb +131 -0
- data/lib/apiwork/introspection/param/literal.rb +45 -0
- data/lib/apiwork/introspection/param/number.rb +121 -0
- data/lib/apiwork/introspection/param/object.rb +59 -0
- data/lib/apiwork/introspection/param/reference.rb +45 -0
- data/lib/apiwork/introspection/param/string.rb +122 -0
- data/lib/apiwork/introspection/param/time.rb +73 -0
- data/lib/apiwork/introspection/param/union.rb +57 -0
- data/lib/apiwork/introspection/param/unknown.rb +26 -0
- data/lib/apiwork/introspection/param/uuid.rb +73 -0
- data/lib/apiwork/introspection/param.rb +31 -0
- data/lib/apiwork/introspection/type.rb +129 -0
- data/lib/apiwork/introspection.rb +28 -0
- data/lib/apiwork/issue.rb +80 -0
- data/lib/apiwork/json_pointer.rb +21 -0
- data/lib/apiwork/object.rb +1618 -0
- data/lib/apiwork/reference_generator.rb +622 -0
- data/lib/apiwork/registry.rb +56 -0
- data/lib/apiwork/representation/association.rb +391 -0
- data/lib/apiwork/representation/attribute.rb +335 -0
- data/lib/apiwork/representation/base.rb +819 -0
- data/lib/apiwork/representation/deserializer.rb +95 -0
- data/lib/apiwork/representation/element.rb +128 -0
- data/lib/apiwork/representation/inheritance.rb +78 -0
- data/lib/apiwork/representation/model_detector.rb +75 -0
- data/lib/apiwork/representation/root_key.rb +35 -0
- data/lib/apiwork/representation/serializer.rb +127 -0
- data/lib/apiwork/request.rb +79 -0
- data/lib/apiwork/response.rb +56 -0
- data/lib/apiwork/union.rb +102 -0
- data/lib/apiwork/version.rb +2 -2
- data/lib/apiwork.rb +61 -3
- data/lib/generators/apiwork/api_generator.rb +38 -0
- data/lib/generators/apiwork/contract_generator.rb +25 -0
- data/lib/generators/apiwork/install_generator.rb +27 -0
- data/lib/generators/apiwork/representation_generator.rb +25 -0
- data/lib/generators/apiwork/templates/api/api.rb.tt +4 -0
- data/lib/generators/apiwork/templates/contract/contract.rb.tt +6 -0
- data/lib/generators/apiwork/templates/install/application_contract.rb.tt +5 -0
- data/lib/generators/apiwork/templates/install/application_representation.rb.tt +5 -0
- data/lib/generators/apiwork/templates/representation/representation.rb.tt +6 -0
- data/lib/tasks/apiwork.rake +102 -0
- metadata +319 -19
- data/.rubocop.yml +0 -8
- data/sig/apiwork.rbs +0 -4
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Capability
|
|
6
|
+
module Operation
|
|
7
|
+
# @api public
|
|
8
|
+
# Base class for capability Operation phase.
|
|
9
|
+
#
|
|
10
|
+
# Operation phase runs on each request.
|
|
11
|
+
# Use it to transform data at runtime.
|
|
12
|
+
class Base
|
|
13
|
+
# @!attribute [r] data
|
|
14
|
+
# @api public
|
|
15
|
+
# The data for this operation.
|
|
16
|
+
#
|
|
17
|
+
# @return [Object]
|
|
18
|
+
# @!attribute [r] options
|
|
19
|
+
# @api public
|
|
20
|
+
# The options for this operation.
|
|
21
|
+
#
|
|
22
|
+
# @return [Configuration]
|
|
23
|
+
# @!attribute [r] request
|
|
24
|
+
# @api public
|
|
25
|
+
# The request for this operation.
|
|
26
|
+
#
|
|
27
|
+
# @return [Request]
|
|
28
|
+
# @!attribute [r] representation_class
|
|
29
|
+
# @api public
|
|
30
|
+
# The representation class for this operation.
|
|
31
|
+
#
|
|
32
|
+
# @return [Class<Representation::Base>]
|
|
33
|
+
attr_reader :data,
|
|
34
|
+
:options,
|
|
35
|
+
:representation_class,
|
|
36
|
+
:request
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
# @api public
|
|
40
|
+
# The target for this operation.
|
|
41
|
+
#
|
|
42
|
+
# @param value [Symbol, nil] (nil) [:collection, :member]
|
|
43
|
+
# The target type.
|
|
44
|
+
# @return [Symbol, nil]
|
|
45
|
+
def target(value = nil)
|
|
46
|
+
@target = value if value
|
|
47
|
+
@target
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @api public
|
|
51
|
+
# Defines metadata shape for this operation.
|
|
52
|
+
#
|
|
53
|
+
# Pass a block or a {MetadataShape} subclass.
|
|
54
|
+
# Blocks are evaluated via instance_exec, providing access to
|
|
55
|
+
# type DSL methods and capability options.
|
|
56
|
+
#
|
|
57
|
+
# @param klass [Class<MetadataShape>, nil] (nil)
|
|
58
|
+
# The metadata shape class.
|
|
59
|
+
# @yield block that defines metadata structure
|
|
60
|
+
# @return [Class<MetadataShape>, nil]
|
|
61
|
+
#
|
|
62
|
+
# @example With block
|
|
63
|
+
# metadata_shape do
|
|
64
|
+
# reference(:pagination, to: :offset_pagination)
|
|
65
|
+
# end
|
|
66
|
+
#
|
|
67
|
+
# @example With class
|
|
68
|
+
# metadata_shape PaginationShape
|
|
69
|
+
def metadata_shape(klass = nil, &block)
|
|
70
|
+
if klass
|
|
71
|
+
@metadata_shape_class = klass
|
|
72
|
+
elsif block
|
|
73
|
+
@metadata_shape_class = wrap_metadata_shape_block(block)
|
|
74
|
+
end
|
|
75
|
+
@metadata_shape_class
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def wrap_metadata_shape_block(callable)
|
|
81
|
+
Class.new(MetadataShape) do
|
|
82
|
+
define_singleton_method(:callable) { callable }
|
|
83
|
+
|
|
84
|
+
def apply
|
|
85
|
+
block = self.class.callable
|
|
86
|
+
block.arity.positive? ? block.call(self) : instance_exec(&block)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def initialize(data, representation_class, options, request, translation_context: {})
|
|
93
|
+
@data = data
|
|
94
|
+
@representation_class = representation_class
|
|
95
|
+
@options = options
|
|
96
|
+
@request = request
|
|
97
|
+
@translation_context = translation_context
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# @api public
|
|
101
|
+
# Applies this operation to the data.
|
|
102
|
+
#
|
|
103
|
+
# Override this method to implement transformation logic.
|
|
104
|
+
# Return `nil` if no changes are made.
|
|
105
|
+
#
|
|
106
|
+
# @return [Result, nil]
|
|
107
|
+
def apply
|
|
108
|
+
raise NotImplementedError
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# @api public
|
|
112
|
+
# Creates a result object.
|
|
113
|
+
#
|
|
114
|
+
# @param data [Object, nil] (nil)
|
|
115
|
+
# The transformed data.
|
|
116
|
+
# @param includes [Array, nil] (nil)
|
|
117
|
+
# The associations to preload.
|
|
118
|
+
# @param metadata [Hash, nil] (nil)
|
|
119
|
+
# The metadata to add to response.
|
|
120
|
+
# @param serialize_options [Hash, nil] (nil)
|
|
121
|
+
# The options for serialization.
|
|
122
|
+
# @return [Result]
|
|
123
|
+
def result(data: nil, includes: nil, metadata: nil, serialize_options: nil)
|
|
124
|
+
Result.new(
|
|
125
|
+
data:,
|
|
126
|
+
includes:,
|
|
127
|
+
metadata:,
|
|
128
|
+
serialize_options:,
|
|
129
|
+
)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# @api public
|
|
133
|
+
# Translates a key using the adapter's i18n convention.
|
|
134
|
+
#
|
|
135
|
+
# Lookup order:
|
|
136
|
+
# 1. `apiwork.apis.<locale_key>.adapters.<adapter_name>.capabilities.<capability_name>.<segments>`
|
|
137
|
+
# 2. `apiwork.adapters.<adapter_name>.capabilities.<capability_name>.<segments>`
|
|
138
|
+
# 3. Provided default
|
|
139
|
+
#
|
|
140
|
+
# @param segments [Array<Symbol, String>]
|
|
141
|
+
# The key path segments.
|
|
142
|
+
# @param default [String, nil] (nil)
|
|
143
|
+
# The fallback value if no translation found.
|
|
144
|
+
# @return [String, nil]
|
|
145
|
+
#
|
|
146
|
+
# @example
|
|
147
|
+
# translate(:issues, :invalid, :detail)
|
|
148
|
+
# # Tries: apiwork.apis.billing.adapters.standard.capabilities.writing.issues.invalid.detail
|
|
149
|
+
# # Falls back to: apiwork.adapters.standard.capabilities.writing.issues.invalid.detail
|
|
150
|
+
def translate(*segments, default: nil)
|
|
151
|
+
adapter_name = @translation_context[:adapter_name]
|
|
152
|
+
capability_name = @translation_context[:capability_name]
|
|
153
|
+
locale_key = @translation_context[:locale_key]
|
|
154
|
+
key_suffix = segments.join('.')
|
|
155
|
+
|
|
156
|
+
if locale_key
|
|
157
|
+
api_key = :"apiwork.apis.#{locale_key}.adapters.#{adapter_name}.capabilities.#{capability_name}.#{key_suffix}"
|
|
158
|
+
result = I18n.translate(api_key, default: nil)
|
|
159
|
+
return result if result
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
adapter_key = :"apiwork.adapters.#{adapter_name}.capabilities.#{capability_name}.#{key_suffix}"
|
|
163
|
+
result = I18n.translate(adapter_key, default: nil)
|
|
164
|
+
return result if result
|
|
165
|
+
|
|
166
|
+
default
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Capability
|
|
6
|
+
module Operation
|
|
7
|
+
# @api public
|
|
8
|
+
# Shape builder for operation metadata.
|
|
9
|
+
#
|
|
10
|
+
# Provides {#options} for accessing capability configuration,
|
|
11
|
+
# plus all DSL methods from {API::Object} for defining structure.
|
|
12
|
+
# Used by operations to define their metadata contribution.
|
|
13
|
+
#
|
|
14
|
+
# @example Add pagination metadata shape
|
|
15
|
+
# metadata_shape do
|
|
16
|
+
# reference :pagination
|
|
17
|
+
# end
|
|
18
|
+
class MetadataShape
|
|
19
|
+
class << self
|
|
20
|
+
def apply(object, options)
|
|
21
|
+
new(object, options).apply
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @api public
|
|
26
|
+
# The capability options for this metadata shape.
|
|
27
|
+
#
|
|
28
|
+
# @return [Configuration]
|
|
29
|
+
attr_reader :options
|
|
30
|
+
|
|
31
|
+
# @!method array(name, **options, &block)
|
|
32
|
+
# @api public
|
|
33
|
+
# @see API::Object#array
|
|
34
|
+
# @!method array?(name, **options, &block)
|
|
35
|
+
# @api public
|
|
36
|
+
# @see API::Object#array?
|
|
37
|
+
# @!method binary(name, **options)
|
|
38
|
+
# @api public
|
|
39
|
+
# @see API::Object#binary
|
|
40
|
+
# @!method binary?(name, **options)
|
|
41
|
+
# @api public
|
|
42
|
+
# @see API::Object#binary?
|
|
43
|
+
# @!method boolean(name, **options)
|
|
44
|
+
# @api public
|
|
45
|
+
# @see API::Object#boolean
|
|
46
|
+
# @!method boolean?(name, **options)
|
|
47
|
+
# @api public
|
|
48
|
+
# @see API::Object#boolean?
|
|
49
|
+
# @!method date(name, **options)
|
|
50
|
+
# @api public
|
|
51
|
+
# @see API::Object#date
|
|
52
|
+
# @!method date?(name, **options)
|
|
53
|
+
# @api public
|
|
54
|
+
# @see API::Object#date?
|
|
55
|
+
# @!method datetime(name, **options)
|
|
56
|
+
# @api public
|
|
57
|
+
# @see API::Object#datetime
|
|
58
|
+
# @!method datetime?(name, **options)
|
|
59
|
+
# @api public
|
|
60
|
+
# @see API::Object#datetime?
|
|
61
|
+
# @!method decimal(name, **options)
|
|
62
|
+
# @api public
|
|
63
|
+
# @see API::Object#decimal
|
|
64
|
+
# @!method decimal?(name, **options)
|
|
65
|
+
# @api public
|
|
66
|
+
# @see API::Object#decimal?
|
|
67
|
+
# @!method integer(name, **options)
|
|
68
|
+
# @api public
|
|
69
|
+
# @see API::Object#integer
|
|
70
|
+
# @!method integer?(name, **options)
|
|
71
|
+
# @api public
|
|
72
|
+
# @see API::Object#integer?
|
|
73
|
+
# @!method literal(name, value:, **options)
|
|
74
|
+
# @api public
|
|
75
|
+
# @see API::Object#literal
|
|
76
|
+
# @!method merge(other)
|
|
77
|
+
# @api public
|
|
78
|
+
# @see API::Object#merge
|
|
79
|
+
# @!method number(name, **options)
|
|
80
|
+
# @api public
|
|
81
|
+
# @see API::Object#number
|
|
82
|
+
# @!method number?(name, **options)
|
|
83
|
+
# @api public
|
|
84
|
+
# @see API::Object#number?
|
|
85
|
+
# @!method object(name, **options, &block)
|
|
86
|
+
# @api public
|
|
87
|
+
# @see API::Object#object
|
|
88
|
+
# @!method object?(name, **options, &block)
|
|
89
|
+
# @api public
|
|
90
|
+
# @see API::Object#object?
|
|
91
|
+
# @!method reference(name, **options)
|
|
92
|
+
# @api public
|
|
93
|
+
# @see API::Object#reference
|
|
94
|
+
# @!method reference?(name, **options)
|
|
95
|
+
# @api public
|
|
96
|
+
# @see API::Object#reference?
|
|
97
|
+
# @!method string(name, **options)
|
|
98
|
+
# @api public
|
|
99
|
+
# @see API::Object#string
|
|
100
|
+
# @!method string?(name, **options)
|
|
101
|
+
# @api public
|
|
102
|
+
# @see API::Object#string?
|
|
103
|
+
# @!method time(name, **options)
|
|
104
|
+
# @api public
|
|
105
|
+
# @see API::Object#time
|
|
106
|
+
# @!method time?(name, **options)
|
|
107
|
+
# @api public
|
|
108
|
+
# @see API::Object#time?
|
|
109
|
+
# @!method union(name, **options, &block)
|
|
110
|
+
# @api public
|
|
111
|
+
# @see API::Object#union
|
|
112
|
+
# @!method union?(name, **options, &block)
|
|
113
|
+
# @api public
|
|
114
|
+
# @see API::Object#union?
|
|
115
|
+
# @!method uuid(name, **options)
|
|
116
|
+
# @api public
|
|
117
|
+
# @see API::Object#uuid
|
|
118
|
+
# @!method uuid?(name, **options)
|
|
119
|
+
# @api public
|
|
120
|
+
# @see API::Object#uuid?
|
|
121
|
+
delegate :array,
|
|
122
|
+
:array?,
|
|
123
|
+
:binary,
|
|
124
|
+
:binary?,
|
|
125
|
+
:boolean,
|
|
126
|
+
:boolean?,
|
|
127
|
+
:date,
|
|
128
|
+
:date?,
|
|
129
|
+
:datetime,
|
|
130
|
+
:datetime?,
|
|
131
|
+
:decimal,
|
|
132
|
+
:decimal?,
|
|
133
|
+
:integer,
|
|
134
|
+
:integer?,
|
|
135
|
+
:literal,
|
|
136
|
+
:merge,
|
|
137
|
+
:number,
|
|
138
|
+
:number?,
|
|
139
|
+
:object,
|
|
140
|
+
:object?,
|
|
141
|
+
:reference,
|
|
142
|
+
:reference?,
|
|
143
|
+
:string,
|
|
144
|
+
:string?,
|
|
145
|
+
:time,
|
|
146
|
+
:time?,
|
|
147
|
+
:union,
|
|
148
|
+
:union?,
|
|
149
|
+
:uuid,
|
|
150
|
+
:uuid?,
|
|
151
|
+
to: :@object
|
|
152
|
+
|
|
153
|
+
def initialize(object, options)
|
|
154
|
+
@object = object
|
|
155
|
+
@options = options
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def apply
|
|
159
|
+
raise NotImplementedError
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Capability
|
|
6
|
+
class Result
|
|
7
|
+
attr_reader :data,
|
|
8
|
+
:includes,
|
|
9
|
+
:metadata,
|
|
10
|
+
:serialize_options
|
|
11
|
+
|
|
12
|
+
def initialize(data:, includes: nil, metadata: nil, serialize_options: nil)
|
|
13
|
+
@data = data
|
|
14
|
+
@metadata = metadata
|
|
15
|
+
@includes = includes
|
|
16
|
+
@serialize_options = serialize_options
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Capability
|
|
6
|
+
class Runner
|
|
7
|
+
class << self
|
|
8
|
+
def run(capabilities, data:, representation_class:, request:, wrapper_type:)
|
|
9
|
+
new(capabilities, wrapper_type:).run(data, representation_class, request)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(capabilities, wrapper_type:)
|
|
14
|
+
@capabilities = capabilities
|
|
15
|
+
@wrapper_type = wrapper_type
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run(data, representation_class, request)
|
|
19
|
+
metadata = {}
|
|
20
|
+
serialize_options = {}
|
|
21
|
+
includes = []
|
|
22
|
+
|
|
23
|
+
result_data = @capabilities.reduce(data) do |current, capability|
|
|
24
|
+
result = capability.apply(current, representation_class, request, wrapper_type: @wrapper_type)
|
|
25
|
+
next current unless result
|
|
26
|
+
|
|
27
|
+
metadata.merge!(result.metadata) if result.metadata
|
|
28
|
+
serialize_options.merge!(result.serialize_options || {})
|
|
29
|
+
includes << result.includes if result.includes.present?
|
|
30
|
+
result.data || current
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
includes.concat(representation_class.preloads)
|
|
34
|
+
preloaded = preload_associations(result_data, includes.flatten.compact)
|
|
35
|
+
|
|
36
|
+
[preloaded, metadata, serialize_options]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def preload_associations(data, includes)
|
|
42
|
+
return data if includes.blank?
|
|
43
|
+
|
|
44
|
+
if data.is_a?(ActiveRecord::Relation)
|
|
45
|
+
data.includes(*includes)
|
|
46
|
+
elsif data.is_a?(ActiveRecord::Base)
|
|
47
|
+
ActiveRecord::Associations::Preloader.new(associations: includes, records: [data]).call
|
|
48
|
+
data
|
|
49
|
+
else
|
|
50
|
+
data
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Capability
|
|
6
|
+
module Transformer
|
|
7
|
+
module Request
|
|
8
|
+
# @api public
|
|
9
|
+
# Base class for request transformers.
|
|
10
|
+
#
|
|
11
|
+
# Request transformers modify requests before or after validation.
|
|
12
|
+
# Register transformers in capabilities using {Capability::Base.request_transformer}.
|
|
13
|
+
#
|
|
14
|
+
# @see Standard::Capability::Filtering::RequestTransformer
|
|
15
|
+
# @see Standard::Capability::Writing::RequestTransformer
|
|
16
|
+
#
|
|
17
|
+
# @example Strip whitespace from strings
|
|
18
|
+
# class MyRequestTransformer < Capability::Transformer::Request::Base
|
|
19
|
+
# phase :before
|
|
20
|
+
#
|
|
21
|
+
# def transform
|
|
22
|
+
# request.transform { |data| strip_strings(data) }
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# private
|
|
26
|
+
#
|
|
27
|
+
# def strip_strings(value)
|
|
28
|
+
# case value
|
|
29
|
+
# when String then value.strip
|
|
30
|
+
# when Hash then value.transform_values { |v| strip_strings(v) }
|
|
31
|
+
# when Array then value.map { |v| strip_strings(v) }
|
|
32
|
+
# else value
|
|
33
|
+
# end
|
|
34
|
+
# end
|
|
35
|
+
# end
|
|
36
|
+
class Base
|
|
37
|
+
attr_reader :request
|
|
38
|
+
|
|
39
|
+
class << self
|
|
40
|
+
# @api public
|
|
41
|
+
# The phase for this transformer.
|
|
42
|
+
#
|
|
43
|
+
# @param value [Symbol, nil] (nil) [:after, :before]
|
|
44
|
+
# The phase. Defaults to `:before` when not set.
|
|
45
|
+
# @return [Symbol]
|
|
46
|
+
def phase(value = nil)
|
|
47
|
+
@phase = value if value
|
|
48
|
+
@phase || :before
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def transform(request)
|
|
52
|
+
new(request).transform
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def initialize(request)
|
|
57
|
+
@request = request
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @api public
|
|
61
|
+
# Transforms the request.
|
|
62
|
+
#
|
|
63
|
+
# @return [Request]
|
|
64
|
+
def transform
|
|
65
|
+
raise NotImplementedError
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Capability
|
|
6
|
+
module Transformer
|
|
7
|
+
module Response
|
|
8
|
+
# @api public
|
|
9
|
+
# Base class for response transformers.
|
|
10
|
+
#
|
|
11
|
+
# Response transformers modify responses before they are returned.
|
|
12
|
+
# Register transformers in capabilities using {Capability::Base.response_transformer}.
|
|
13
|
+
#
|
|
14
|
+
# @example Add generated_at to response
|
|
15
|
+
# class MyResponseTransformer < Capability::Transformer::Response::Base
|
|
16
|
+
# def transform
|
|
17
|
+
# response.transform_body { |body| body.merge(generated_at: Time.zone.now) }
|
|
18
|
+
# end
|
|
19
|
+
# end
|
|
20
|
+
class Base
|
|
21
|
+
attr_reader :response
|
|
22
|
+
|
|
23
|
+
class << self
|
|
24
|
+
def transform(response)
|
|
25
|
+
new(response).transform
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def initialize(response)
|
|
30
|
+
@response = response
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# @api public
|
|
34
|
+
# Transforms the response.
|
|
35
|
+
#
|
|
36
|
+
# @return [Response]
|
|
37
|
+
def transform
|
|
38
|
+
raise NotImplementedError
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
class Registry < Apiwork::Registry
|
|
6
|
+
class << self
|
|
7
|
+
def register(adapter_class)
|
|
8
|
+
raise ArgumentError, 'Adapter must inherit from Apiwork::Adapter::Base' unless adapter_class < Base
|
|
9
|
+
raise ArgumentError, "Adapter #{adapter_class} must define an adapter_name" unless adapter_class.adapter_name
|
|
10
|
+
|
|
11
|
+
store[adapter_class.adapter_name] = adapter_class
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Serializer
|
|
6
|
+
module Error
|
|
7
|
+
# @api public
|
|
8
|
+
# Base class for error serializers.
|
|
9
|
+
#
|
|
10
|
+
# Error serializers handle serialization of errors and define
|
|
11
|
+
# error-related types at the API level.
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# class MyErrorSerializer < Serializer::Error::Base
|
|
15
|
+
# api_builder Builder::API
|
|
16
|
+
#
|
|
17
|
+
# def serialize(error, context:)
|
|
18
|
+
# { errors: error.issues.map(&:to_h) }
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
class Base
|
|
22
|
+
class << self
|
|
23
|
+
def serialize(error, context:)
|
|
24
|
+
new.serialize(error, context:)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @api public
|
|
28
|
+
# The data type for this serializer.
|
|
29
|
+
#
|
|
30
|
+
# @param name [Symbol, nil] (nil)
|
|
31
|
+
# The type name.
|
|
32
|
+
# @return [Symbol, nil]
|
|
33
|
+
def data_type(name = nil)
|
|
34
|
+
@data_type = name if name
|
|
35
|
+
@data_type
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @api public
|
|
39
|
+
# The API builder for this serializer.
|
|
40
|
+
#
|
|
41
|
+
# @param klass [Class<Builder::API::Base>, nil] (nil)
|
|
42
|
+
# The builder class.
|
|
43
|
+
# @return [Class<Builder::API::Base>, nil]
|
|
44
|
+
def api_builder(klass = nil)
|
|
45
|
+
@api_builder = klass if klass
|
|
46
|
+
@api_builder
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def api_types(api_class)
|
|
51
|
+
builder_class = self.class.api_builder
|
|
52
|
+
return unless builder_class
|
|
53
|
+
|
|
54
|
+
builder_class.new(api_class, data_type: self.class.data_type).build
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @api public
|
|
58
|
+
# Serializes an error.
|
|
59
|
+
#
|
|
60
|
+
# @param error [Error]
|
|
61
|
+
# The error to serialize.
|
|
62
|
+
# @param context [Hash]
|
|
63
|
+
# The serialization context.
|
|
64
|
+
# @return [Hash]
|
|
65
|
+
def serialize(error, context:)
|
|
66
|
+
raise NotImplementedError
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Serializer
|
|
6
|
+
module Error
|
|
7
|
+
class Default < Base
|
|
8
|
+
class APIBuilder < Adapter::Builder::API::Base
|
|
9
|
+
def build
|
|
10
|
+
enum(:layer, values: %w[http contract domain])
|
|
11
|
+
|
|
12
|
+
object(:issue) do |object|
|
|
13
|
+
object.string(:code)
|
|
14
|
+
object.string(:detail)
|
|
15
|
+
object.array(:path, &:string)
|
|
16
|
+
object.string(:pointer)
|
|
17
|
+
object.object(:meta)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
object(data_type) do |object|
|
|
21
|
+
object.reference(:layer)
|
|
22
|
+
object.array(:issues) do |element|
|
|
23
|
+
element.reference(:issue)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Serializer
|
|
6
|
+
module Error
|
|
7
|
+
# @api public
|
|
8
|
+
# Default error serializer.
|
|
9
|
+
#
|
|
10
|
+
# Serializes errors into a hash with issues array and layer.
|
|
11
|
+
#
|
|
12
|
+
# @example Configuration
|
|
13
|
+
# class MyAdapter < Adapter::Base
|
|
14
|
+
# error_serializer Serializer::Error::Default
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# @example Output
|
|
18
|
+
# {
|
|
19
|
+
# "issues": [{ "code": "invalid", "detail": "...", "path": [...], "pointer": "/..." }],
|
|
20
|
+
# "layer": "contract"
|
|
21
|
+
# }
|
|
22
|
+
class Default < Base
|
|
23
|
+
data_type :error
|
|
24
|
+
|
|
25
|
+
api_builder APIBuilder
|
|
26
|
+
|
|
27
|
+
def serialize(error, context:)
|
|
28
|
+
{
|
|
29
|
+
issues: error.issues.map(&:to_h),
|
|
30
|
+
layer: error.layer,
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|