apiwork 0.0.0.pre → 0.1.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.
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 +638 -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,386 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apiwork
4
+ class Element
5
+ # @!attribute [r] custom_type
6
+ # @api public
7
+ # The custom type name for this element.
8
+ #
9
+ # @return [Symbol, nil]
10
+ # @!attribute [r] discriminator
11
+ # @api public
12
+ # The discriminator for this element.
13
+ #
14
+ # @return [Symbol, nil]
15
+ # @!attribute [r] enum
16
+ # @api public
17
+ # The allowed values for this element.
18
+ #
19
+ # @return [Array, Symbol, nil]
20
+ # @!attribute [r] format
21
+ # @api public
22
+ # The format hint for this element.
23
+ #
24
+ # @return [Symbol, nil]
25
+ # @!attribute [r] inner
26
+ # @api public
27
+ # The inner element for nested arrays.
28
+ #
29
+ # @return [Element, nil]
30
+ # @!attribute [r] max
31
+ # @api public
32
+ # The maximum constraint for this element.
33
+ #
34
+ # @return [Numeric, nil]
35
+ # @!attribute [r] min
36
+ # @api public
37
+ # The minimum constraint for this element.
38
+ #
39
+ # @return [Numeric, nil]
40
+ # @!attribute [r] shape
41
+ # @api public
42
+ # The shape for this element.
43
+ #
44
+ # @return [Object, nil]
45
+ # @!attribute [r] type
46
+ # @api public
47
+ # The type for this element.
48
+ #
49
+ # @return [Symbol, nil]
50
+ # @!attribute [r] value
51
+ # @api public
52
+ # The literal value for this element.
53
+ #
54
+ # @return [Object, nil]
55
+ attr_reader :custom_type,
56
+ :discriminator,
57
+ :enum,
58
+ :format,
59
+ :inner,
60
+ :max,
61
+ :min,
62
+ :shape,
63
+ :type,
64
+ :value
65
+
66
+ def initialize
67
+ @custom_type = nil
68
+ @defined = false
69
+ @discriminator = nil
70
+ @enum = nil
71
+ @format = nil
72
+ @inner = nil
73
+ @max = nil
74
+ @min = nil
75
+ @shape = nil
76
+ @type = nil
77
+ @value = nil
78
+ end
79
+
80
+ # @api public
81
+ # Defines a string.
82
+ #
83
+ # @param enum [Array, Symbol, nil] (nil)
84
+ # The allowed values.
85
+ # @param format [Symbol, nil] (nil) [:date, :datetime, :email, :hostname, :ipv4, :ipv6, :password, :url, :uuid]
86
+ # Format hint for exports. Does not change the type, but exports may add validation or documentation based on it.
87
+ # Valid formats by type: `:string`.
88
+ # @param max [Integer, nil] (nil)
89
+ # The maximum length.
90
+ # @param min [Integer, nil] (nil)
91
+ # The minimum length.
92
+ # @return [void]
93
+ #
94
+ # @example Basic string
95
+ # array :tags do
96
+ # string
97
+ # end
98
+ #
99
+ # @example With length constraints
100
+ # array :tags do
101
+ # string min: 1, max: 50
102
+ # end
103
+ def string(enum: nil, format: nil, max: nil, min: nil)
104
+ of(:string, enum:, format:, max:, min:)
105
+ end
106
+
107
+ # @api public
108
+ # Defines an integer.
109
+ #
110
+ # @param enum [Array, Symbol, nil] (nil)
111
+ # The allowed values.
112
+ # @param max [Integer, nil] (nil)
113
+ # The maximum value.
114
+ # @param min [Integer, nil] (nil)
115
+ # The minimum value.
116
+ # @return [void]
117
+ #
118
+ # @example Basic integer
119
+ # array :counts do
120
+ # integer
121
+ # end
122
+ #
123
+ # @example With range constraints
124
+ # array :scores do
125
+ # integer min: 0, max: 100
126
+ # end
127
+ def integer(enum: nil, max: nil, min: nil)
128
+ of(:integer, enum:, max:, min:)
129
+ end
130
+
131
+ # @api public
132
+ # Defines a decimal.
133
+ #
134
+ # @param max [Numeric, nil] (nil)
135
+ # The maximum value.
136
+ # @param min [Numeric, nil] (nil)
137
+ # The minimum value.
138
+ # @return [void]
139
+ #
140
+ # @example Basic decimal
141
+ # array :amounts do
142
+ # decimal
143
+ # end
144
+ #
145
+ # @example With range constraints
146
+ # array :prices do
147
+ # decimal min: 0
148
+ # end
149
+ def decimal(max: nil, min: nil)
150
+ of(:decimal, max:, min:)
151
+ end
152
+
153
+ # @api public
154
+ # Defines a number.
155
+ #
156
+ # @param max [Numeric, nil] (nil)
157
+ # The maximum value.
158
+ # @param min [Numeric, nil] (nil)
159
+ # The minimum value.
160
+ # @return [void]
161
+ #
162
+ # @example Basic number
163
+ # array :coordinates do
164
+ # number
165
+ # end
166
+ #
167
+ # @example With range constraints
168
+ # array :latitudes do
169
+ # number min: -90, max: 90
170
+ # end
171
+ def number(max: nil, min: nil)
172
+ of(:number, max:, min:)
173
+ end
174
+
175
+ # @api public
176
+ # Defines a boolean.
177
+ #
178
+ # @return [void]
179
+ #
180
+ # @example
181
+ # array :flags do
182
+ # boolean
183
+ # end
184
+ def boolean
185
+ of(:boolean)
186
+ end
187
+
188
+ # @api public
189
+ # Defines a datetime.
190
+ #
191
+ # @return [void]
192
+ #
193
+ # @example
194
+ # array :timestamps do
195
+ # datetime
196
+ # end
197
+ def datetime
198
+ of(:datetime)
199
+ end
200
+
201
+ # @api public
202
+ # Defines a date.
203
+ #
204
+ # @return [void]
205
+ #
206
+ # @example
207
+ # array :dates do
208
+ # date
209
+ # end
210
+ def date
211
+ of(:date)
212
+ end
213
+
214
+ # @api public
215
+ # Defines a UUID.
216
+ #
217
+ # @return [void]
218
+ #
219
+ # @example
220
+ # array :ids do
221
+ # uuid
222
+ # end
223
+ def uuid
224
+ of(:uuid)
225
+ end
226
+
227
+ # @api public
228
+ # Defines a time.
229
+ #
230
+ # @return [void]
231
+ #
232
+ # @example
233
+ # array :times do
234
+ # time
235
+ # end
236
+ def time
237
+ of(:time)
238
+ end
239
+
240
+ # @api public
241
+ # Defines a binary.
242
+ #
243
+ # @return [void]
244
+ #
245
+ # @example
246
+ # array :blobs do
247
+ # binary
248
+ # end
249
+ def binary
250
+ of(:binary)
251
+ end
252
+
253
+ # @api public
254
+ # Defines an object.
255
+ #
256
+ # @yield block defining nested structure
257
+ # @yieldparam object [Object]
258
+ # @return [void]
259
+ #
260
+ # @example instance_eval style
261
+ # array :items do
262
+ # object do
263
+ # string :name
264
+ # decimal :price
265
+ # end
266
+ # end
267
+ #
268
+ # @example yield style
269
+ # array :items do |element|
270
+ # element.object do |object|
271
+ # object.string :name
272
+ # object.decimal :price
273
+ # end
274
+ # end
275
+ def object(&block)
276
+ of(:object, &block)
277
+ end
278
+
279
+ # @api public
280
+ # Defines an array.
281
+ #
282
+ # @yield block defining element type
283
+ # @yieldparam element [Element]
284
+ # @return [void]
285
+ #
286
+ # @example instance_eval style
287
+ # array :matrix do
288
+ # array do
289
+ # integer
290
+ # end
291
+ # end
292
+ #
293
+ # @example yield style
294
+ # array :matrix do |element|
295
+ # element.array do |inner|
296
+ # inner.integer
297
+ # end
298
+ # end
299
+ def array(&block)
300
+ of(:array, &block)
301
+ end
302
+
303
+ # @api public
304
+ # Defines a union.
305
+ #
306
+ # @param discriminator [Symbol, nil] (nil)
307
+ # The discriminator field name.
308
+ # @yield block defining union variants
309
+ # @yieldparam union [Union]
310
+ # @return [void]
311
+ #
312
+ # @example instance_eval style
313
+ # array :payments do
314
+ # union discriminator: :type do
315
+ # variant tag: 'card' do
316
+ # object do
317
+ # string :last_four
318
+ # end
319
+ # end
320
+ # end
321
+ # end
322
+ #
323
+ # @example yield style
324
+ # array :payments do |element|
325
+ # element.union discriminator: :type do |union|
326
+ # union.variant tag: 'card' do |variant|
327
+ # variant.object do |object|
328
+ # object.string :last_four
329
+ # end
330
+ # end
331
+ # end
332
+ # end
333
+ def union(discriminator: nil, &block)
334
+ of(:union, discriminator:, &block)
335
+ end
336
+
337
+ # @api public
338
+ # Defines a literal value.
339
+ #
340
+ # @param value [Object]
341
+ # The literal value.
342
+ # @return [void]
343
+ #
344
+ # @example
345
+ # variant tag: 'card' do
346
+ # literal value: 'card'
347
+ # end
348
+ def literal(value:)
349
+ of(:literal, value:)
350
+ end
351
+
352
+ # @api public
353
+ # Defines a reference to a named type.
354
+ #
355
+ # @param type_name [Symbol]
356
+ # The type to reference.
357
+ # @return [void]
358
+ #
359
+ # @example
360
+ # array :items do
361
+ # reference :item
362
+ # end
363
+ def reference(type_name)
364
+ of(type_name)
365
+ end
366
+
367
+ def validate!
368
+ raise ArgumentError, 'must define exactly one type' unless @defined
369
+ end
370
+
371
+ def of(type, **options, &block)
372
+ raise NotImplementedError, "#{self.class} must implement #of"
373
+ end
374
+
375
+ private
376
+
377
+ def set_type(type_value, enum: nil, format: nil, max: nil, min: nil)
378
+ @type = type_value
379
+ @enum = enum
380
+ @format = format
381
+ @max = max
382
+ @min = min
383
+ @defined = true
384
+ end
385
+ end
386
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apiwork
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace Apiwork
6
+ engine_name 'apiwork'
7
+
8
+ rake_tasks do
9
+ load File.expand_path('../tasks/apiwork.rake', __dir__)
10
+ end
11
+
12
+ initializer 'apiwork.i18n' do
13
+ config.i18n.load_path += Dir[File.expand_path('../../config/locales/**/*.yml', __dir__)]
14
+ end
15
+
16
+ config.to_prepare do
17
+ Apiwork.prepare!
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apiwork
4
+ class Error < StandardError
5
+ end
6
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apiwork
4
+ module ErrorCode
5
+ # @api public
6
+ # Represents a registered error code.
7
+ #
8
+ # Error codes define HTTP status codes and behavior for API errors.
9
+ # Retrieved via {ErrorCode.find} or {ErrorCode.find!}.
10
+ #
11
+ # @!attribute [r] key
12
+ # @api public
13
+ # The key for this error code.
14
+ #
15
+ # @return [Symbol]
16
+ #
17
+ # @!attribute [r] status
18
+ # @api public
19
+ # The status for this error code.
20
+ #
21
+ # @return [Integer]
22
+ #
23
+ # @example
24
+ # error_code = Apiwork::ErrorCode.find!(:not_found)
25
+ # error_code.key # => :not_found
26
+ # error_code.status # => 404
27
+ # error_code.attach_path? # => true
28
+ Definition = Struct.new(:key, :status, :attach_path, keyword_init: true) do
29
+ # @api public
30
+ # Whether this error code attaches the request path.
31
+ #
32
+ # @return [Boolean]
33
+ def attach_path?
34
+ attach_path
35
+ end
36
+
37
+ # @api public
38
+ # The description for this error code.
39
+ #
40
+ # @param locale_key [String, nil] (nil)
41
+ # The I18n namespace for API-specific translations.
42
+ # @return [String]
43
+ #
44
+ # @example
45
+ # error_code = Apiwork::ErrorCode.find!(:not_found)
46
+ # error_code.description # => "Not Found"
47
+ # error_code.description(locale_key: 'api/v1') # apiwork.apis.api/v1.error_codes.not_found.description
48
+ def description(locale_key: nil)
49
+ if locale_key
50
+ api_key = :"apiwork.apis.#{locale_key}.error_codes.#{key}.description"
51
+ result = I18n.translate(api_key, default: nil)
52
+ return result if result
53
+ end
54
+
55
+ global_key = :"apiwork.error_codes.#{key}.description"
56
+ result = I18n.translate(global_key, default: nil)
57
+ return result if result
58
+
59
+ key.to_s.titleize
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apiwork
4
+ module ErrorCode
5
+ class Registry < Apiwork::Registry
6
+ class << self
7
+ def register(key, attach_path: false, status:)
8
+ key = normalize_key(key)
9
+ status = Integer(status)
10
+
11
+ raise ArgumentError, "Status must be 400-599, got #{status}" unless (400..599).cover?(status)
12
+
13
+ store[key] = Definition.new(attach_path:, key:, status:)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apiwork
4
+ # @api public
5
+ module ErrorCode
6
+ DEFAULTS = {
7
+ bad_gateway: {
8
+ status: 502,
9
+ },
10
+ bad_request: {
11
+ status: 400,
12
+ },
13
+ conflict: {
14
+ status: 409,
15
+ },
16
+ forbidden: {
17
+ status: 403,
18
+ },
19
+ gateway_timeout: {
20
+ status: 504,
21
+ },
22
+ gone: {
23
+ status: 410,
24
+ },
25
+ internal_server_error: {
26
+ status: 500,
27
+ },
28
+ locked: {
29
+ status: 423,
30
+ },
31
+ method_not_allowed: {
32
+ status: 405,
33
+ },
34
+ not_acceptable: {
35
+ status: 406,
36
+ },
37
+ not_found: {
38
+ attach_path: true,
39
+ status: 404,
40
+ },
41
+ not_implemented: {
42
+ status: 501,
43
+ },
44
+ payment_required: {
45
+ status: 402,
46
+ },
47
+ precondition_failed: {
48
+ status: 412,
49
+ },
50
+ request_timeout: {
51
+ status: 408,
52
+ },
53
+ service_unavailable: {
54
+ status: 503,
55
+ },
56
+ too_many_requests: {
57
+ status: 429,
58
+ },
59
+ unauthorized: {
60
+ status: 401,
61
+ },
62
+ unprocessable_entity: {
63
+ status: 422,
64
+ },
65
+ unsupported_media_type: {
66
+ status: 415,
67
+ },
68
+ }.freeze
69
+
70
+ class << self
71
+ # @!method find(key)
72
+ # @api public
73
+ # Finds an error code by key.
74
+ # @param key [Symbol]
75
+ # The error code key.
76
+ # @return [ErrorCode::Definition, nil]
77
+ # @see .find!
78
+ # @example
79
+ # Apiwork::ErrorCode.find(:not_found)
80
+ #
81
+ # @!method find!(key)
82
+ # @api public
83
+ # Finds an error code by key.
84
+ # @param key [Symbol]
85
+ # The error code key.
86
+ # @return [ErrorCode::Definition]
87
+ # @raise [KeyError] if the error code is not found
88
+ # @see .find
89
+ # @example
90
+ # Apiwork::ErrorCode.find!(:not_found)
91
+ #
92
+ # @!method register(key, attach_path: false, status:)
93
+ # @api public
94
+ # Registers a custom error code for use in API responses.
95
+ #
96
+ # Error codes are used with `raises` declarations and `expose_error`
97
+ # in controllers. Built-in codes (400-504) are pre-registered.
98
+ #
99
+ # @param key [Symbol]
100
+ # The unique identifier for the error code.
101
+ # @param attach_path [Boolean] (false)
102
+ # Include request path in error response.
103
+ # @param status [Integer]
104
+ # The HTTP status code (must be 400-599).
105
+ # @return [ErrorCode::Definition]
106
+ # @raise [ArgumentError] if status is outside 400-599 range
107
+ # @see Issue
108
+ #
109
+ # @example Register custom error code
110
+ # Apiwork::ErrorCode.register :resource_locked, status: 423
111
+ #
112
+ # @example With path attachment
113
+ # Apiwork::ErrorCode.register :not_found, status: 404, attach_path: true
114
+ delegate :clear!,
115
+ :exists?,
116
+ :find,
117
+ :find!,
118
+ :keys,
119
+ :register,
120
+ :values,
121
+ to: Registry
122
+
123
+ def key_for_status(status)
124
+ DEFAULTS.find { |_key, config| config[:status] == status }&.first
125
+ end
126
+
127
+ def register_defaults!
128
+ DEFAULTS.each { |key, options| register(key, **options) }
129
+ end
130
+ end
131
+ end
132
+ end