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,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/errors/field_spec_parse_error'
4
+
5
+ module Apia
6
+ class FieldSpec
7
+
8
+ attr_reader :paths
9
+ attr_reader :excludes
10
+ attr_reader :parsed_string
11
+
12
+ def initialize(paths, excludes: [], parsed_string: nil)
13
+ @paths = paths
14
+ @excludes = excludes
15
+ @parsed_string = parsed_string
16
+ end
17
+
18
+ def include_field?(field_path)
19
+ if field_path.is_a?(String)
20
+ path = field_path.split('.')
21
+ else
22
+ path = field_path.map { |r| r.name.to_s }
23
+ end
24
+
25
+ # If the field path matches exactly any item in the list of paths
26
+ # allowed, then allow this path.
27
+ return true if @paths.include?(path.join('.'))
28
+
29
+ # If the field is purposely excluded, we'll check that and ensure that it
30
+ # isn't included.
31
+ return false if @excludes.include?(path.join('.'))
32
+
33
+ # If there's a wildcard at the root we can allow it at this point
34
+ # return true if @paths.include?('*')
35
+
36
+ # Check to see whether we're allowing a wildcard to be permitted at any
37
+ # point in the chain
38
+ path.size.times do |i|
39
+ parts = path[0, path.size - i - 1]
40
+
41
+ next unless @paths.include?((parts + ['*']).join('.'))
42
+
43
+ next_parts = path[0, path.size - i]
44
+ unless @paths.include?(next_parts.join('.'))
45
+ return true
46
+ end
47
+ end
48
+
49
+ false
50
+ end
51
+
52
+ class Parser
53
+
54
+ def initialize(string)
55
+ @string = string
56
+ @paths = Set.new
57
+ @excludes = Set.new
58
+ @type = :normal
59
+ @last_word = ''
60
+ @sections = []
61
+ end
62
+
63
+ def parse
64
+ @string.each_char do |character|
65
+ case character
66
+ when ','
67
+ next if @last_word.empty?
68
+
69
+ add_last_word
70
+
71
+ when '['
72
+ if @last_word.empty?
73
+ raise FieldSpecParseError, '[ requires a word before it'
74
+ end
75
+
76
+ @sections << @last_word
77
+ @paths << @sections.join('.')
78
+ @last_word = ''
79
+
80
+ when ']'
81
+ if @sections.last.nil?
82
+ raise FieldSpecParseError, 'unopened bracket closure'
83
+ end
84
+
85
+ add_last_word unless @last_word.empty?
86
+
87
+ @sections.pop
88
+
89
+ when /\s+/
90
+ # Ignore whitespace
91
+
92
+ when '-'
93
+ if @last_word.empty?
94
+ @type = :exclude
95
+ else
96
+ add_last_word
97
+ end
98
+
99
+ when 'a'..'z', '0'..'9', '_', '*'
100
+ @last_word += character
101
+
102
+ else
103
+ raise FieldSpecParseError, "invalid character #{character}"
104
+ end
105
+ end
106
+
107
+ unless @sections.empty?
108
+ raise FieldSpecParseError, 'unbalanced brackets'
109
+ end
110
+
111
+ add_last_word
112
+
113
+ FieldSpec.new(@paths, excludes: @excludes, parsed_string: @string)
114
+ end
115
+
116
+ private
117
+
118
+ def add_last_word
119
+ return if @last_word.empty?
120
+
121
+ case @type
122
+ when :exclude
123
+ destination = @excludes
124
+ else
125
+ destination = @paths
126
+ end
127
+
128
+ if @sections.empty?
129
+ destination << @last_word
130
+ else
131
+ destination << "#{@sections.join('.')}.#{@last_word}"
132
+ end
133
+
134
+ @last_word = ''
135
+ @type = :normal
136
+ end
137
+
138
+ end
139
+
140
+ class << self
141
+
142
+ # data_center # => Return all default attributes for data center
143
+ # data_center[name] # => Only return name for data center
144
+ # data_center[+country[id,name]] # => Add the country to the default parameters with it only containing id and name
145
+ # data_center[-country] # => Remove country from the default parameters (assuming it is part of them)
146
+ # data_center[name,+country] # => Pointless but should return name plus the default country params (same as name,country)
147
+ def parse(string)
148
+ parser = Parser.new(string)
149
+ parser.parse
150
+ end
151
+
152
+ end
153
+
154
+ end
155
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/scalars'
4
+
5
+ module Apia
6
+ module Helpers
7
+
8
+ class << self
9
+
10
+ # Convert a ruby class name into an ID for use by objects
11
+ #
12
+ # @param name [String]
13
+ # @return [String]
14
+ def class_name_to_id(name)
15
+ name.to_s.gsub('::', '/')
16
+ end
17
+
18
+ # Convert a string into CamelCase
19
+ #
20
+ # @param string [String, nil]
21
+ # @return [String, nil]
22
+ def camelize(string)
23
+ return nil if string.nil?
24
+
25
+ string = string.to_s.sub(/^[a-z\d]*/) { |match| match.capitalize }
26
+ string.gsub(/(?:_)([a-z\d]*)/) do
27
+ Regexp.last_match(1).capitalize.to_s
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apia
4
+ class HookSet
5
+
6
+ def initialize
7
+ @hooks = []
8
+ end
9
+
10
+ def add(block_by_var = nil, &block)
11
+ @hooks << block_by_var if block_by_var.is_a?(Proc)
12
+ @hooks << block if block_given?
13
+ end
14
+
15
+ def call(*args)
16
+ @hooks.map do |hook|
17
+ hook.call(*args)
18
+ end
19
+ end
20
+
21
+ def include?(proc)
22
+ @hooks.include?(proc)
23
+ end
24
+
25
+ def size
26
+ @hooks.size
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/argument_set'
4
+ require 'apia/definitions/lookup_argument_set'
5
+ require 'apia/lookup_environment'
6
+
7
+ module Apia
8
+ class LookupArgumentSet < ArgumentSet
9
+
10
+ class << self
11
+
12
+ # Return the definition for this argument set
13
+ #
14
+ # @return [Apia::Definitions::ArgumentSet]
15
+ def definition
16
+ @definition ||= Definitions::LookupArgumentSet.new(Helpers.class_name_to_id(name))
17
+ end
18
+
19
+ # Finds all objects referenced by this argument set and add them
20
+ # to the provided set.
21
+ #
22
+ # @param set [Apia::ObjectSet]
23
+ # @return [void]
24
+ def collate_objects(set)
25
+ super
26
+
27
+ definition.potential_errors.each do |error|
28
+ set.add_object(error)
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ def resolve(*args)
35
+ return if self.class.definition.resolver.nil?
36
+
37
+ environment.call(@request, *args, &self.class.definition.resolver)
38
+ end
39
+
40
+ def environment
41
+ @environment ||= LookupEnvironment.new(self)
42
+ end
43
+
44
+ def validate(argument, index: nil)
45
+ if @source.empty?
46
+ raise InvalidArgumentError.new(argument, issue: :missing_lookup_value, index: index, path: @path)
47
+ end
48
+
49
+ if @source.values.compact.size > 1
50
+ raise InvalidArgumentError.new(argument, issue: :ambiguous_lookup_values, index: index, path: @path)
51
+ end
52
+
53
+ true
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/environment_error_handling'
4
+
5
+ module Apia
6
+ class LookupEnvironment
7
+
8
+ include EnvironmentErrorHandling
9
+
10
+ def initialize(set)
11
+ @set = set
12
+ end
13
+
14
+ def call(request, *args, &block)
15
+ return unless block_given?
16
+
17
+ instance_exec(@set, request, *args, &block)
18
+ end
19
+
20
+ private
21
+
22
+ def potential_error_sources
23
+ [@set.class]
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/errors/manifest_error'
4
+
5
+ module Apia
6
+ class ManifestErrors
7
+
8
+ attr_reader :errors
9
+
10
+ def initialize
11
+ @errors = {}
12
+ end
13
+
14
+ def add(object, code, message)
15
+ @errors[object] ||= Errors.new
16
+ @errors[object].add(code: code, message: message)
17
+ end
18
+
19
+ def for(object)
20
+ @errors[object] || Errors.new
21
+ end
22
+
23
+ def empty?
24
+ @errors.empty?
25
+ end
26
+
27
+ def raise_if_needed
28
+ return if empty?
29
+
30
+ raise ManifestError, self
31
+ end
32
+
33
+ class Errors
34
+
35
+ def initialize
36
+ @errors = []
37
+ end
38
+
39
+ def add(code:, message:)
40
+ @errors << { code: code, message: message }
41
+ end
42
+
43
+ def include?(code)
44
+ @errors.any? { |e| e[:code] == code }
45
+ end
46
+
47
+ def empty?
48
+ @errors.empty?
49
+ end
50
+
51
+ def each(&block)
52
+ @errors.each(&block)
53
+ end
54
+
55
+ def map(&block)
56
+ @errors.map(&block)
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/request'
4
+
5
+ module Apia
6
+ class MockRequest < Request
7
+
8
+ def json_body
9
+ @json_body ||= {}
10
+ end
11
+
12
+ def ip
13
+ @ip ||= '127.0.0.1'
14
+ end
15
+ attr_writer :ip
16
+
17
+ end
18
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apia/helpers'
4
+ require 'apia/definitions/object'
5
+ require 'apia/defineable'
6
+
7
+ module Apia
8
+ class Object
9
+
10
+ extend Defineable
11
+
12
+ class << self
13
+
14
+ # Return the definition for this type
15
+ #
16
+ # @return [Apia::Definitions::Object]
17
+ def definition
18
+ @definition ||= Definitions::Object.new(Helpers.class_name_to_id(name))
19
+ end
20
+
21
+ # Collate all objects that this type 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
+ definition.fields.each_value do |field|
28
+ set.add_object(field.type.klass) if field.type.usable_for_field?
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ # Initialize an instance of this type with the value provided
35
+ #
36
+ # @param value [Object, Hash]
37
+ # @return [Apia::Object]
38
+ def initialize(value)
39
+ @value = value
40
+ end
41
+
42
+ # Return the raw value object for this type
43
+ #
44
+ # @return [Object, Hash]
45
+ attr_reader :value
46
+
47
+ # Generate a hash based on the fields defined in this type
48
+ #
49
+ # @param request [Apia::Request] the associated request
50
+ # @return [Hash]
51
+ def hash(request: nil, path: [])
52
+ self.class.definition.fields.generate_hash(@value, request: request, path: path)
53
+ end
54
+
55
+ # Should this type be included in any output?
56
+ #
57
+ # @param request [Apia::Request]
58
+ # @return [Boolean]
59
+ def include?(request)
60
+ return true if self.class.definition.conditions.empty?
61
+
62
+ self.class.definition.conditions.all? do |cond|
63
+ cond.call(@value, request) == true
64
+ end
65
+ end
66
+
67
+ end
68
+ end