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.
Files changed (202) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +2 -2
  3. data/README.md +117 -1
  4. data/Rakefile +5 -3
  5. data/app/controllers/apiwork/errors_controller.rb +13 -0
  6. data/app/controllers/apiwork/exports_controller.rb +22 -0
  7. data/lib/apiwork/abstractable.rb +26 -0
  8. data/lib/apiwork/adapter/base.rb +369 -0
  9. data/lib/apiwork/adapter/builder/api/base.rb +66 -0
  10. data/lib/apiwork/adapter/builder/contract/base.rb +86 -0
  11. data/lib/apiwork/adapter/capability/api/base.rb +51 -0
  12. data/lib/apiwork/adapter/capability/api/scope.rb +64 -0
  13. data/lib/apiwork/adapter/capability/base.rb +291 -0
  14. data/lib/apiwork/adapter/capability/contract/base.rb +37 -0
  15. data/lib/apiwork/adapter/capability/contract/scope.rb +110 -0
  16. data/lib/apiwork/adapter/capability/operation/base.rb +172 -0
  17. data/lib/apiwork/adapter/capability/operation/metadata_shape.rb +165 -0
  18. data/lib/apiwork/adapter/capability/result.rb +21 -0
  19. data/lib/apiwork/adapter/capability/runner.rb +56 -0
  20. data/lib/apiwork/adapter/capability/transformer/request/base.rb +72 -0
  21. data/lib/apiwork/adapter/capability/transformer/response/base.rb +45 -0
  22. data/lib/apiwork/adapter/registry.rb +16 -0
  23. data/lib/apiwork/adapter/serializer/error/base.rb +72 -0
  24. data/lib/apiwork/adapter/serializer/error/default/api_builder.rb +32 -0
  25. data/lib/apiwork/adapter/serializer/error/default.rb +37 -0
  26. data/lib/apiwork/adapter/serializer/resource/base.rb +84 -0
  27. data/lib/apiwork/adapter/serializer/resource/default/contract_builder.rb +209 -0
  28. data/lib/apiwork/adapter/serializer/resource/default.rb +39 -0
  29. data/lib/apiwork/adapter/standard/capability/filtering/api_builder.rb +75 -0
  30. data/lib/apiwork/adapter/standard/capability/filtering/constants.rb +37 -0
  31. data/lib/apiwork/adapter/standard/capability/filtering/contract_builder.rb +193 -0
  32. data/lib/apiwork/adapter/standard/capability/filtering/operation/filter/builder.rb +47 -0
  33. data/lib/apiwork/adapter/standard/capability/filtering/operation/filter/operator_builder.rb +36 -0
  34. data/lib/apiwork/adapter/standard/capability/filtering/operation/filter.rb +462 -0
  35. data/lib/apiwork/adapter/standard/capability/filtering/operation.rb +22 -0
  36. data/lib/apiwork/adapter/standard/capability/filtering/request_transformer.rb +47 -0
  37. data/lib/apiwork/adapter/standard/capability/filtering.rb +18 -0
  38. data/lib/apiwork/adapter/standard/capability/including/contract_builder.rb +169 -0
  39. data/lib/apiwork/adapter/standard/capability/including/operation.rb +20 -0
  40. data/lib/apiwork/adapter/standard/capability/including.rb +16 -0
  41. data/lib/apiwork/adapter/standard/capability/pagination/api_builder.rb +34 -0
  42. data/lib/apiwork/adapter/standard/capability/pagination/contract_builder.rb +35 -0
  43. data/lib/apiwork/adapter/standard/capability/pagination/operation/paginate/cursor.rb +84 -0
  44. data/lib/apiwork/adapter/standard/capability/pagination/operation/paginate/offset.rb +66 -0
  45. data/lib/apiwork/adapter/standard/capability/pagination/operation/paginate.rb +24 -0
  46. data/lib/apiwork/adapter/standard/capability/pagination/operation.rb +24 -0
  47. data/lib/apiwork/adapter/standard/capability/pagination.rb +21 -0
  48. data/lib/apiwork/adapter/standard/capability/sorting/api_builder.rb +19 -0
  49. data/lib/apiwork/adapter/standard/capability/sorting/contract_builder.rb +84 -0
  50. data/lib/apiwork/adapter/standard/capability/sorting/operation/sort.rb +83 -0
  51. data/lib/apiwork/adapter/standard/capability/sorting/operation.rb +22 -0
  52. data/lib/apiwork/adapter/standard/capability/sorting.rb +17 -0
  53. data/lib/apiwork/adapter/standard/capability/writing/constants.rb +15 -0
  54. data/lib/apiwork/adapter/standard/capability/writing/contract_builder.rb +253 -0
  55. data/lib/apiwork/adapter/standard/capability/writing/operation/issue_mapper.rb +210 -0
  56. data/lib/apiwork/adapter/standard/capability/writing/operation.rb +32 -0
  57. data/lib/apiwork/adapter/standard/capability/writing/request_transformer.rb +37 -0
  58. data/lib/apiwork/adapter/standard/capability/writing.rb +17 -0
  59. data/lib/apiwork/adapter/standard/includes_resolver.rb +106 -0
  60. data/lib/apiwork/adapter/standard.rb +22 -0
  61. data/lib/apiwork/adapter/wrapper/base.rb +70 -0
  62. data/lib/apiwork/adapter/wrapper/collection/base.rb +60 -0
  63. data/lib/apiwork/adapter/wrapper/collection/default.rb +47 -0
  64. data/lib/apiwork/adapter/wrapper/error/base.rb +30 -0
  65. data/lib/apiwork/adapter/wrapper/error/default.rb +34 -0
  66. data/lib/apiwork/adapter/wrapper/member/base.rb +58 -0
  67. data/lib/apiwork/adapter/wrapper/member/default.rb +40 -0
  68. data/lib/apiwork/adapter/wrapper/shape.rb +203 -0
  69. data/lib/apiwork/adapter.rb +50 -0
  70. data/lib/apiwork/api/base.rb +802 -0
  71. data/lib/apiwork/api/element.rb +110 -0
  72. data/lib/apiwork/api/enum_registry/definition.rb +51 -0
  73. data/lib/apiwork/api/enum_registry.rb +98 -0
  74. data/lib/apiwork/api/info/contact.rb +67 -0
  75. data/lib/apiwork/api/info/license.rb +50 -0
  76. data/lib/apiwork/api/info/server.rb +50 -0
  77. data/lib/apiwork/api/info.rb +221 -0
  78. data/lib/apiwork/api/object.rb +235 -0
  79. data/lib/apiwork/api/registry.rb +33 -0
  80. data/lib/apiwork/api/representation_registry.rb +76 -0
  81. data/lib/apiwork/api/resource/action.rb +41 -0
  82. data/lib/apiwork/api/resource.rb +648 -0
  83. data/lib/apiwork/api/router.rb +104 -0
  84. data/lib/apiwork/api/type_registry/definition.rb +117 -0
  85. data/lib/apiwork/api/type_registry.rb +99 -0
  86. data/lib/apiwork/api/union.rb +49 -0
  87. data/lib/apiwork/api.rb +85 -0
  88. data/lib/apiwork/configurable.rb +71 -0
  89. data/lib/apiwork/configuration/option.rb +125 -0
  90. data/lib/apiwork/configuration/validatable.rb +25 -0
  91. data/lib/apiwork/configuration.rb +95 -0
  92. data/lib/apiwork/configuration_error.rb +6 -0
  93. data/lib/apiwork/constraint_error.rb +20 -0
  94. data/lib/apiwork/contract/action/request.rb +79 -0
  95. data/lib/apiwork/contract/action/response.rb +87 -0
  96. data/lib/apiwork/contract/action.rb +258 -0
  97. data/lib/apiwork/contract/base.rb +714 -0
  98. data/lib/apiwork/contract/element.rb +130 -0
  99. data/lib/apiwork/contract/object/coercer.rb +194 -0
  100. data/lib/apiwork/contract/object/deserializer.rb +101 -0
  101. data/lib/apiwork/contract/object/transformer.rb +95 -0
  102. data/lib/apiwork/contract/object/validator/result.rb +27 -0
  103. data/lib/apiwork/contract/object/validator.rb +734 -0
  104. data/lib/apiwork/contract/object.rb +566 -0
  105. data/lib/apiwork/contract/request_parser/result.rb +25 -0
  106. data/lib/apiwork/contract/request_parser.rb +72 -0
  107. data/lib/apiwork/contract/response_parser/result.rb +25 -0
  108. data/lib/apiwork/contract/response_parser.rb +35 -0
  109. data/lib/apiwork/contract/union.rb +56 -0
  110. data/lib/apiwork/contract_error.rb +9 -0
  111. data/lib/apiwork/controller.rb +300 -0
  112. data/lib/apiwork/domain_error.rb +13 -0
  113. data/lib/apiwork/element.rb +386 -0
  114. data/lib/apiwork/engine.rb +20 -0
  115. data/lib/apiwork/error.rb +6 -0
  116. data/lib/apiwork/error_code/definition.rb +63 -0
  117. data/lib/apiwork/error_code/registry.rb +18 -0
  118. data/lib/apiwork/error_code.rb +132 -0
  119. data/lib/apiwork/export/base.rb +291 -0
  120. data/lib/apiwork/export/open_api.rb +600 -0
  121. data/lib/apiwork/export/pipeline/writer.rb +66 -0
  122. data/lib/apiwork/export/pipeline.rb +84 -0
  123. data/lib/apiwork/export/registry.rb +16 -0
  124. data/lib/apiwork/export/surface_resolver.rb +189 -0
  125. data/lib/apiwork/export/type_analysis.rb +170 -0
  126. data/lib/apiwork/export/type_script.rb +23 -0
  127. data/lib/apiwork/export/type_script_mapper.rb +349 -0
  128. data/lib/apiwork/export/zod.rb +39 -0
  129. data/lib/apiwork/export/zod_mapper.rb +421 -0
  130. data/lib/apiwork/export.rb +80 -0
  131. data/lib/apiwork/http_error.rb +16 -0
  132. data/lib/apiwork/introspection/action/request.rb +66 -0
  133. data/lib/apiwork/introspection/action/response.rb +57 -0
  134. data/lib/apiwork/introspection/action.rb +124 -0
  135. data/lib/apiwork/introspection/api/info/contact.rb +59 -0
  136. data/lib/apiwork/introspection/api/info/license.rb +49 -0
  137. data/lib/apiwork/introspection/api/info/server.rb +50 -0
  138. data/lib/apiwork/introspection/api/info.rb +107 -0
  139. data/lib/apiwork/introspection/api/resource.rb +83 -0
  140. data/lib/apiwork/introspection/api.rb +92 -0
  141. data/lib/apiwork/introspection/contract.rb +63 -0
  142. data/lib/apiwork/introspection/dump/action.rb +101 -0
  143. data/lib/apiwork/introspection/dump/api.rb +119 -0
  144. data/lib/apiwork/introspection/dump/contract.rb +129 -0
  145. data/lib/apiwork/introspection/dump/param.rb +486 -0
  146. data/lib/apiwork/introspection/dump/resource.rb +112 -0
  147. data/lib/apiwork/introspection/dump/type.rb +339 -0
  148. data/lib/apiwork/introspection/dump.rb +17 -0
  149. data/lib/apiwork/introspection/enum.rb +63 -0
  150. data/lib/apiwork/introspection/error_code.rb +44 -0
  151. data/lib/apiwork/introspection/param/array.rb +88 -0
  152. data/lib/apiwork/introspection/param/base.rb +285 -0
  153. data/lib/apiwork/introspection/param/binary.rb +73 -0
  154. data/lib/apiwork/introspection/param/boolean.rb +73 -0
  155. data/lib/apiwork/introspection/param/date.rb +73 -0
  156. data/lib/apiwork/introspection/param/date_time.rb +73 -0
  157. data/lib/apiwork/introspection/param/decimal.rb +121 -0
  158. data/lib/apiwork/introspection/param/integer.rb +131 -0
  159. data/lib/apiwork/introspection/param/literal.rb +45 -0
  160. data/lib/apiwork/introspection/param/number.rb +121 -0
  161. data/lib/apiwork/introspection/param/object.rb +59 -0
  162. data/lib/apiwork/introspection/param/reference.rb +45 -0
  163. data/lib/apiwork/introspection/param/string.rb +122 -0
  164. data/lib/apiwork/introspection/param/time.rb +73 -0
  165. data/lib/apiwork/introspection/param/union.rb +57 -0
  166. data/lib/apiwork/introspection/param/unknown.rb +26 -0
  167. data/lib/apiwork/introspection/param/uuid.rb +73 -0
  168. data/lib/apiwork/introspection/param.rb +31 -0
  169. data/lib/apiwork/introspection/type.rb +129 -0
  170. data/lib/apiwork/introspection.rb +28 -0
  171. data/lib/apiwork/issue.rb +80 -0
  172. data/lib/apiwork/json_pointer.rb +21 -0
  173. data/lib/apiwork/object.rb +1618 -0
  174. data/lib/apiwork/reference_generator.rb +622 -0
  175. data/lib/apiwork/registry.rb +56 -0
  176. data/lib/apiwork/representation/association.rb +391 -0
  177. data/lib/apiwork/representation/attribute.rb +335 -0
  178. data/lib/apiwork/representation/base.rb +819 -0
  179. data/lib/apiwork/representation/deserializer.rb +95 -0
  180. data/lib/apiwork/representation/element.rb +128 -0
  181. data/lib/apiwork/representation/inheritance.rb +78 -0
  182. data/lib/apiwork/representation/model_detector.rb +75 -0
  183. data/lib/apiwork/representation/root_key.rb +35 -0
  184. data/lib/apiwork/representation/serializer.rb +127 -0
  185. data/lib/apiwork/request.rb +79 -0
  186. data/lib/apiwork/response.rb +56 -0
  187. data/lib/apiwork/union.rb +102 -0
  188. data/lib/apiwork/version.rb +2 -2
  189. data/lib/apiwork.rb +61 -3
  190. data/lib/generators/apiwork/api_generator.rb +38 -0
  191. data/lib/generators/apiwork/contract_generator.rb +25 -0
  192. data/lib/generators/apiwork/install_generator.rb +27 -0
  193. data/lib/generators/apiwork/representation_generator.rb +25 -0
  194. data/lib/generators/apiwork/templates/api/api.rb.tt +4 -0
  195. data/lib/generators/apiwork/templates/contract/contract.rb.tt +6 -0
  196. data/lib/generators/apiwork/templates/install/application_contract.rb.tt +5 -0
  197. data/lib/generators/apiwork/templates/install/application_representation.rb.tt +5 -0
  198. data/lib/generators/apiwork/templates/representation/representation.rb.tt +6 -0
  199. data/lib/tasks/apiwork.rake +102 -0
  200. metadata +319 -19
  201. data/.rubocop.yml +0 -8
  202. 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