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,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
class Standard
|
|
6
|
+
class IncludesResolver
|
|
7
|
+
attr_reader :representation_class
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def resolve(representation_class, params = {}, include_always: false)
|
|
11
|
+
new(representation_class).resolve(params, include_always:)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(representation_class)
|
|
16
|
+
@representation_class = representation_class
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def resolve(params = {}, include_always: false)
|
|
20
|
+
base = include_always ? always_included : {}
|
|
21
|
+
merged = merge(base, from_params(params))
|
|
22
|
+
format(merged)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def always_included(visited = Set.new)
|
|
26
|
+
associations = representation_class.associations.select { |_, a| a.include == :always }
|
|
27
|
+
extract_associations(associations, visited)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def from_params(params, visited = Set.new)
|
|
31
|
+
extract_from_hash(params, visited)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def merge(base, override)
|
|
35
|
+
return base if override.blank?
|
|
36
|
+
|
|
37
|
+
base.deep_merge(override.deep_symbolize_keys)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def format(hash)
|
|
41
|
+
return [] if hash.blank?
|
|
42
|
+
|
|
43
|
+
result = hash.map do |key, value|
|
|
44
|
+
if value.blank?
|
|
45
|
+
key
|
|
46
|
+
else
|
|
47
|
+
{ key => format(value) }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
result.size == 1 ? result.first : result
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def extract_associations(associations, visited = Set.new)
|
|
57
|
+
return {} if visited.include?(representation_class.name)
|
|
58
|
+
|
|
59
|
+
visited = visited.dup.add(representation_class.name)
|
|
60
|
+
|
|
61
|
+
associations.transform_values do |association|
|
|
62
|
+
if association.representation_class
|
|
63
|
+
self.class.new(association.representation_class).always_included(visited)
|
|
64
|
+
else
|
|
65
|
+
{}
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def extract_from_hash(hash, visited = Set.new)
|
|
71
|
+
return {} if hash.blank?
|
|
72
|
+
return {} if visited.include?(representation_class.name)
|
|
73
|
+
|
|
74
|
+
visited_with_current = visited.dup.add(representation_class.name)
|
|
75
|
+
|
|
76
|
+
if hash.is_a?(Array)
|
|
77
|
+
return hash.each_with_object({}) do |item, result|
|
|
78
|
+
result.deep_merge!(extract_from_hash(item, visited))
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
hash.each_with_object({}) do |(key, value), result|
|
|
83
|
+
key = key.to_sym
|
|
84
|
+
|
|
85
|
+
if [Capability::Filtering::Constants::OR, Capability::Filtering::Constants::AND].include?(key) && value.is_a?(Array)
|
|
86
|
+
value.each { |item| result.deep_merge!(extract_from_hash(item, visited)) }
|
|
87
|
+
next
|
|
88
|
+
elsif key == Capability::Filtering::Constants::NOT && value.is_a?(Hash)
|
|
89
|
+
result.deep_merge!(extract_from_hash(value, visited))
|
|
90
|
+
next
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
association = representation_class.associations[key]
|
|
94
|
+
next unless association
|
|
95
|
+
|
|
96
|
+
result[key] = if value.is_a?(Hash) && association.representation_class.respond_to?(:associations)
|
|
97
|
+
self.class.new(association.representation_class).from_params(value, visited_with_current)
|
|
98
|
+
else
|
|
99
|
+
{}
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
class Standard < Base
|
|
6
|
+
adapter_name :standard
|
|
7
|
+
|
|
8
|
+
resource_serializer Serializer::Resource::Default
|
|
9
|
+
error_serializer Serializer::Error::Default
|
|
10
|
+
|
|
11
|
+
member_wrapper Wrapper::Member::Default
|
|
12
|
+
collection_wrapper Wrapper::Collection::Default
|
|
13
|
+
error_wrapper Wrapper::Error::Default
|
|
14
|
+
|
|
15
|
+
capability Capability::Filtering
|
|
16
|
+
capability Capability::Including
|
|
17
|
+
capability Capability::Pagination
|
|
18
|
+
capability Capability::Sorting
|
|
19
|
+
capability Capability::Writing
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Wrapper
|
|
6
|
+
class Base
|
|
7
|
+
class_attribute :shape_class
|
|
8
|
+
class_attribute :wrapper_type
|
|
9
|
+
|
|
10
|
+
# @api public
|
|
11
|
+
# The data for this wrapper.
|
|
12
|
+
#
|
|
13
|
+
# @return [Hash]
|
|
14
|
+
attr_reader :data
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
# @api public
|
|
18
|
+
# Defines the response shape for contract generation.
|
|
19
|
+
#
|
|
20
|
+
# @param klass_or_callable [Class<Shape>, Proc, nil] (nil)
|
|
21
|
+
# A {Shape} subclass or callable.
|
|
22
|
+
# @yield block that defines the shape using the Shape DSL
|
|
23
|
+
# @return [Class<Shape>, nil]
|
|
24
|
+
def shape(klass_or_callable = nil, &block)
|
|
25
|
+
callable = block || klass_or_callable
|
|
26
|
+
|
|
27
|
+
if callable
|
|
28
|
+
self.shape_class = if callable.respond_to?(:call)
|
|
29
|
+
wrap_callable(callable)
|
|
30
|
+
else
|
|
31
|
+
callable
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
shape_class
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def wrap(...)
|
|
39
|
+
new(...).wrap
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def wrap_callable(callable)
|
|
45
|
+
Class.new(Shape) do
|
|
46
|
+
define_singleton_method(:callable) { callable }
|
|
47
|
+
|
|
48
|
+
def apply
|
|
49
|
+
block = self.class.callable
|
|
50
|
+
block.arity.positive? ? block.call(self) : instance_exec(&block)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def initialize(data)
|
|
57
|
+
@data = data
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @api public
|
|
61
|
+
# Transforms the data into the final response format.
|
|
62
|
+
#
|
|
63
|
+
# @return [Hash]
|
|
64
|
+
def wrap
|
|
65
|
+
raise NotImplementedError
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Wrapper
|
|
6
|
+
module Collection
|
|
7
|
+
# @api public
|
|
8
|
+
# Base class for collection response wrappers.
|
|
9
|
+
#
|
|
10
|
+
# Collection wrappers structure responses for index actions that return
|
|
11
|
+
# multiple records. Extend this class to customize how collections are
|
|
12
|
+
# wrapped in your API responses.
|
|
13
|
+
#
|
|
14
|
+
# @example Custom collection wrapper
|
|
15
|
+
# class MyCollectionWrapper < Wrapper::Collection::Base
|
|
16
|
+
# shape do
|
|
17
|
+
# array(root_key.plural.to_sym) do |array|
|
|
18
|
+
# array.reference(data_type)
|
|
19
|
+
# end
|
|
20
|
+
# object?(:meta)
|
|
21
|
+
# metadata_type_names.each { |type_name| merge(type_name) }
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# def wrap
|
|
25
|
+
# { root_key.plural.to_sym => data, meta: meta.presence, **metadata }.compact
|
|
26
|
+
# end
|
|
27
|
+
# end
|
|
28
|
+
class Base < Wrapper::Base
|
|
29
|
+
self.wrapper_type = :collection
|
|
30
|
+
|
|
31
|
+
# @!attribute [r] meta
|
|
32
|
+
# @api public
|
|
33
|
+
# The meta for this wrapper.
|
|
34
|
+
#
|
|
35
|
+
# @return [Hash]
|
|
36
|
+
# @!attribute [r] metadata
|
|
37
|
+
# @api public
|
|
38
|
+
# The metadata for this wrapper.
|
|
39
|
+
#
|
|
40
|
+
# @return [Hash]
|
|
41
|
+
# @!attribute [r] root_key
|
|
42
|
+
# @api public
|
|
43
|
+
# The root key for this wrapper.
|
|
44
|
+
#
|
|
45
|
+
# @return [RootKey]
|
|
46
|
+
attr_reader :meta,
|
|
47
|
+
:metadata,
|
|
48
|
+
:root_key
|
|
49
|
+
|
|
50
|
+
def initialize(data, metadata, root_key, meta)
|
|
51
|
+
super(data)
|
|
52
|
+
@metadata = metadata
|
|
53
|
+
@root_key = root_key
|
|
54
|
+
@meta = meta
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Wrapper
|
|
6
|
+
module Collection
|
|
7
|
+
# @api public
|
|
8
|
+
# Default collection response wrapper.
|
|
9
|
+
#
|
|
10
|
+
# Wraps serialized records under a pluralized root key with optional meta and capability metadata.
|
|
11
|
+
#
|
|
12
|
+
# @example Configuration
|
|
13
|
+
# class MyAdapter < Adapter::Base
|
|
14
|
+
# collection_wrapper Wrapper::Collection::Default
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# @example Output
|
|
18
|
+
# {
|
|
19
|
+
# "invoices": [
|
|
20
|
+
# { "id": 1, "number": "INV-001" },
|
|
21
|
+
# { "id": 2, "number": "INV-002" }
|
|
22
|
+
# ],
|
|
23
|
+
# "meta": { ... },
|
|
24
|
+
# "pagination": { "current": 1, "total": 5 }
|
|
25
|
+
# }
|
|
26
|
+
class Default < Base
|
|
27
|
+
shape do
|
|
28
|
+
array(root_key.plural.to_sym) do |array|
|
|
29
|
+
array.reference(data_type)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
object?(:meta)
|
|
33
|
+
metadata_type_names.each { |type_name| merge(type_name) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def wrap
|
|
37
|
+
{
|
|
38
|
+
root_key.plural.to_sym => data,
|
|
39
|
+
meta: meta.presence,
|
|
40
|
+
**metadata,
|
|
41
|
+
}.compact
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Wrapper
|
|
6
|
+
module Error
|
|
7
|
+
# @api public
|
|
8
|
+
# Base class for error response wrappers.
|
|
9
|
+
#
|
|
10
|
+
# Error wrappers structure responses for validation errors and other
|
|
11
|
+
# error conditions. Extend this class to customize how errors are
|
|
12
|
+
# wrapped in your API responses.
|
|
13
|
+
#
|
|
14
|
+
# @example Custom error wrapper
|
|
15
|
+
# class MyErrorWrapper < Wrapper::Error::Base
|
|
16
|
+
# shape do
|
|
17
|
+
# extends(data_type)
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# def wrap
|
|
21
|
+
# data
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
class Base < Wrapper::Base
|
|
25
|
+
self.wrapper_type = :error
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Wrapper
|
|
6
|
+
module Error
|
|
7
|
+
# @api public
|
|
8
|
+
# Default error response wrapper.
|
|
9
|
+
#
|
|
10
|
+
# Passes serialized error data through unchanged.
|
|
11
|
+
#
|
|
12
|
+
# @example Configuration
|
|
13
|
+
# class MyAdapter < Adapter::Base
|
|
14
|
+
# error_wrapper Wrapper::Error::Default
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# @example Output
|
|
18
|
+
# {
|
|
19
|
+
# "issues": [{ "code": "blank", "detail": "can't be blank", ... }],
|
|
20
|
+
# "layer": "domain"
|
|
21
|
+
# }
|
|
22
|
+
class Default < Base
|
|
23
|
+
shape do
|
|
24
|
+
extends(data_type)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def wrap
|
|
28
|
+
data
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Wrapper
|
|
6
|
+
module Member
|
|
7
|
+
# @api public
|
|
8
|
+
# Base class for member response wrappers.
|
|
9
|
+
#
|
|
10
|
+
# Member wrappers structure responses for show, create, and update actions
|
|
11
|
+
# that return a single record. Extend this class to customize how individual
|
|
12
|
+
# resources are wrapped in your API responses.
|
|
13
|
+
#
|
|
14
|
+
# @example Custom member wrapper
|
|
15
|
+
# class MyMemberWrapper < Wrapper::Member::Base
|
|
16
|
+
# shape do
|
|
17
|
+
# reference(root_key.singular.to_sym, to: data_type)
|
|
18
|
+
# object?(:meta)
|
|
19
|
+
# metadata_type_names.each { |type_name| merge(type_name) }
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# def wrap
|
|
23
|
+
# { root_key.singular.to_sym => data, meta: meta.presence, **metadata }.compact
|
|
24
|
+
# end
|
|
25
|
+
# end
|
|
26
|
+
class Base < Wrapper::Base
|
|
27
|
+
self.wrapper_type = :member
|
|
28
|
+
|
|
29
|
+
# @!attribute [r] meta
|
|
30
|
+
# @api public
|
|
31
|
+
# The meta for this wrapper.
|
|
32
|
+
#
|
|
33
|
+
# @return [Hash]
|
|
34
|
+
# @!attribute [r] metadata
|
|
35
|
+
# @api public
|
|
36
|
+
# The metadata for this wrapper.
|
|
37
|
+
#
|
|
38
|
+
# @return [Hash]
|
|
39
|
+
# @!attribute [r] root_key
|
|
40
|
+
# @api public
|
|
41
|
+
# The root key for this wrapper.
|
|
42
|
+
#
|
|
43
|
+
# @return [RootKey]
|
|
44
|
+
attr_reader :meta,
|
|
45
|
+
:metadata,
|
|
46
|
+
:root_key
|
|
47
|
+
|
|
48
|
+
def initialize(data, metadata, root_key, meta)
|
|
49
|
+
super(data)
|
|
50
|
+
@metadata = metadata
|
|
51
|
+
@root_key = root_key
|
|
52
|
+
@meta = meta
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Wrapper
|
|
6
|
+
module Member
|
|
7
|
+
# @api public
|
|
8
|
+
# Default member response wrapper.
|
|
9
|
+
#
|
|
10
|
+
# Wraps a serialized record under a singular root key with optional meta and capability metadata.
|
|
11
|
+
#
|
|
12
|
+
# @example Configuration
|
|
13
|
+
# class MyAdapter < Adapter::Base
|
|
14
|
+
# member_wrapper Wrapper::Member::Default
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# @example Output
|
|
18
|
+
# {
|
|
19
|
+
# "invoice": { "id": 1, "number": "INV-001" },
|
|
20
|
+
# "meta": { ... }
|
|
21
|
+
# }
|
|
22
|
+
class Default < Base
|
|
23
|
+
shape do
|
|
24
|
+
reference(root_key.singular.to_sym, to: data_type)
|
|
25
|
+
object?(:meta)
|
|
26
|
+
metadata_type_names.each { |type_name| merge(type_name) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def wrap
|
|
30
|
+
{
|
|
31
|
+
root_key.singular.to_sym => data,
|
|
32
|
+
meta: meta.presence,
|
|
33
|
+
**metadata,
|
|
34
|
+
}.compact
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Apiwork
|
|
4
|
+
module Adapter
|
|
5
|
+
module Wrapper
|
|
6
|
+
# @api public
|
|
7
|
+
# Base class for wrapper shapes.
|
|
8
|
+
#
|
|
9
|
+
# Subclass to define response type structure for record or collection wrappers.
|
|
10
|
+
# The block is evaluated via instance_exec, providing access to type DSL methods
|
|
11
|
+
# and helpers like root_key and {#metadata_type_names}.
|
|
12
|
+
#
|
|
13
|
+
# @example Custom shape class
|
|
14
|
+
# class MyShape < Wrapper::Shape
|
|
15
|
+
# def apply
|
|
16
|
+
# reference(:invoice)
|
|
17
|
+
# object?(:meta)
|
|
18
|
+
# metadata_type_names.each { |type_name| merge(type_name) }
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# @example Inline shape block
|
|
23
|
+
# shape do
|
|
24
|
+
# reference(root_key.singular.to_sym)
|
|
25
|
+
# object?(:meta)
|
|
26
|
+
# metadata_type_names.each { |type_name| merge(type_name) }
|
|
27
|
+
# end
|
|
28
|
+
class Shape
|
|
29
|
+
class << self
|
|
30
|
+
def apply(target, root_key, capabilities, representation_class, type, data_type: nil)
|
|
31
|
+
metadata_type_names = build_metadata_type_names(capabilities, representation_class, type)
|
|
32
|
+
new(target, root_key, metadata_type_names, data_type:).apply
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def build_metadata_type_names(capabilities, representation_class, type)
|
|
38
|
+
capabilities.filter_map { |capability| capability.shape(representation_class, type) }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @!attribute [r] data_type
|
|
43
|
+
# @api public
|
|
44
|
+
# The data type for this shape.
|
|
45
|
+
#
|
|
46
|
+
# @return [Symbol, nil]
|
|
47
|
+
# @!attribute [r] metadata_type_names
|
|
48
|
+
# @api public
|
|
49
|
+
# The metadata type names for this shape.
|
|
50
|
+
#
|
|
51
|
+
# Auto-generated type names from capability {Adapter::Capability::Operation::Base.metadata_shape}
|
|
52
|
+
# definitions. Use with {#merge} to include capability metadata fields in the shape.
|
|
53
|
+
#
|
|
54
|
+
# @return [Array<Symbol>]
|
|
55
|
+
# @!attribute [r] root_key
|
|
56
|
+
# @api public
|
|
57
|
+
# The root key for this shape.
|
|
58
|
+
#
|
|
59
|
+
# @return [RootKey]
|
|
60
|
+
attr_reader :data_type,
|
|
61
|
+
:metadata_type_names,
|
|
62
|
+
:root_key
|
|
63
|
+
|
|
64
|
+
# @!method array(name, **options, &block)
|
|
65
|
+
# @api public
|
|
66
|
+
# @see API::Object#array
|
|
67
|
+
# @!method array?(name, **options, &block)
|
|
68
|
+
# @api public
|
|
69
|
+
# @see API::Object#array?
|
|
70
|
+
# @!method binary(name, **options)
|
|
71
|
+
# @api public
|
|
72
|
+
# @see API::Object#binary
|
|
73
|
+
# @!method binary?(name, **options)
|
|
74
|
+
# @api public
|
|
75
|
+
# @see API::Object#binary?
|
|
76
|
+
# @!method boolean(name, **options)
|
|
77
|
+
# @api public
|
|
78
|
+
# @see API::Object#boolean
|
|
79
|
+
# @!method boolean?(name, **options)
|
|
80
|
+
# @api public
|
|
81
|
+
# @see API::Object#boolean?
|
|
82
|
+
# @!method date(name, **options)
|
|
83
|
+
# @api public
|
|
84
|
+
# @see API::Object#date
|
|
85
|
+
# @!method date?(name, **options)
|
|
86
|
+
# @api public
|
|
87
|
+
# @see API::Object#date?
|
|
88
|
+
# @!method datetime(name, **options)
|
|
89
|
+
# @api public
|
|
90
|
+
# @see API::Object#datetime
|
|
91
|
+
# @!method datetime?(name, **options)
|
|
92
|
+
# @api public
|
|
93
|
+
# @see API::Object#datetime?
|
|
94
|
+
# @!method decimal(name, **options)
|
|
95
|
+
# @api public
|
|
96
|
+
# @see API::Object#decimal
|
|
97
|
+
# @!method decimal?(name, **options)
|
|
98
|
+
# @api public
|
|
99
|
+
# @see API::Object#decimal?
|
|
100
|
+
# @!method extends(type)
|
|
101
|
+
# @api public
|
|
102
|
+
# @see API::Object#extends
|
|
103
|
+
# @!method integer(name, **options)
|
|
104
|
+
# @api public
|
|
105
|
+
# @see API::Object#integer
|
|
106
|
+
# @!method integer?(name, **options)
|
|
107
|
+
# @api public
|
|
108
|
+
# @see API::Object#integer?
|
|
109
|
+
# @!method literal(name, value:, **options)
|
|
110
|
+
# @api public
|
|
111
|
+
# @see API::Object#literal
|
|
112
|
+
# @!method merge(other)
|
|
113
|
+
# @api public
|
|
114
|
+
# @see API::Object#merge
|
|
115
|
+
# @!method number(name, **options)
|
|
116
|
+
# @api public
|
|
117
|
+
# @see API::Object#number
|
|
118
|
+
# @!method number?(name, **options)
|
|
119
|
+
# @api public
|
|
120
|
+
# @see API::Object#number?
|
|
121
|
+
# @!method object(name, **options, &block)
|
|
122
|
+
# @api public
|
|
123
|
+
# @see API::Object#object
|
|
124
|
+
# @!method object?(name, **options, &block)
|
|
125
|
+
# @api public
|
|
126
|
+
# @see API::Object#object?
|
|
127
|
+
# @!method reference(name, **options)
|
|
128
|
+
# @api public
|
|
129
|
+
# @see API::Object#reference
|
|
130
|
+
# @!method reference?(name, **options)
|
|
131
|
+
# @api public
|
|
132
|
+
# @see API::Object#reference?
|
|
133
|
+
# @!method string(name, **options)
|
|
134
|
+
# @api public
|
|
135
|
+
# @see API::Object#string
|
|
136
|
+
# @!method string?(name, **options)
|
|
137
|
+
# @api public
|
|
138
|
+
# @see API::Object#string?
|
|
139
|
+
# @!method time(name, **options)
|
|
140
|
+
# @api public
|
|
141
|
+
# @see API::Object#time
|
|
142
|
+
# @!method time?(name, **options)
|
|
143
|
+
# @api public
|
|
144
|
+
# @see API::Object#time?
|
|
145
|
+
# @!method union(name, **options, &block)
|
|
146
|
+
# @api public
|
|
147
|
+
# @see API::Object#union
|
|
148
|
+
# @!method union?(name, **options, &block)
|
|
149
|
+
# @api public
|
|
150
|
+
# @see API::Object#union?
|
|
151
|
+
# @!method uuid(name, **options)
|
|
152
|
+
# @api public
|
|
153
|
+
# @see API::Object#uuid
|
|
154
|
+
# @!method uuid?(name, **options)
|
|
155
|
+
# @api public
|
|
156
|
+
# @see API::Object#uuid?
|
|
157
|
+
delegate :array,
|
|
158
|
+
:array?,
|
|
159
|
+
:binary,
|
|
160
|
+
:binary?,
|
|
161
|
+
:boolean,
|
|
162
|
+
:boolean?,
|
|
163
|
+
:date,
|
|
164
|
+
:date?,
|
|
165
|
+
:datetime,
|
|
166
|
+
:datetime?,
|
|
167
|
+
:decimal,
|
|
168
|
+
:decimal?,
|
|
169
|
+
:extends,
|
|
170
|
+
:integer,
|
|
171
|
+
:integer?,
|
|
172
|
+
:literal,
|
|
173
|
+
:merge,
|
|
174
|
+
:number,
|
|
175
|
+
:number?,
|
|
176
|
+
:object,
|
|
177
|
+
:object?,
|
|
178
|
+
:reference,
|
|
179
|
+
:reference?,
|
|
180
|
+
:string,
|
|
181
|
+
:string?,
|
|
182
|
+
:time,
|
|
183
|
+
:time?,
|
|
184
|
+
:union,
|
|
185
|
+
:union?,
|
|
186
|
+
:uuid,
|
|
187
|
+
:uuid?,
|
|
188
|
+
to: :@target
|
|
189
|
+
|
|
190
|
+
def initialize(target, root_key, metadata_type_names, data_type: nil)
|
|
191
|
+
@target = target
|
|
192
|
+
@root_key = root_key
|
|
193
|
+
@metadata_type_names = metadata_type_names
|
|
194
|
+
@data_type = data_type
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def apply
|
|
198
|
+
raise NotImplementedError
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|