apia 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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