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