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.
- checksums.yaml +7 -0
- data/VERSION +1 -0
- data/lib/apia.rb +21 -0
- data/lib/apia/api.rb +100 -0
- data/lib/apia/argument_set.rb +221 -0
- data/lib/apia/authenticator.rb +57 -0
- data/lib/apia/callable_with_environment.rb +43 -0
- data/lib/apia/controller.rb +32 -0
- data/lib/apia/defineable.rb +60 -0
- data/lib/apia/definition.rb +27 -0
- data/lib/apia/definitions/api.rb +51 -0
- data/lib/apia/definitions/argument.rb +77 -0
- data/lib/apia/definitions/argument_set.rb +33 -0
- data/lib/apia/definitions/authenticator.rb +46 -0
- data/lib/apia/definitions/controller.rb +41 -0
- data/lib/apia/definitions/endpoint.rb +74 -0
- data/lib/apia/definitions/enum.rb +31 -0
- data/lib/apia/definitions/error.rb +59 -0
- data/lib/apia/definitions/field.rb +117 -0
- data/lib/apia/definitions/lookup_argument_set.rb +27 -0
- data/lib/apia/definitions/object.rb +29 -0
- data/lib/apia/definitions/polymorph.rb +29 -0
- data/lib/apia/definitions/polymorph_option.rb +53 -0
- data/lib/apia/definitions/scalar.rb +23 -0
- data/lib/apia/definitions/type.rb +109 -0
- data/lib/apia/dsl.rb +23 -0
- data/lib/apia/dsls/api.rb +37 -0
- data/lib/apia/dsls/argument.rb +27 -0
- data/lib/apia/dsls/argument_set.rb +35 -0
- data/lib/apia/dsls/authenticator.rb +38 -0
- data/lib/apia/dsls/concerns/has_fields.rb +38 -0
- data/lib/apia/dsls/controller.rb +34 -0
- data/lib/apia/dsls/endpoint.rb +79 -0
- data/lib/apia/dsls/enum.rb +19 -0
- data/lib/apia/dsls/error.rb +26 -0
- data/lib/apia/dsls/field.rb +27 -0
- data/lib/apia/dsls/lookup_argument_set.rb +24 -0
- data/lib/apia/dsls/object.rb +19 -0
- data/lib/apia/dsls/polymorph.rb +19 -0
- data/lib/apia/dsls/route_group.rb +43 -0
- data/lib/apia/dsls/route_set.rb +40 -0
- data/lib/apia/dsls/scalar.rb +23 -0
- data/lib/apia/dsls/scope_descriptions.rb +17 -0
- data/lib/apia/endpoint.rb +110 -0
- data/lib/apia/enum.rb +43 -0
- data/lib/apia/environment_error_handling.rb +74 -0
- data/lib/apia/error.rb +61 -0
- data/lib/apia/error_set.rb +15 -0
- data/lib/apia/errors/error_exception_error.rb +32 -0
- data/lib/apia/errors/field_spec_parse_error.rb +23 -0
- data/lib/apia/errors/invalid_argument_error.rb +68 -0
- data/lib/apia/errors/invalid_enum_option_error.rb +21 -0
- data/lib/apia/errors/invalid_helper_error.rb +6 -0
- data/lib/apia/errors/invalid_json_error.rb +23 -0
- data/lib/apia/errors/invalid_polymorph_value_error.rb +21 -0
- data/lib/apia/errors/invalid_scalar_value_error.rb +21 -0
- data/lib/apia/errors/manifest_error.rb +43 -0
- data/lib/apia/errors/missing_argument_error.rb +40 -0
- data/lib/apia/errors/null_field_value_error.rb +37 -0
- data/lib/apia/errors/parse_error.rb +10 -0
- data/lib/apia/errors/runtime_error.rb +30 -0
- data/lib/apia/errors/scope_not_granted_error.rb +15 -0
- data/lib/apia/errors/standard_error.rb +6 -0
- data/lib/apia/field_set.rb +76 -0
- data/lib/apia/field_spec.rb +155 -0
- data/lib/apia/helpers.rb +34 -0
- data/lib/apia/hook_set.rb +30 -0
- data/lib/apia/lookup_argument_set.rb +57 -0
- data/lib/apia/lookup_environment.rb +27 -0
- data/lib/apia/manifest_errors.rb +62 -0
- data/lib/apia/mock_request.rb +18 -0
- data/lib/apia/object.rb +68 -0
- data/lib/apia/object_set.rb +21 -0
- data/lib/apia/pagination_object.rb +34 -0
- data/lib/apia/polymorph.rb +50 -0
- data/lib/apia/rack.rb +184 -0
- data/lib/apia/rack_error.rb +17 -0
- data/lib/apia/request.rb +67 -0
- data/lib/apia/request_environment.rb +84 -0
- data/lib/apia/request_headers.rb +42 -0
- data/lib/apia/response.rb +64 -0
- data/lib/apia/route.rb +61 -0
- data/lib/apia/route_group.rb +20 -0
- data/lib/apia/route_set.rb +89 -0
- data/lib/apia/scalar.rb +52 -0
- data/lib/apia/scalars.rb +25 -0
- data/lib/apia/scalars/base64.rb +31 -0
- data/lib/apia/scalars/boolean.rb +37 -0
- data/lib/apia/scalars/date.rb +45 -0
- data/lib/apia/scalars/decimal.rb +36 -0
- data/lib/apia/scalars/integer.rb +34 -0
- data/lib/apia/scalars/string.rb +24 -0
- data/lib/apia/scalars/unix_time.rb +40 -0
- data/lib/apia/schema/api_controller_schema_type.rb +17 -0
- data/lib/apia/schema/api_schema_type.rb +43 -0
- data/lib/apia/schema/argument_schema_type.rb +28 -0
- data/lib/apia/schema/argument_set_schema_type.rb +21 -0
- data/lib/apia/schema/authenticator_schema_type.rb +22 -0
- data/lib/apia/schema/controller.rb +39 -0
- data/lib/apia/schema/controller_endpoint_schema_type.rb +17 -0
- data/lib/apia/schema/controller_schema_type.rb +32 -0
- data/lib/apia/schema/endpoint_schema_type.rb +35 -0
- data/lib/apia/schema/enum_schema_type.rb +20 -0
- data/lib/apia/schema/enum_value_schema_type.rb +14 -0
- data/lib/apia/schema/error_schema_type.rb +23 -0
- data/lib/apia/schema/field_schema_type.rb +38 -0
- data/lib/apia/schema/field_spec_options_schema_type.rb +16 -0
- data/lib/apia/schema/lookup_argument_set_schema_type.rb +25 -0
- data/lib/apia/schema/object_schema_polymorph.rb +31 -0
- data/lib/apia/schema/object_schema_type.rb +21 -0
- data/lib/apia/schema/polymorph_option_schema_type.rb +16 -0
- data/lib/apia/schema/polymorph_schema_type.rb +20 -0
- data/lib/apia/schema/request_method_enum.rb +21 -0
- data/lib/apia/schema/route_group_schema_type.rb +19 -0
- data/lib/apia/schema/route_schema_type.rb +31 -0
- data/lib/apia/schema/route_set_schema_type.rb +20 -0
- data/lib/apia/schema/scalar_schema_type.rb +15 -0
- data/lib/apia/schema/scope_type.rb +14 -0
- data/lib/apia/version.rb +12 -0
- 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
|
data/lib/apia/helpers.rb
ADDED
@@ -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
|
data/lib/apia/object.rb
ADDED
@@ -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
|