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,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/defineable'
4
+ require 'apia/definitions/controller'
5
+ require 'apia/helpers'
6
+
7
+ module Apia
8
+ class Controller
9
+
10
+ extend Defineable
11
+
12
+ class << self
13
+
14
+ # Return the definition object for the controller
15
+ #
16
+ # @return [Apia::Definitions::Controller]
17
+ def definition
18
+ @definition ||= Definitions::Controller.new(Helpers.class_name_to_id(name))
19
+ end
20
+
21
+ # Collate all objects that this controller references and add them to the
22
+ # given object set
23
+ #
24
+ # @param set [Apia::ObjectSet]
25
+ # @return [void]
26
+ def collate_objects(set)
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apia
4
+ module Defineable
5
+
6
+ # Inspect an object
7
+ #
8
+ # @return [String]
9
+ def inspect
10
+ type = ancestors.find { |c| c.name =~ /\AApia::/ }
11
+ "<#{definition.id} [#{type}]>"
12
+ end
13
+
14
+ # Create a new object
15
+ #
16
+ # @param id [String]
17
+ # @return [Class]
18
+ def create(id, &block)
19
+ klass = Class.new(self)
20
+ klass.definition.id = id
21
+ if block_given?
22
+ klass.definition.dsl.instance_eval(&block)
23
+ end
24
+ klass
25
+ end
26
+
27
+ # Ability to set a name (for DSL purposes) but also returns a name
28
+ # of the actual class if no new value is provided
29
+ #
30
+ # @param new_name [String, nil]
31
+ # @return [String]
32
+ def name(new_name = nil)
33
+ if new_name
34
+ definition.name = new_name
35
+ return new_name
36
+ end
37
+
38
+ super()
39
+ end
40
+
41
+ # Passes all other values through to the DSL for the definition if
42
+ # the DSL supoprts it.
43
+ def method_missing(name, *args, **kwargs, &block)
44
+ if definition.dsl.respond_to?(name)
45
+ if kwargs.empty?
46
+ definition.dsl.send(name, *args, &block)
47
+ else
48
+ definition.dsl.send(name, *args, **kwargs, &block)
49
+ end
50
+ else
51
+ super
52
+ end
53
+ end
54
+
55
+ def respond_to_missing?(name, include_private = false)
56
+ definition.dsl.respond_to?(name) || super
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apia
4
+ class Definition
5
+
6
+ attr_accessor :id
7
+ attr_accessor :name
8
+ attr_accessor :description
9
+ attr_accessor :schema
10
+
11
+ def initialize(id)
12
+ @id = id
13
+ setup
14
+ end
15
+
16
+ def schema?
17
+ @schema != false
18
+ end
19
+
20
+ def validate(errors)
21
+ end
22
+
23
+ def setup
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/definition'
4
+ require 'apia/dsls/api'
5
+ require 'apia/hook_set'
6
+ require 'apia/route_set'
7
+
8
+ module Apia
9
+ module Definitions
10
+ class API < Definition
11
+
12
+ attr_accessor :authenticator
13
+ attr_reader :controllers
14
+ attr_reader :route_set
15
+ attr_reader :exception_handlers
16
+ attr_reader :scopes
17
+
18
+ def setup
19
+ @route_set = RouteSet.new
20
+ @controllers = {}
21
+ @exception_handlers = HookSet.new
22
+ @scopes = {}
23
+ end
24
+
25
+ def dsl
26
+ @dsl ||= DSLs::API.new(self)
27
+ end
28
+
29
+ # Validate the API to ensure that everything within is acceptable for use
30
+ #
31
+ # @param errors [Apia::ManifestErrors]
32
+ # @return [void]
33
+ def validate(errors)
34
+ if @authenticator && !(@authenticator.respond_to?(:ancestors) && @authenticator.ancestors.include?(Apia::Authenticator))
35
+ errors.add self, 'InvalidAuthenticator', 'The authenticator must be a class that inherits from Apia::Authenticator'
36
+ end
37
+
38
+ @controllers.each do |name, controller|
39
+ unless name.to_s =~ /\A[\w\-]+\z/i
40
+ errors.add self, 'InvalidControllerName', "The controller name #{name} is invalid. It can only contain letters, numbers, underscores, and hyphens"
41
+ end
42
+
43
+ unless controller.respond_to?(:ancestors) && controller.ancestors.include?(Apia::Controller)
44
+ errors.add self, 'InvalidController', "The controller for #{name} must be a class that inherits from Apia::Controller"
45
+ end
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/definition'
4
+ require 'apia/dsls/argument'
5
+ require 'apia/helpers'
6
+
7
+ module Apia
8
+ module Definitions
9
+ class Argument < Definition
10
+
11
+ attr_reader :options
12
+ attr_reader :validations
13
+ attr_accessor :required
14
+ attr_accessor :array
15
+ attr_accessor :default
16
+ attr_writer :type
17
+
18
+ def initialize(name, id: nil)
19
+ @id = id
20
+ @name = name
21
+ @validations = []
22
+ end
23
+
24
+ def dsl
25
+ @dsl ||= DSLs::Argument.new(self)
26
+ end
27
+
28
+ def validate(errors)
29
+ if @name.nil?
30
+ errors.add self, 'MissingName', 'Arguments must have a name'
31
+ elsif @name.to_s !~ /\A[a-z0-9\-_]+\z/i
32
+ errors.add self, 'InvalidName', 'Argument name must only include letters, numbers, hyphens and underscores'
33
+ end
34
+
35
+ if @type.nil?
36
+ errors.add self, 'MissingType', 'Arguments must have a type'
37
+ elsif !type.usable_for_argument?
38
+ errors.add self, 'InvalidType', 'Type must be an argument set, scalar or enum'
39
+ end
40
+ end
41
+
42
+ # Return the type of object (either a ArgumentSet or a Scalar) which
43
+ # this argument represents.
44
+ #
45
+ # @return [Class]
46
+ def type
47
+ Type.new(@type)
48
+ end
49
+
50
+ # Is this argument required?
51
+ #
52
+ # @return [Boolean]
53
+ def required?
54
+ @required == true
55
+ end
56
+
57
+ # Is this an array?
58
+ #
59
+ # @return [Boolean]
60
+ def array?
61
+ @array == true
62
+ end
63
+
64
+ # Validate a given value through all validations and
65
+ # return an array of all errors
66
+ #
67
+ # @param value [Object]
68
+ # @return [Array]
69
+ def validate_value(value)
70
+ @validations.each_with_object([]) do |validation, errors|
71
+ errors << validation[:name] unless validation[:block].call(value)
72
+ end
73
+ end
74
+
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/definition'
4
+ require 'apia/error_set'
5
+ require 'apia/dsls/argument_set'
6
+
7
+ module Apia
8
+ module Definitions
9
+ class ArgumentSet < Definition
10
+
11
+ attr_reader :arguments
12
+
13
+ def setup
14
+ @arguments = {}
15
+ end
16
+
17
+ def dsl
18
+ @dsl ||= DSLs::ArgumentSet.new(self)
19
+ end
20
+
21
+ def validate(errors)
22
+ @arguments.each do |name, argument|
23
+ if argument.is_a?(Apia::Definitions::Argument)
24
+ argument.validate(errors)
25
+ else
26
+ errors.add self, 'InvalidArgument', "The argument '#{name}' is not an instance of Apia::Definitions::Argument"
27
+ end
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/definition'
4
+ require 'apia/dsls/authenticator'
5
+
6
+ module Apia
7
+ module Definitions
8
+ class Authenticator < Definition
9
+
10
+ TYPES = [:bearer, :anonymous].freeze
11
+
12
+ attr_accessor :type
13
+ attr_accessor :action
14
+ attr_accessor :scope_validator
15
+ attr_reader :potential_errors
16
+
17
+ def setup
18
+ @id = id
19
+ @potential_errors = []
20
+ end
21
+
22
+ def dsl
23
+ @dsl ||= DSLs::Authenticator.new(self)
24
+ end
25
+
26
+ def validate(errors)
27
+ if @type.nil?
28
+ errors.add self, 'MissingType', 'A type must be defined for authenticators'
29
+ elsif !TYPES.include?(@type)
30
+ errors.add self, 'InvalidType', "The type must be one of #{TYPES.join(', ')} (was: #{@type.inspect})"
31
+ end
32
+
33
+ if @action && !@action.is_a?(Proc)
34
+ errors.add self, 'InvalidAction', 'The action provided must be a Proc'
35
+ end
36
+
37
+ @potential_errors.each_with_index do |error, index|
38
+ unless error.respond_to?(:ancestors) && error.ancestors.include?(Apia::Error)
39
+ errors.add self, 'InvalidPotentialError', "Potential error at index #{index} must be a class that inherits from Apia::Error"
40
+ end
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/definition'
4
+ require 'apia/dsls/controller'
5
+
6
+ module Apia
7
+ module Definitions
8
+ class Controller < Definition
9
+
10
+ attr_accessor :authenticator
11
+ attr_reader :endpoints
12
+ attr_reader :helpers
13
+
14
+ def setup
15
+ @endpoints = {}
16
+ @helpers = {}
17
+ end
18
+
19
+ def dsl
20
+ @dsl ||= DSLs::Controller.new(self)
21
+ end
22
+
23
+ def validate(errors)
24
+ if @authenticator && !(@authenticator.respond_to?(:ancestors) && @authenticator.ancestors.include?(Apia::Authenticator))
25
+ errors.add self, 'InvalidAuthenticator', 'The authenticator must be a class that inherits from Apia::Authenticator'
26
+ end
27
+
28
+ @endpoints.each do |name, endpoint|
29
+ unless name.to_s =~ /\A[\w-]+\z/i
30
+ errors.add self, 'InvalidEndpointName', "The endpoint name #{name} is invalid. It can only contain letters, numbers, underscores, and hyphens"
31
+ end
32
+
33
+ unless endpoint.respond_to?(:ancestors) && endpoint.ancestors.include?(Apia::Endpoint)
34
+ errors.add self, 'InvalidEndpoint', "The endpoint for #{name} must be a class that inherits from Apia::Endpoint"
35
+ end
36
+ end
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/definition'
4
+ require 'apia/argument_set'
5
+ require 'apia/dsls/endpoint'
6
+ require 'apia/field_set'
7
+ require 'apia/error_set'
8
+ require 'rack/utils'
9
+
10
+ module Apia
11
+ module Definitions
12
+ class Endpoint < Definition
13
+
14
+ attr_accessor :authenticator
15
+ attr_accessor :action
16
+ attr_accessor :http_status
17
+ attr_accessor :paginated_field
18
+ attr_reader :fields
19
+ attr_reader :scopes
20
+
21
+ def setup
22
+ @fields = FieldSet.new
23
+ @http_status = 200
24
+ @scopes = []
25
+ end
26
+
27
+ def argument_set
28
+ @argument_set ||= begin
29
+ as = Apia::ArgumentSet.create("#{@id}/BaseArgumentSet")
30
+ as.definition.schema = schema?
31
+ as
32
+ end
33
+ end
34
+
35
+ def potential_errors
36
+ @potential_errors ||= Apia::ErrorSet.new
37
+ end
38
+
39
+ def dsl
40
+ @dsl ||= DSLs::Endpoint.new(self)
41
+ end
42
+
43
+ def http_status_code
44
+ if @http_status.is_a?(Symbol)
45
+ ::Rack::Utils::SYMBOL_TO_STATUS_CODE[@http_status]
46
+ else
47
+ @http_status
48
+ end
49
+ end
50
+
51
+ def validate(errors)
52
+ if @action && !@action.is_a?(Proc)
53
+ errors.add self, 'InvalidAction', 'The action provided must be a Proc'
54
+ end
55
+
56
+ if http_status_code.is_a?(Integer) && ::Rack::Utils::HTTP_STATUS_CODES[http_status_code]
57
+ # OK
58
+ elsif http_status_code.is_a?(Integer)
59
+ errors.add self, 'InvalidHTTPStatus', "The HTTP status is not valid (must be one of #{::Rack::Utils::HTTP_STATUS_CODES.keys.join(', ')})"
60
+ else
61
+ errors.add self, 'InvalidHTTPStatus', 'The HTTP status is not valid (must be an integer)'
62
+ end
63
+
64
+ if @authenticator && !(@authenticator.respond_to?(:ancestors) && @authenticator.ancestors.include?(Apia::Authenticator))
65
+ errors.add self, 'InvalidAuthenticator', 'The authenticator must be a class that inherits from Apia::Authenticator'
66
+ end
67
+
68
+ @fields.validate(errors, self)
69
+ @potential_errors&.validate(errors, self)
70
+ end
71
+
72
+ end
73
+ end
74
+ end