aws-sdk-resources 2.0.0.pre
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/lib/aws-sdk-resources.rb +36 -0
- data/lib/aws-sdk-resources/batch.rb +100 -0
- data/lib/aws-sdk-resources/builder.rb +86 -0
- data/lib/aws-sdk-resources/builder_sources.rb +136 -0
- data/lib/aws-sdk-resources/collection.rb +137 -0
- data/lib/aws-sdk-resources/definition.rb +363 -0
- data/lib/aws-sdk-resources/documenter.rb +18 -0
- data/lib/aws-sdk-resources/documenter/base_operation_documenter.rb +269 -0
- data/lib/aws-sdk-resources/documenter/data_operation_documenter.rb +47 -0
- data/lib/aws-sdk-resources/documenter/enumerate_data_operation_documenter.rb +50 -0
- data/lib/aws-sdk-resources/documenter/enumerate_resource_operation_documenter.rb +69 -0
- data/lib/aws-sdk-resources/documenter/operation_documenter.rb +43 -0
- data/lib/aws-sdk-resources/documenter/reference_operation_documenter.rb +102 -0
- data/lib/aws-sdk-resources/documenter/resource_operation_documenter.rb +65 -0
- data/lib/aws-sdk-resources/documenter/waiter_operation_documenter.rb +81 -0
- data/lib/aws-sdk-resources/errors.rb +15 -0
- data/lib/aws-sdk-resources/operation_methods.rb +53 -0
- data/lib/aws-sdk-resources/operations.rb +294 -0
- data/lib/aws-sdk-resources/options.rb +23 -0
- data/lib/aws-sdk-resources/request.rb +39 -0
- data/lib/aws-sdk-resources/request_params.rb +225 -0
- data/lib/aws-sdk-resources/resource.rb +137 -0
- data/lib/aws-sdk-resources/source.rb +39 -0
- data/lib/aws-sdk-resources/validator.rb +152 -0
- data/lib/aws-sdk-resources/validator/context.rb +60 -0
- data/lib/aws-sdk-resources/validator/identifier_validator.rb +107 -0
- data/lib/aws-sdk-resources/validator/operation_validator.rb +352 -0
- data/lib/aws-sdk-resources/validator/rule.rb +45 -0
- data/lib/aws-sdk-resources/validator/shape_validator.rb +47 -0
- metadata +87 -0
@@ -0,0 +1,137 @@
|
|
1
|
+
module Aws
|
2
|
+
module Resources
|
3
|
+
class Resource
|
4
|
+
|
5
|
+
extend OperationMethods
|
6
|
+
|
7
|
+
# @overload initialize(options = {})
|
8
|
+
# @overload initialize(*identifiers, options = {})
|
9
|
+
# @param options Options except `:data` and identifier options are
|
10
|
+
# used to construct a {Client} unless `:client` is given.
|
11
|
+
# @option options [Client] :client
|
12
|
+
def initialize(*args)
|
13
|
+
options = args.last.is_a?(Hash) ? args.pop.dup : {}
|
14
|
+
@identifiers = extract_identifiers(args, options)
|
15
|
+
@data = options.delete(:data)
|
16
|
+
@client = extract_client(options)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Marked private to prevent double documentation
|
20
|
+
# @return [Client]
|
21
|
+
attr_reader :client
|
22
|
+
|
23
|
+
# Marked private to prevent double documentation
|
24
|
+
# @return [Hash<Symbol,String>]
|
25
|
+
attr_reader :identifiers
|
26
|
+
|
27
|
+
# @return [Struct]
|
28
|
+
def data
|
29
|
+
load unless @data
|
30
|
+
@data
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Boolean] Returns `true` if {#data} has been loaded.
|
34
|
+
def data_loaded?
|
35
|
+
!@data.nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
# Loads data for this resource.
|
39
|
+
# @note Calling this method will send a request to AWS.
|
40
|
+
# @return [self]
|
41
|
+
def load
|
42
|
+
if load_operation = self.class.load_operation
|
43
|
+
@data = load_operation.call(resource:self, client:client)
|
44
|
+
self
|
45
|
+
else
|
46
|
+
raise NotImplementedError, "#load not defined for #{self.class.name}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
alias reload load
|
50
|
+
|
51
|
+
# @api private
|
52
|
+
def inspect
|
53
|
+
identifiers = self.identifiers.map do |name, value|
|
54
|
+
"#{name}=#{value.inspect}"
|
55
|
+
end.join(', ')
|
56
|
+
"#<#{[self.class.name, identifiers].join(' ').strip}>"
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def extract_client(options)
|
62
|
+
if options[:client]
|
63
|
+
options[:client]
|
64
|
+
else
|
65
|
+
self.class.client_class.new(options)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def extract_identifiers(args, options)
|
70
|
+
identifiers = {}
|
71
|
+
self.class.identifiers.each.with_index do |name, n|
|
72
|
+
if args[n]
|
73
|
+
identifiers[name] = args[n]
|
74
|
+
elsif options.key?(name)
|
75
|
+
identifiers[name] = options.delete(name)
|
76
|
+
else
|
77
|
+
raise ArgumentError, "missing required option #{name.inspect}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
identifiers
|
81
|
+
end
|
82
|
+
|
83
|
+
class << self
|
84
|
+
|
85
|
+
# @return [String, nil] The resource name.
|
86
|
+
attr_accessor :resource_name
|
87
|
+
|
88
|
+
# @return [Class<Client>, nil] When constructing
|
89
|
+
# a resource, the client will default to an instance of the
|
90
|
+
# this class.
|
91
|
+
attr_accessor :client_class
|
92
|
+
|
93
|
+
# @return [Operations::DataOperation, nil]
|
94
|
+
attr_accessor :load_operation
|
95
|
+
|
96
|
+
# @return [Array<Symbol>]
|
97
|
+
# @see add_identifier
|
98
|
+
# @see #identifiers
|
99
|
+
def identifiers
|
100
|
+
@identifiers.dup
|
101
|
+
end
|
102
|
+
|
103
|
+
# @param [Symbol] name
|
104
|
+
# @return [void]
|
105
|
+
def add_identifier(name)
|
106
|
+
name = name.to_sym
|
107
|
+
safe_define_method(name) { @identifiers[name] }
|
108
|
+
@identifiers << name
|
109
|
+
end
|
110
|
+
|
111
|
+
# Registers a data attribute. This defines a simple getter
|
112
|
+
# for the attribute which will access {#data}, loading the
|
113
|
+
# resource if necessary.
|
114
|
+
# @param [Symbol] name
|
115
|
+
# @return [void]
|
116
|
+
def add_data_attribute(name)
|
117
|
+
safe_define_method(name) { data[name] }
|
118
|
+
@data_attributes << name
|
119
|
+
end
|
120
|
+
|
121
|
+
# @return [Array<Symbol>] Returns an array of symbolized data
|
122
|
+
# attribute names.
|
123
|
+
def data_attributes
|
124
|
+
@data_attributes.dup
|
125
|
+
end
|
126
|
+
|
127
|
+
# @api private
|
128
|
+
def inherited(subclass)
|
129
|
+
subclass.send(:instance_variable_set, "@identifiers", [])
|
130
|
+
subclass.send(:instance_variable_set, "@data_attributes", [])
|
131
|
+
super
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Aws
|
4
|
+
module Resources
|
5
|
+
class Source
|
6
|
+
|
7
|
+
def initialize(definition, file = nil)
|
8
|
+
@definition = definition
|
9
|
+
@file = file
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [Hash]
|
13
|
+
attr_reader :definition
|
14
|
+
|
15
|
+
# @return [String, nil]
|
16
|
+
attr_reader :file
|
17
|
+
|
18
|
+
def format
|
19
|
+
json = JSON.pretty_generate(definition, indent: ' ', space: '')
|
20
|
+
stack = [[]]
|
21
|
+
json.lines.each do |line|
|
22
|
+
if line.match(/({|\[)$/)
|
23
|
+
stack.push([])
|
24
|
+
end
|
25
|
+
stack.last.push(line)
|
26
|
+
if line.match(/(}|\]),?$/)
|
27
|
+
frame = stack.pop
|
28
|
+
if frame.size == 3 && !frame[1].match(/[{}]/)
|
29
|
+
frame = [frame[0].rstrip, '', frame[1].strip, '', frame[2].lstrip]
|
30
|
+
end
|
31
|
+
stack.last.push(frame.join)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
stack.last.join
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
require 'json-schema'
|
3
|
+
require 'aws-sdk-resources/validator/context'
|
4
|
+
require 'aws-sdk-resources/validator/rule'
|
5
|
+
require 'aws-sdk-resources/validator/shape_validator'
|
6
|
+
require 'aws-sdk-resources/validator/identifier_validator'
|
7
|
+
require 'aws-sdk-resources/validator/operation_validator'
|
8
|
+
|
9
|
+
module Aws
|
10
|
+
module Resources
|
11
|
+
module Validator
|
12
|
+
|
13
|
+
# @api private
|
14
|
+
RULES = []
|
15
|
+
|
16
|
+
# @api private
|
17
|
+
SCHEMA_PATH = File.expand_path(File.join([
|
18
|
+
File.dirname(__FILE__), '..', '..', 'resources.schema.json'
|
19
|
+
]))
|
20
|
+
|
21
|
+
class << self
|
22
|
+
|
23
|
+
def match(pattern, &block)
|
24
|
+
RULES << Rule.new(pattern, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param [Hash] definition
|
28
|
+
# @param [Hash] api
|
29
|
+
# @return [Array<String>]
|
30
|
+
def validate(definition, api)
|
31
|
+
errors = apply_schema(definition)
|
32
|
+
errors = lint('#', definition, definition, api) if errors.empty?
|
33
|
+
errors
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# Validates the resource definition document against the JSON
|
39
|
+
# schema for resources.
|
40
|
+
# @param [Hash] definition
|
41
|
+
# @return [Array<String>] Returns an array of schema validation errors.
|
42
|
+
# Returns an empty array if there are no errors.
|
43
|
+
def apply_schema(definition)
|
44
|
+
schema = MultiJson.load(File.read(SCHEMA_PATH))
|
45
|
+
JSON::Validator.fully_validate(schema, definition)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Recursively lints the resource definition hash against the given
|
49
|
+
# api.
|
50
|
+
# @param [Hash] definition
|
51
|
+
# @param [Hash] api
|
52
|
+
# @return [Array<String>] Returns an array of schema validation errors.
|
53
|
+
# Returns an empty array if there are no errors.
|
54
|
+
def lint(prefix, node, definition, api, errors = [])
|
55
|
+
lint_node(prefix, node, definition, api, errors)
|
56
|
+
case node
|
57
|
+
when Hash
|
58
|
+
node.each do |key, value|
|
59
|
+
lint("#{prefix}/#{key}", value, definition, api, errors)
|
60
|
+
end
|
61
|
+
when Array
|
62
|
+
node.each.with_index do |value, index|
|
63
|
+
lint("#{prefix}/#{index}", value, definition, api, errors)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
errors
|
67
|
+
end
|
68
|
+
|
69
|
+
def lint_node(path, value, definition, api, errors)
|
70
|
+
RULES.each do |rule|
|
71
|
+
if rule.applies?(path)
|
72
|
+
errors.concat(rule.validate(path, value, definition, api))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
match('#/resources/(\w+)/shape') do |context|
|
80
|
+
ShapeValidator.new(context, context.value).validate
|
81
|
+
end
|
82
|
+
|
83
|
+
match('#/resources/(\w+)/identifiers') do |context|
|
84
|
+
context.value.each.with_index do |identifier, index|
|
85
|
+
IdentifierValidator.new(context, identifier, index).validate
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
match('#/resources/(\w+)/load') do |context|
|
90
|
+
v = OperationValidator.new(context)
|
91
|
+
v.validate_request do
|
92
|
+
v.validate_param_source_type_not_used('dataMember')
|
93
|
+
v.validate_path(origin: :response, target: :self)
|
94
|
+
end
|
95
|
+
v.validate_path_set
|
96
|
+
v.validate_resource_not_set
|
97
|
+
end
|
98
|
+
|
99
|
+
match('#/resources/(\w+)/actions/\w+') do |context|
|
100
|
+
v = OperationValidator.new(context)
|
101
|
+
v.validate_request
|
102
|
+
if v.resource_set?
|
103
|
+
v.validate_resource
|
104
|
+
v.validate_path(origin: :response, target: :resource)
|
105
|
+
else
|
106
|
+
v.validate_path(origin: :response)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
match('#/resources/(\w+)/batchActions/\w+') do |context|
|
111
|
+
# TODO : much like a normal action???
|
112
|
+
end
|
113
|
+
|
114
|
+
match('#/resources/(\w+)/hasMany/\w+') do |context|
|
115
|
+
v = OperationValidator.new(context)
|
116
|
+
v.validate_request
|
117
|
+
v.validate_resource do
|
118
|
+
# at least one of the identifier sources must be plural
|
119
|
+
end
|
120
|
+
if v.path_set?
|
121
|
+
v.validate_path(origin: :response, target: :resource)
|
122
|
+
v.validate_path_is_plural
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
match('#/resources/(\w+)/(hasSome|hasOne)/\w+') do |context|
|
127
|
+
v = OperationValidator.new(context)
|
128
|
+
v.validate_request_not_set
|
129
|
+
v.validate_resource do
|
130
|
+
# disallow requestParameter
|
131
|
+
# disallow responsepath
|
132
|
+
end
|
133
|
+
# path must resolve FROM the resource data shape
|
134
|
+
# to the target resource data
|
135
|
+
v.validate_path(origin: :self, target: :resource)
|
136
|
+
|
137
|
+
if context.matches[2] == 'hasSome'
|
138
|
+
# at least one identifier source must be plural
|
139
|
+
# path must be plural if given
|
140
|
+
else
|
141
|
+
# identifier sources must be singular
|
142
|
+
# path must be singular if given
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
match('#/resources/(\w+)/subResources') do |context|
|
147
|
+
# TODO : validate subResources
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Aws
|
2
|
+
module Resources
|
3
|
+
module Validator
|
4
|
+
class Context
|
5
|
+
|
6
|
+
# @option options [required, String] :path
|
7
|
+
# @option options [required, Object] :value
|
8
|
+
# @option options [required, Hash] :definition
|
9
|
+
# @option options [required, Hash] :api
|
10
|
+
# @option options [required, MatchData] :matches
|
11
|
+
def initialize(options = {})
|
12
|
+
[:path, :value, :definition, :api, :matches].each do |opt|
|
13
|
+
if options.key?(opt)
|
14
|
+
instance_variable_set("@#{opt}", options[opt])
|
15
|
+
else
|
16
|
+
raise ArgumentError, "missing required option :#{opt}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
@errors = []
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [String]
|
23
|
+
attr_reader :path
|
24
|
+
|
25
|
+
# @return [Object]
|
26
|
+
attr_reader :value
|
27
|
+
|
28
|
+
# @return [Hash]
|
29
|
+
attr_reader :definition
|
30
|
+
|
31
|
+
# @return [Hash]
|
32
|
+
attr_reader :api
|
33
|
+
|
34
|
+
# @return [MatchData]
|
35
|
+
attr_reader :matches
|
36
|
+
|
37
|
+
# @return [Array<String>]
|
38
|
+
attr_reader :errors
|
39
|
+
|
40
|
+
def error(msg)
|
41
|
+
@errors << msg
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
def resource
|
46
|
+
definition['resources'][matches[1]]
|
47
|
+
end
|
48
|
+
|
49
|
+
def resource_name
|
50
|
+
matches[1]
|
51
|
+
end
|
52
|
+
|
53
|
+
def shape(name)
|
54
|
+
api['shapes'][name]
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Aws
|
2
|
+
module Resources
|
3
|
+
module Validator
|
4
|
+
class IdentifierValidator
|
5
|
+
|
6
|
+
# @param [Validator::Context] context
|
7
|
+
# @param [Hash] identifier
|
8
|
+
# @param [Integer] index
|
9
|
+
def initialize(context, identifier, index)
|
10
|
+
@context = context
|
11
|
+
@identifier = identifier
|
12
|
+
@index = index
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :identifier
|
16
|
+
|
17
|
+
attr_reader :index
|
18
|
+
|
19
|
+
def validate
|
20
|
+
validate_name_is_uniq
|
21
|
+
validate_name_is_not_prefixed
|
22
|
+
if member_name
|
23
|
+
validate_resource_has_shape && validate_resource_shape_contains_member
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def validate_name_is_uniq
|
30
|
+
if all_identifier_names.count(name) > 1
|
31
|
+
error("#{path("#{index}/name")} must be uniq within #{path('*/name')}.")
|
32
|
+
else
|
33
|
+
true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate_name_is_not_prefixed
|
38
|
+
if name.match(/^#{resource_name}/)
|
39
|
+
error("#{path("#{index}/name")} must not be prefixed with '#{resource_name}'.")
|
40
|
+
else
|
41
|
+
true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def validate_resource_has_shape
|
46
|
+
if resource_shape_name
|
47
|
+
true
|
48
|
+
else
|
49
|
+
error("#{path("#{index}/memberName")} requires '#/resources/#{resource_name}/shape' to be set.")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def validate_resource_shape_contains_member
|
54
|
+
if
|
55
|
+
resource_shape['type'] == 'structure' &&
|
56
|
+
resource_shape['members'].key?(member_name)
|
57
|
+
then
|
58
|
+
true
|
59
|
+
else
|
60
|
+
error("#{path("#{index}/memberName")} is not defined at 'api#/shapes/#{resource_shape_name}/members/#{member_name}'.")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def error(msg)
|
65
|
+
@context.error(msg)
|
66
|
+
end
|
67
|
+
|
68
|
+
def path(suffix = nil)
|
69
|
+
if suffix
|
70
|
+
"'#{@context.path}/#{suffix}'"
|
71
|
+
else
|
72
|
+
"'#{@context.path}'"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def name
|
77
|
+
@identifier['name']
|
78
|
+
end
|
79
|
+
|
80
|
+
def member_name
|
81
|
+
@identifier['memberName']
|
82
|
+
end
|
83
|
+
|
84
|
+
def resource_name
|
85
|
+
@context.matches[1]
|
86
|
+
end
|
87
|
+
|
88
|
+
def all_identifier_names
|
89
|
+
@context.value.map { |i| i['name'] }
|
90
|
+
end
|
91
|
+
|
92
|
+
def resource
|
93
|
+
@context.definition['resources'][resource_name]
|
94
|
+
end
|
95
|
+
|
96
|
+
def resource_shape_name
|
97
|
+
resource['shape']
|
98
|
+
end
|
99
|
+
|
100
|
+
def resource_shape
|
101
|
+
@context.api['shapes'][resource_shape_name]
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|