apia 3.0.0

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 (120) hide show
  1. checksums.yaml +7 -0
  2. data/VERSION +1 -0
  3. data/lib/apia.rb +21 -0
  4. data/lib/apia/api.rb +100 -0
  5. data/lib/apia/argument_set.rb +221 -0
  6. data/lib/apia/authenticator.rb +57 -0
  7. data/lib/apia/callable_with_environment.rb +43 -0
  8. data/lib/apia/controller.rb +32 -0
  9. data/lib/apia/defineable.rb +60 -0
  10. data/lib/apia/definition.rb +27 -0
  11. data/lib/apia/definitions/api.rb +51 -0
  12. data/lib/apia/definitions/argument.rb +77 -0
  13. data/lib/apia/definitions/argument_set.rb +33 -0
  14. data/lib/apia/definitions/authenticator.rb +46 -0
  15. data/lib/apia/definitions/controller.rb +41 -0
  16. data/lib/apia/definitions/endpoint.rb +74 -0
  17. data/lib/apia/definitions/enum.rb +31 -0
  18. data/lib/apia/definitions/error.rb +59 -0
  19. data/lib/apia/definitions/field.rb +117 -0
  20. data/lib/apia/definitions/lookup_argument_set.rb +27 -0
  21. data/lib/apia/definitions/object.rb +29 -0
  22. data/lib/apia/definitions/polymorph.rb +29 -0
  23. data/lib/apia/definitions/polymorph_option.rb +53 -0
  24. data/lib/apia/definitions/scalar.rb +23 -0
  25. data/lib/apia/definitions/type.rb +109 -0
  26. data/lib/apia/dsl.rb +23 -0
  27. data/lib/apia/dsls/api.rb +37 -0
  28. data/lib/apia/dsls/argument.rb +27 -0
  29. data/lib/apia/dsls/argument_set.rb +35 -0
  30. data/lib/apia/dsls/authenticator.rb +38 -0
  31. data/lib/apia/dsls/concerns/has_fields.rb +38 -0
  32. data/lib/apia/dsls/controller.rb +34 -0
  33. data/lib/apia/dsls/endpoint.rb +79 -0
  34. data/lib/apia/dsls/enum.rb +19 -0
  35. data/lib/apia/dsls/error.rb +26 -0
  36. data/lib/apia/dsls/field.rb +27 -0
  37. data/lib/apia/dsls/lookup_argument_set.rb +24 -0
  38. data/lib/apia/dsls/object.rb +19 -0
  39. data/lib/apia/dsls/polymorph.rb +19 -0
  40. data/lib/apia/dsls/route_group.rb +43 -0
  41. data/lib/apia/dsls/route_set.rb +40 -0
  42. data/lib/apia/dsls/scalar.rb +23 -0
  43. data/lib/apia/dsls/scope_descriptions.rb +17 -0
  44. data/lib/apia/endpoint.rb +110 -0
  45. data/lib/apia/enum.rb +43 -0
  46. data/lib/apia/environment_error_handling.rb +74 -0
  47. data/lib/apia/error.rb +61 -0
  48. data/lib/apia/error_set.rb +15 -0
  49. data/lib/apia/errors/error_exception_error.rb +32 -0
  50. data/lib/apia/errors/field_spec_parse_error.rb +23 -0
  51. data/lib/apia/errors/invalid_argument_error.rb +68 -0
  52. data/lib/apia/errors/invalid_enum_option_error.rb +21 -0
  53. data/lib/apia/errors/invalid_helper_error.rb +6 -0
  54. data/lib/apia/errors/invalid_json_error.rb +23 -0
  55. data/lib/apia/errors/invalid_polymorph_value_error.rb +21 -0
  56. data/lib/apia/errors/invalid_scalar_value_error.rb +21 -0
  57. data/lib/apia/errors/manifest_error.rb +43 -0
  58. data/lib/apia/errors/missing_argument_error.rb +40 -0
  59. data/lib/apia/errors/null_field_value_error.rb +37 -0
  60. data/lib/apia/errors/parse_error.rb +10 -0
  61. data/lib/apia/errors/runtime_error.rb +30 -0
  62. data/lib/apia/errors/scope_not_granted_error.rb +15 -0
  63. data/lib/apia/errors/standard_error.rb +6 -0
  64. data/lib/apia/field_set.rb +76 -0
  65. data/lib/apia/field_spec.rb +155 -0
  66. data/lib/apia/helpers.rb +34 -0
  67. data/lib/apia/hook_set.rb +30 -0
  68. data/lib/apia/lookup_argument_set.rb +57 -0
  69. data/lib/apia/lookup_environment.rb +27 -0
  70. data/lib/apia/manifest_errors.rb +62 -0
  71. data/lib/apia/mock_request.rb +18 -0
  72. data/lib/apia/object.rb +68 -0
  73. data/lib/apia/object_set.rb +21 -0
  74. data/lib/apia/pagination_object.rb +34 -0
  75. data/lib/apia/polymorph.rb +50 -0
  76. data/lib/apia/rack.rb +184 -0
  77. data/lib/apia/rack_error.rb +17 -0
  78. data/lib/apia/request.rb +67 -0
  79. data/lib/apia/request_environment.rb +84 -0
  80. data/lib/apia/request_headers.rb +42 -0
  81. data/lib/apia/response.rb +64 -0
  82. data/lib/apia/route.rb +61 -0
  83. data/lib/apia/route_group.rb +20 -0
  84. data/lib/apia/route_set.rb +89 -0
  85. data/lib/apia/scalar.rb +52 -0
  86. data/lib/apia/scalars.rb +25 -0
  87. data/lib/apia/scalars/base64.rb +31 -0
  88. data/lib/apia/scalars/boolean.rb +37 -0
  89. data/lib/apia/scalars/date.rb +45 -0
  90. data/lib/apia/scalars/decimal.rb +36 -0
  91. data/lib/apia/scalars/integer.rb +34 -0
  92. data/lib/apia/scalars/string.rb +24 -0
  93. data/lib/apia/scalars/unix_time.rb +40 -0
  94. data/lib/apia/schema/api_controller_schema_type.rb +17 -0
  95. data/lib/apia/schema/api_schema_type.rb +43 -0
  96. data/lib/apia/schema/argument_schema_type.rb +28 -0
  97. data/lib/apia/schema/argument_set_schema_type.rb +21 -0
  98. data/lib/apia/schema/authenticator_schema_type.rb +22 -0
  99. data/lib/apia/schema/controller.rb +39 -0
  100. data/lib/apia/schema/controller_endpoint_schema_type.rb +17 -0
  101. data/lib/apia/schema/controller_schema_type.rb +32 -0
  102. data/lib/apia/schema/endpoint_schema_type.rb +35 -0
  103. data/lib/apia/schema/enum_schema_type.rb +20 -0
  104. data/lib/apia/schema/enum_value_schema_type.rb +14 -0
  105. data/lib/apia/schema/error_schema_type.rb +23 -0
  106. data/lib/apia/schema/field_schema_type.rb +38 -0
  107. data/lib/apia/schema/field_spec_options_schema_type.rb +16 -0
  108. data/lib/apia/schema/lookup_argument_set_schema_type.rb +25 -0
  109. data/lib/apia/schema/object_schema_polymorph.rb +31 -0
  110. data/lib/apia/schema/object_schema_type.rb +21 -0
  111. data/lib/apia/schema/polymorph_option_schema_type.rb +16 -0
  112. data/lib/apia/schema/polymorph_schema_type.rb +20 -0
  113. data/lib/apia/schema/request_method_enum.rb +21 -0
  114. data/lib/apia/schema/route_group_schema_type.rb +19 -0
  115. data/lib/apia/schema/route_schema_type.rb +31 -0
  116. data/lib/apia/schema/route_set_schema_type.rb +20 -0
  117. data/lib/apia/schema/scalar_schema_type.rb +15 -0
  118. data/lib/apia/schema/scope_type.rb +14 -0
  119. data/lib/apia/version.rb +12 -0
  120. metadata +188 -0
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/definition'
4
+ require 'apia/dsls/enum'
5
+
6
+ module Apia
7
+ module Definitions
8
+ class Enum < Definition
9
+
10
+ attr_accessor :cast
11
+ attr_reader :values
12
+
13
+ def setup
14
+ @values = {}
15
+ end
16
+
17
+ def dsl
18
+ @dsl ||= DSLs::Enum.new(self)
19
+ end
20
+
21
+ def validate(errors)
22
+ if cast && !cast.is_a?(Proc)
23
+ errors.add self, 'CastMustBeProc', 'The value provided for casting an enum must be an instance of Proc'
24
+ end
25
+
26
+ true
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/definition'
4
+ require 'apia/dsls/error'
5
+ require 'apia/field_set'
6
+
7
+ module Apia
8
+ module Definitions
9
+ class Error < Definition
10
+
11
+ attr_accessor :code
12
+ attr_accessor :http_status
13
+ attr_reader :fields
14
+ attr_reader :catchable_exceptions
15
+
16
+ def setup
17
+ @fields = FieldSet.new
18
+ @catchable_exceptions = {}
19
+ end
20
+
21
+ def dsl
22
+ @dsl ||= DSLs::Error.new(self)
23
+ end
24
+
25
+ # Return the actual HTTP status code
26
+ #
27
+ # @return [Integer]
28
+ def http_status_code
29
+ if @http_status.is_a?(Symbol)
30
+ ::Rack::Utils::SYMBOL_TO_STATUS_CODE[@http_status]
31
+ else
32
+ @http_status
33
+ end
34
+ end
35
+
36
+ # Validate that this error class is valid and thus can be used in the
37
+ # API.
38
+ #
39
+ # @param errors [Apia::ManifestErrors]
40
+ # @reeturn [void]
41
+ def validate(errors)
42
+ unless code.is_a?(Symbol)
43
+ errors.add self, 'InvalidCode', 'Code must be a symbol'
44
+ end
45
+
46
+ if http_status_code.is_a?(Integer) && ::Rack::Utils::HTTP_STATUS_CODES[http_status_code]
47
+ # OK
48
+ elsif http_status_code.is_a?(Integer)
49
+ errors.add self, 'InvalidHTTPStatus', "The HTTP status is not valid (must be one of #{::Rack::Utils::HTTP_STATUS_CODES.keys.join(', ')})"
50
+ else
51
+ errors.add self, 'InvalidHTTPStatus', 'The HTTP status is not valid (must be an integer)'
52
+ end
53
+
54
+ @fields.validate(errors, self)
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/definition'
4
+ require 'apia/helpers'
5
+ require 'apia/dsls/field'
6
+ require 'apia/definitions/type'
7
+ require 'apia/errors/invalid_scalar_value_error'
8
+ require 'apia/errors/null_field_value_error'
9
+
10
+ module Apia
11
+ module Definitions
12
+ class Field < Definition
13
+
14
+ attr_accessor :description
15
+ attr_accessor :backend
16
+ attr_accessor :array
17
+ attr_accessor :null
18
+ attr_accessor :condition
19
+ attr_writer :type
20
+ attr_accessor :include
21
+
22
+ def initialize(name, id: nil)
23
+ @name = name
24
+ @id = id
25
+ end
26
+
27
+ # Return the type of object
28
+ #
29
+ # @return [Class]
30
+ def type
31
+ Type.new(@type)
32
+ end
33
+
34
+ # Can the result for thsi field be nil?
35
+ #
36
+ # @return [Boolean]
37
+ def null?
38
+ @null == true
39
+ end
40
+
41
+ # Is the result from this field expected to be an array?
42
+ #
43
+ # @return [Boolean]
44
+ def array?
45
+ @array == true
46
+ end
47
+
48
+ # Should this field be inclued for the given value and request
49
+ #
50
+ # @param value [Object]
51
+ # @param request [Apia::Request]
52
+ # @return [Boolean]
53
+ def include?(value, request)
54
+ return true if @condition.nil?
55
+
56
+ @condition.call(value, request) == true
57
+ end
58
+
59
+ # Return a DSL instance for this field
60
+ #
61
+ # @return [Apia::DSLs::Field]
62
+ def dsl
63
+ @dsl ||= DSLs::Field.new(self)
64
+ end
65
+
66
+ # Return the backend value from the given object based on the
67
+ # rules that exist for this field.
68
+ #
69
+ # @param object [Object]
70
+ # @return [Object]
71
+ def raw_value_from_object(object)
72
+ if @backend
73
+ get_value_from_backend(object)
74
+ else
75
+ get_value_directly_from_object(object, @name)
76
+ end
77
+ end
78
+
79
+ # Return an instance of a Type or a Scalar for this field
80
+ #
81
+ # @param object [Object]
82
+ # @return [Object]
83
+ def value(object, request: nil, path: [])
84
+ raw_value = raw_value_from_object(object)
85
+
86
+ return nil if raw_value.nil? && null?
87
+ raise Apia::NullFieldValueError.new(self, object) if raw_value.nil?
88
+
89
+ if array? && raw_value.is_a?(Array)
90
+ raw_value.map { |v| type.cast(v, request: request, path: path) }
91
+ else
92
+ type.cast(raw_value, request: request, path: path)
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ def get_value_from_backend(object)
99
+ case @backend
100
+ when Symbol
101
+ get_value_directly_from_object(object, @backend)
102
+ when Proc
103
+ @backend.call(object)
104
+ end
105
+ end
106
+
107
+ def get_value_directly_from_object(object, name)
108
+ if object.is_a?(Hash)
109
+ object.fetch(name.to_sym) { object[name.to_s] }
110
+ else
111
+ object.public_send(name.to_sym)
112
+ end
113
+ end
114
+
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/definitions/argument_set'
4
+ require 'apia/dsls/lookup_argument_set'
5
+
6
+ module Apia
7
+ module Definitions
8
+ class LookupArgumentSet < Definitions::ArgumentSet
9
+
10
+ attr_accessor :resolver
11
+
12
+ def dsl
13
+ @dsl ||= DSLs::LookupArgumentSet.new(self)
14
+ end
15
+
16
+ def potential_errors
17
+ @potential_errors ||= ErrorSet.new
18
+ end
19
+
20
+ def validate(errors)
21
+ super
22
+ @potential_errors&.validate(errors, self)
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/definition'
4
+ require 'apia/dsls/object'
5
+ require 'apia/field_set'
6
+
7
+ module Apia
8
+ module Definitions
9
+ class Object < Definition
10
+
11
+ attr_reader :conditions
12
+ attr_reader :fields
13
+
14
+ def setup
15
+ @conditions = []
16
+ @fields = FieldSet.new
17
+ end
18
+
19
+ def dsl
20
+ @dsl ||= DSLs::Object.new(self)
21
+ end
22
+
23
+ def validate(errors)
24
+ @fields.validate(errors, self)
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/definition'
4
+ require 'apia/dsls/polymorph'
5
+ require 'apia/helpers'
6
+
7
+ module Apia
8
+ module Definitions
9
+ class Polymorph < Definition
10
+
11
+ attr_reader :options
12
+
13
+ def setup
14
+ @options = {}
15
+ end
16
+
17
+ def dsl
18
+ @dsl ||= DSLs::Polymorph.new(self)
19
+ end
20
+
21
+ def validate(errors)
22
+ @options.each_value do |option|
23
+ option.validate(errors)
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/definitions/type'
4
+
5
+ module Apia
6
+ module Definitions
7
+ class PolymorphOption
8
+
9
+ attr_reader :id
10
+ attr_reader :name
11
+ attr_reader :matcher
12
+
13
+ def initialize(id, name, type: nil, matcher: nil)
14
+ @id = id
15
+ @name = name
16
+ @type = type
17
+ @matcher = matcher
18
+ end
19
+
20
+ def type
21
+ Type.new(@type)
22
+ end
23
+
24
+ def matches?(value)
25
+ return false if @matcher.nil?
26
+
27
+ @matcher.call(value) == true
28
+ end
29
+
30
+ def cast(value, request: nil, path: [])
31
+ {
32
+ type: @name.to_s,
33
+ value: type.cast(value, request: request, path: path)
34
+ }
35
+ end
36
+
37
+ def validate(errors)
38
+ if @type.nil?
39
+ errors.add self, 'MissingType', "Type for #{name} is missing"
40
+ elsif !type.usable_for_field?
41
+ errors.add self, 'InvalidType', "Type for #{name} must a scalar, polymorph, object or enum "
42
+ end
43
+
44
+ unless @matcher.is_a?(Proc)
45
+ errors.add self, 'MissingMatcher', "A matcher must be provided for all options (missing for #{name})"
46
+ end
47
+
48
+ true
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/definition'
4
+ require 'apia/dsls/scalar'
5
+
6
+ module Apia
7
+ module Definitions
8
+ class Scalar < Definition
9
+
10
+ attr_accessor :cast
11
+ attr_accessor :parse
12
+ attr_accessor :validator
13
+
14
+ def dsl
15
+ @dsl ||= DSLs::Scalar.new(self)
16
+ end
17
+
18
+ def validate(errors)
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apia
4
+ module Definitions
5
+ class Type
6
+
7
+ def initialize(type)
8
+ @type = type
9
+ end
10
+
11
+ def id
12
+ klass&.definition&.id
13
+ end
14
+
15
+ def klass
16
+ if @type.is_a?(Symbol) || @type.is_a?(String)
17
+ Scalars.fetch(@type.to_sym)
18
+ else
19
+ @type
20
+ end
21
+ end
22
+
23
+ # Can this type actually be used?
24
+ #
25
+ # @return [Boolean]
26
+ def usable_for_field?
27
+ scalar? || object? || enum? || polymorph?
28
+ end
29
+
30
+ # Can this type be used for an argument?
31
+ #
32
+ # @return [Boolean]
33
+ def usable_for_argument?
34
+ scalar? || enum? || argument_set?
35
+ end
36
+
37
+ # Cast the given value into an response that can be sent to the
38
+ # consumer.
39
+ #
40
+ # @param value [Object, nil]
41
+ # @param request [Apia::Request, nil]
42
+ # @return [Object, nil]
43
+ def cast(value, request: nil, path: [])
44
+ return nil if value.nil?
45
+
46
+ if scalar? || enum?
47
+ # If this is a scalar or an enum, we're going to just return the
48
+ # value that they return. There's nothing complicated about them
49
+ # and they return scalars.
50
+ klass.cast(value)
51
+
52
+ elsif object?
53
+ # If this field returns an object, we'll go ahead and generate
54
+ # the hash for the object at this point.
55
+ object = klass.new(value)
56
+
57
+ # If this item shouldn't be included. we'll return :skip which
58
+ # will instruct the field set not to include it at all.
59
+ return :skip unless object.include?(request)
60
+
61
+ # Otherwise, we'll return the hash
62
+ object.hash(request: request, path: path)
63
+
64
+ elsif polymorph?
65
+ # If the type is a polymorph and this value
66
+ option = klass.option_for_value(value)
67
+ option.cast(value, request: request, path: path)
68
+
69
+ end
70
+ end
71
+
72
+ # Does this field return an argument set?
73
+ #
74
+ # @return [Boolean]
75
+ def argument_set?
76
+ klass&.ancestors&.include?(Apia::ArgumentSet)
77
+ end
78
+
79
+ # Does this field return a scalar?
80
+ #
81
+ # @return [Boolean]
82
+ def scalar?
83
+ klass&.ancestors&.include?(Apia::Scalar)
84
+ end
85
+
86
+ # Does this field return an enum?
87
+ #
88
+ # @return [Boolean]
89
+ def enum?
90
+ klass&.ancestors&.include?(Apia::Enum)
91
+ end
92
+
93
+ # Does this field return an object?
94
+ #
95
+ # @return [Boolean]
96
+ def object?
97
+ klass&.ancestors&.include?(Apia::Object)
98
+ end
99
+
100
+ # Does this field return a polymorph?
101
+ #
102
+ # @return [Boolean]
103
+ def polymorph?
104
+ klass&.ancestors&.include?(Apia::Polymorph)
105
+ end
106
+
107
+ end
108
+ end
109
+ end