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,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apiwork
4
+ # @api public
5
+ # Typed access to configuration values with automatic defaults.
6
+ #
7
+ # @see API::Base#adapter_config
8
+ # @see Representation::Base.adapter_config
9
+ #
10
+ # @example Reading values
11
+ # config.pagination.default_size # => 20
12
+ # config.pagination.strategy # => :offset
13
+ #
14
+ # @example Using dig for dynamic access
15
+ # config.dig(:pagination, :default_size) # => 20
16
+ class Configuration
17
+ def initialize(options_source, storage = {})
18
+ @options = extract_options(options_source)
19
+ @storage = storage
20
+ end
21
+
22
+ def method_missing(name, *args, &block)
23
+ option = @options[name]
24
+ raise ConfigurationError, "Unknown option: #{name}" unless option
25
+
26
+ if args.empty? && !block
27
+ stored = @storage[name]
28
+
29
+ if option.nested?
30
+ nested_storage = stored || {}
31
+ return Configuration.new(option, nested_storage)
32
+ end
33
+
34
+ return stored.nil? ? option.default : stored
35
+ end
36
+
37
+ value = args.first
38
+ if block && option.nested?
39
+ @storage[name] ||= {}
40
+ nested = Configuration.new(option, @storage[name])
41
+ block.arity.positive? ? yield(nested) : nested.instance_eval(&block)
42
+ else
43
+ option.validate!(value)
44
+ @storage[name] = value
45
+ end
46
+ end
47
+
48
+ def respond_to_missing?(name, include_private = false)
49
+ @options.key?(name) || super
50
+ end
51
+
52
+ def merge(hash)
53
+ Configuration.new(@options, @storage.deep_merge(hash))
54
+ end
55
+
56
+ # @api public
57
+ # Accesses nested configuration values by key path.
58
+ #
59
+ # @param keys [Symbol]
60
+ # One or more keys to traverse.
61
+ #
62
+ # @example
63
+ # config.dig(:pagination) # => #<Apiwork::Configuration:...>
64
+ # config.dig(:pagination, :strategy) # => :offset
65
+ def dig(*keys)
66
+ keys.compact.reduce(self) { |config, key| config.public_send(key) }
67
+ end
68
+
69
+ # @api public
70
+ # Converts the configuration to a hash.
71
+ #
72
+ # @return [Hash]
73
+ #
74
+ # @example
75
+ # config.to_h # => { pagination: { strategy: :offset, default_size: 20 } }
76
+ def to_h
77
+ @options.each_with_object({}) do |(name, option), result|
78
+ if option.nested?
79
+ result[name] = Configuration.new(option, @storage[name] || {}).to_h
80
+ else
81
+ stored = @storage[name]
82
+ result[name] = stored.nil? ? option.default : stored
83
+ end
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def extract_options(source)
90
+ return source if source.is_a?(Hash)
91
+
92
+ source.respond_to?(:options) ? source.options : source.children
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apiwork
4
+ class ConfigurationError < Error
5
+ end
6
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apiwork
4
+ class ConstraintError < Error
5
+ attr_reader :issues
6
+
7
+ def initialize(issues)
8
+ @issues = Array(issues)
9
+ super(@issues.map(&:detail).join('; '))
10
+ end
11
+
12
+ def error_code
13
+ @error_code ||= ErrorCode.find!(:bad_request)
14
+ end
15
+
16
+ def status
17
+ error_code.status
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apiwork
4
+ module Contract
5
+ class Action
6
+ # @api public
7
+ # Defines query and body for a request.
8
+ #
9
+ # Returns {Contract::Object} via `query` and `body`.
10
+ class Request
11
+ attr_reader :action_name,
12
+ :contract_class
13
+
14
+ def initialize(contract_class, action_name)
15
+ @contract_class = contract_class
16
+ @action_name = action_name
17
+ @query = nil
18
+ @body = nil
19
+ end
20
+
21
+ # @api public
22
+ # Defines query parameters for this request.
23
+ #
24
+ # Query parameters are parsed from the URL query string.
25
+ #
26
+ # @yield block for defining query params (instance_eval style)
27
+ # @yieldparam query [Contract::Object]
28
+ # @return [Contract::Object]
29
+ #
30
+ # @example instance_eval style
31
+ # query do
32
+ # integer? :page
33
+ # string? :status, enum: :status
34
+ # end
35
+ #
36
+ # @example yield style
37
+ # query do |query|
38
+ # query.integer? :page
39
+ # query.string? :status, enum: :status
40
+ # end
41
+ def query(&block)
42
+ @query ||= Object.new(@contract_class, action_name: @action_name)
43
+ if block
44
+ block.arity.positive? ? yield(@query) : @query.instance_eval(&block)
45
+ end
46
+ @query
47
+ end
48
+
49
+ # @api public
50
+ # Defines the request body for this request.
51
+ #
52
+ # Body is parsed from the JSON request body.
53
+ #
54
+ # @yield block for defining body params (instance_eval style)
55
+ # @yieldparam body [Contract::Object]
56
+ # @return [Contract::Object]
57
+ #
58
+ # @example instance_eval style
59
+ # body do
60
+ # string :title
61
+ # decimal :amount
62
+ # end
63
+ #
64
+ # @example yield style
65
+ # body do |body|
66
+ # body.string :title
67
+ # body.decimal :amount
68
+ # end
69
+ def body(&block)
70
+ @body ||= Object.new(@contract_class, action_name: @action_name)
71
+ if block
72
+ block.arity.positive? ? yield(@body) : @body.instance_eval(&block)
73
+ end
74
+ @body
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apiwork
4
+ module Contract
5
+ class Action
6
+ # @api public
7
+ # Defines body for a response.
8
+ #
9
+ # Returns {Contract::Object} via `body`.
10
+ class Response
11
+ attr_reader :action_name,
12
+ :contract_class
13
+
14
+ attr_accessor :result_wrapper
15
+
16
+ def initialize(contract_class, action_name)
17
+ @contract_class = contract_class
18
+ @action_name = action_name
19
+ @body = nil
20
+ @result_wrapper = nil
21
+ @no_content = false
22
+ end
23
+
24
+ # @api public
25
+ # Whether this response has no content.
26
+ #
27
+ # @return [Boolean]
28
+ def no_content?
29
+ @no_content
30
+ end
31
+
32
+ # @api public
33
+ # Declares this response as 204 No Content.
34
+ #
35
+ # Use for actions that don't return a response body,
36
+ # like DELETE or actions that only perform side effects.
37
+ #
38
+ # @return [void]
39
+ #
40
+ # @example
41
+ # action :destroy do
42
+ # response { no_content! }
43
+ # end
44
+ #
45
+ # @example Archive action
46
+ # action :archive do
47
+ # response { no_content! }
48
+ # end
49
+ def no_content!
50
+ @no_content = true
51
+ end
52
+
53
+ # @api public
54
+ # Defines the response body for this response.
55
+ #
56
+ # @yield block for defining body params (instance_eval style)
57
+ # @yieldparam body [Contract::Object]
58
+ # @return [Contract::Object]
59
+ #
60
+ # @example instance_eval style
61
+ # body do
62
+ # integer :id
63
+ # string :title
64
+ # decimal :amount
65
+ # end
66
+ #
67
+ # @example yield style
68
+ # body do |body|
69
+ # body.integer :id
70
+ # body.string :title
71
+ # body.decimal :amount
72
+ # end
73
+ def body(&block)
74
+ if block
75
+ @body ||= Object.new(
76
+ @contract_class,
77
+ action_name: @action_name,
78
+ wrapped: true,
79
+ )
80
+ block.arity.positive? ? yield(@body) : @body.instance_eval(&block)
81
+ end
82
+ @body
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,258 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apiwork
4
+ module Contract
5
+ # @api public
6
+ # Defines request/response structure for an action.
7
+ #
8
+ # Returns {Action::Request} via `request` and {Action::Response} via `response`.
9
+ class Action
10
+ attr_reader :contract_class,
11
+ :name
12
+
13
+ def initialize(contract_class, name, replace: false)
14
+ @name = name
15
+ @contract_class = contract_class
16
+ @reset_request = replace
17
+ @reset_response = replace
18
+ @request = nil
19
+ @response = nil
20
+ @raises = []
21
+ @summary = nil
22
+ @description = nil
23
+ @tags = nil
24
+ @deprecated = nil
25
+ @operation_id = nil
26
+ end
27
+
28
+ # @api public
29
+ # The summary for this action.
30
+ #
31
+ # Used in generated specs as the operation summary.
32
+ #
33
+ # @param value [String, nil] (nil)
34
+ # The summary.
35
+ # @return [String, nil]
36
+ #
37
+ # @example
38
+ # action :create do
39
+ # summary 'Create a new invoice'
40
+ # end
41
+ def summary(value = nil)
42
+ return @summary if value.nil?
43
+
44
+ @summary = value
45
+ end
46
+
47
+ # @api public
48
+ # The description for this action.
49
+ #
50
+ # Used in generated specs as the operation description.
51
+ # Supports Markdown formatting.
52
+ #
53
+ # @param value [String, nil] (nil)
54
+ # The description.
55
+ # @return [String, nil]
56
+ #
57
+ # @example
58
+ # action :create do
59
+ # description 'Creates a new invoice and sends notification email.'
60
+ # end
61
+ def description(value = nil)
62
+ return @description if value.nil?
63
+
64
+ @description = value
65
+ end
66
+
67
+ # @api public
68
+ # The tags for this action.
69
+ #
70
+ # Tags help organize actions in generated documentation.
71
+ #
72
+ # @param tags [Array<String, Symbol>]
73
+ # The tag names.
74
+ # @return [Array<Symbol>, nil]
75
+ #
76
+ # @example
77
+ # action :create do
78
+ # tags :billing, :invoices
79
+ # end
80
+ def tags(*tags)
81
+ @tags = tags.flatten if tags.any?
82
+ @tags
83
+ end
84
+
85
+ # @api public
86
+ # Marks this action as deprecated.
87
+ #
88
+ # @return [void]
89
+ #
90
+ # @example
91
+ # action :legacy_create do
92
+ # deprecated!
93
+ # end
94
+ def deprecated!
95
+ @deprecated = true
96
+ end
97
+
98
+ # @api public
99
+ # Whether this action is deprecated.
100
+ #
101
+ # @return [Boolean]
102
+ def deprecated?
103
+ @deprecated == true
104
+ end
105
+
106
+ # @api public
107
+ # The operation ID for this action.
108
+ #
109
+ # @param value [String, nil] (nil)
110
+ # The operation ID.
111
+ # @return [String, nil]
112
+ #
113
+ # @example
114
+ # action :create do
115
+ # operation_id 'createNewInvoice'
116
+ # end
117
+ def operation_id(value = nil)
118
+ return @operation_id if value.nil?
119
+
120
+ @operation_id = value
121
+ end
122
+
123
+ # @api public
124
+ # Declares the raised error codes for this action.
125
+ #
126
+ # @param error_code_keys [Symbol]
127
+ # The error code keys.
128
+ # @return [void]
129
+ # @raise [ConfigurationError] if error code is not registered
130
+ #
131
+ # @example
132
+ # raises :not_found
133
+ # raises :forbidden
134
+ #
135
+ # @example
136
+ # action :show do
137
+ # raises :not_found, :forbidden
138
+ # end
139
+ def raises(*error_code_keys)
140
+ error_code_keys = error_code_keys.flatten
141
+ error_code_keys.each do |error_code_key|
142
+ unless error_code_key.is_a?(Symbol)
143
+ hint = error_code_key.is_a?(Integer) ? " Use :#{ErrorCode.key_for_status(error_code_key)} instead." : ''
144
+ raise ConfigurationError, "raises must be symbols, got #{error_code_key.class}: #{error_code_key}.#{hint}"
145
+ end
146
+
147
+ next if ErrorCode.exists?(error_code_key)
148
+
149
+ raise ConfigurationError,
150
+ "Unknown error code :#{error_code_key}. Register it with: " \
151
+ "Apiwork::ErrorCode.register :#{error_code_key}, status: <status>"
152
+ end
153
+ @raises |= error_code_keys
154
+ end
155
+
156
+ # @api public
157
+ # Defines the request structure for this action.
158
+ #
159
+ # Use the block to define query parameters and request body.
160
+ #
161
+ # @param replace [Boolean] (false)
162
+ # Whether to replace inherited definition.
163
+ # @yield block for defining query and body (instance_eval style)
164
+ # @yieldparam request [Action::Request]
165
+ # @return [Action::Request]
166
+ #
167
+ # @example instance_eval style
168
+ # action :create do
169
+ # request do
170
+ # query do
171
+ # boolean? :dry_run
172
+ # end
173
+ # body do
174
+ # string :title
175
+ # end
176
+ # end
177
+ # end
178
+ #
179
+ # @example yield style
180
+ # action :create do
181
+ # request do |request|
182
+ # request.query do |query|
183
+ # query.boolean? :dry_run
184
+ # end
185
+ # request.body do |body|
186
+ # body.string :title
187
+ # end
188
+ # end
189
+ # end
190
+ def request(replace: false, &block)
191
+ @reset_request = replace if replace
192
+
193
+ @request ||= Request.new(contract_class, name)
194
+
195
+ if block
196
+ block.arity.positive? ? yield(@request) : @request.instance_eval(&block)
197
+ end
198
+
199
+ @request
200
+ end
201
+
202
+ # @api public
203
+ # Defines the response structure for this action.
204
+ #
205
+ # Use the block to define response body or declare no_content.
206
+ #
207
+ # @param replace [Boolean] (false)
208
+ # Whether to replace inherited definition.
209
+ # @yield block for defining body or no_content (instance_eval style)
210
+ # @yieldparam response [Action::Response]
211
+ # @return [Action::Response]
212
+ #
213
+ # @example instance_eval style
214
+ # action :show do
215
+ # response do
216
+ # body do
217
+ # uuid :id
218
+ # string :title
219
+ # end
220
+ # end
221
+ # end
222
+ #
223
+ # @example yield style
224
+ # action :show do
225
+ # response do |response|
226
+ # response.body do |body|
227
+ # body.uuid :id
228
+ # body.string :title
229
+ # end
230
+ # end
231
+ # end
232
+ #
233
+ # @example No content response
234
+ # action :destroy do
235
+ # response { no_content! }
236
+ # end
237
+ def response(replace: false, &block)
238
+ @reset_response = replace if replace
239
+
240
+ @response ||= Response.new(contract_class, name)
241
+
242
+ if block
243
+ block.arity.positive? ? yield(@response) : @response.instance_eval(&block)
244
+ end
245
+
246
+ @response
247
+ end
248
+
249
+ def resets_request?
250
+ @reset_request
251
+ end
252
+
253
+ def resets_response?
254
+ @reset_response
255
+ end
256
+ end
257
+ end
258
+ end