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.
Files changed (31) hide show
  1. checksums.yaml +7 -0
  2. data/lib/aws-sdk-resources.rb +36 -0
  3. data/lib/aws-sdk-resources/batch.rb +100 -0
  4. data/lib/aws-sdk-resources/builder.rb +86 -0
  5. data/lib/aws-sdk-resources/builder_sources.rb +136 -0
  6. data/lib/aws-sdk-resources/collection.rb +137 -0
  7. data/lib/aws-sdk-resources/definition.rb +363 -0
  8. data/lib/aws-sdk-resources/documenter.rb +18 -0
  9. data/lib/aws-sdk-resources/documenter/base_operation_documenter.rb +269 -0
  10. data/lib/aws-sdk-resources/documenter/data_operation_documenter.rb +47 -0
  11. data/lib/aws-sdk-resources/documenter/enumerate_data_operation_documenter.rb +50 -0
  12. data/lib/aws-sdk-resources/documenter/enumerate_resource_operation_documenter.rb +69 -0
  13. data/lib/aws-sdk-resources/documenter/operation_documenter.rb +43 -0
  14. data/lib/aws-sdk-resources/documenter/reference_operation_documenter.rb +102 -0
  15. data/lib/aws-sdk-resources/documenter/resource_operation_documenter.rb +65 -0
  16. data/lib/aws-sdk-resources/documenter/waiter_operation_documenter.rb +81 -0
  17. data/lib/aws-sdk-resources/errors.rb +15 -0
  18. data/lib/aws-sdk-resources/operation_methods.rb +53 -0
  19. data/lib/aws-sdk-resources/operations.rb +294 -0
  20. data/lib/aws-sdk-resources/options.rb +23 -0
  21. data/lib/aws-sdk-resources/request.rb +39 -0
  22. data/lib/aws-sdk-resources/request_params.rb +225 -0
  23. data/lib/aws-sdk-resources/resource.rb +137 -0
  24. data/lib/aws-sdk-resources/source.rb +39 -0
  25. data/lib/aws-sdk-resources/validator.rb +152 -0
  26. data/lib/aws-sdk-resources/validator/context.rb +60 -0
  27. data/lib/aws-sdk-resources/validator/identifier_validator.rb +107 -0
  28. data/lib/aws-sdk-resources/validator/operation_validator.rb +352 -0
  29. data/lib/aws-sdk-resources/validator/rule.rb +45 -0
  30. data/lib/aws-sdk-resources/validator/shape_validator.rb +47 -0
  31. 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