microsoft_graph 0.1.1 → 0.1.2
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 +4 -4
- data/.gitignore +11 -11
- data/.rspec +2 -2
- data/.travis.yml +4 -4
- data/Gemfile +4 -4
- data/LICENSE +10 -10
- data/README.md +98 -98
- data/Rakefile +7 -7
- data/data/metadata_v1.0.xml +1686 -1686
- data/integration_spec/integration_spec_helper.rb +18 -18
- data/integration_spec/live_spec.rb +180 -180
- data/lib/microsoft_graph.rb +35 -35
- data/lib/microsoft_graph/base.rb +110 -110
- data/lib/microsoft_graph/base_entity.rb +152 -152
- data/lib/microsoft_graph/cached_metadata_directory.rb +3 -3
- data/lib/microsoft_graph/class_builder.rb +217 -217
- data/lib/microsoft_graph/collection.rb +95 -95
- data/lib/microsoft_graph/collection_association.rb +232 -230
- data/lib/microsoft_graph/errors.rb +6 -6
- data/lib/microsoft_graph/version.rb +3 -3
- data/lib/odata.rb +49 -49
- data/lib/odata/entity_set.rb +20 -20
- data/lib/odata/errors.rb +18 -18
- data/lib/odata/navigation_property.rb +30 -30
- data/lib/odata/operation.rb +17 -17
- data/lib/odata/property.rb +38 -38
- data/lib/odata/request.rb +49 -49
- data/lib/odata/service.rb +279 -279
- data/lib/odata/singleton.rb +20 -20
- data/lib/odata/type.rb +25 -25
- data/lib/odata/types/collection_type.rb +30 -30
- data/lib/odata/types/complex_type.rb +19 -19
- data/lib/odata/types/entity_type.rb +33 -33
- data/lib/odata/types/enum_type.rb +37 -37
- data/lib/odata/types/primitive_type.rb +12 -12
- data/lib/odata/types/primitive_types/binary_type.rb +15 -15
- data/lib/odata/types/primitive_types/boolean_type.rb +15 -15
- data/lib/odata/types/primitive_types/date_time_offset_type.rb +15 -15
- data/lib/odata/types/primitive_types/date_type.rb +23 -23
- data/lib/odata/types/primitive_types/double_type.rb +16 -16
- data/lib/odata/types/primitive_types/guid_type.rb +24 -24
- data/lib/odata/types/primitive_types/int_16_type.rb +19 -19
- data/lib/odata/types/primitive_types/int_32_type.rb +15 -15
- data/lib/odata/types/primitive_types/int_64_type.rb +15 -15
- data/lib/odata/types/primitive_types/stream_type.rb +15 -15
- data/lib/odata/types/primitive_types/string_type.rb +15 -15
- data/microsoft_graph.gemspec +31 -31
- data/tasks/update_metadata.rb +17 -17
- metadata +5 -5
@@ -1,230 +1,232 @@
|
|
1
|
-
class MicrosoftGraph
|
2
|
-
class CollectionAssociation < Collection
|
3
|
-
attr_reader :type
|
4
|
-
attr_reader :parent
|
5
|
-
|
6
|
-
def initialize(options = {})
|
7
|
-
@type = options[:type]
|
8
|
-
@graph = options[:graph]
|
9
|
-
@resource_name = options[:resource_name]
|
10
|
-
@parent = options[:parent]
|
11
|
-
@order_by = options[:order_by]
|
12
|
-
@filter = options[:filter]
|
13
|
-
@dirty = false
|
14
|
-
@loaded = false
|
15
|
-
@internal_values = []
|
16
|
-
|
17
|
-
raise MicrosoftGraph::TypeError.new("A collection cannot be both ordered and filtered.") if @order_by && @filter
|
18
|
-
@order_by && @order_by.each do |field|
|
19
|
-
field_name, direction = field.to_s.split(' ')
|
20
|
-
field_names = field_name.split("/")
|
21
|
-
unless valid_nested_property(field_names, @type)
|
22
|
-
raise MicrosoftGraph::TypeError.new("Tried to order by invalid field: #{field_name}")
|
23
|
-
end
|
24
|
-
if direction && ! %w(asc desc).include?(direction)
|
25
|
-
raise MicrosoftGraph::TypeError.new("Tried to order field #{field_name} by invalid direction: #{direction}")
|
26
|
-
end
|
27
|
-
end
|
28
|
-
@filter && @filter.respond_to?(:keys) && @filter.keys.each do |field_name|
|
29
|
-
unless @type.properties.map(&:name).include? OData.convert_to_camel_case(field_name)
|
30
|
-
raise MicrosoftGraph::TypeError.new("Tried to filter by invalid field: #{field_name}")
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
def find(id)
|
37
|
-
if response = graph.service.get("#{path}/#{URI.escape(id.to_s)}")
|
38
|
-
klass = if member_type = specified_member_type(response)
|
39
|
-
ClassBuilder.get_namespaced_class(response)
|
40
|
-
else
|
41
|
-
default_member_class
|
42
|
-
end
|
43
|
-
klass.new(
|
44
|
-
graph: graph,
|
45
|
-
parent: self,
|
46
|
-
attributes: response[:attributes],
|
47
|
-
persisted: true
|
48
|
-
)
|
49
|
-
else
|
50
|
-
nil
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def path
|
55
|
-
if parent && parent.path
|
56
|
-
"#{parent.path}/#{@resource_name}"
|
57
|
-
else
|
58
|
-
@resource_name
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def query_path
|
63
|
-
if @order_by
|
64
|
-
order_by_names = @order_by.map do |field|
|
65
|
-
URI.escape OData.convert_to_camel_case(field)
|
66
|
-
end
|
67
|
-
"#{path}?$orderby=#{order_by_names.join(',')}"
|
68
|
-
elsif @filter
|
69
|
-
escaped_filters = URI.escape(stringify_filters(@filter))
|
70
|
-
"#{path}?$filter=#{escaped_filters}"
|
71
|
-
else
|
72
|
-
path
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def order_by(*fields)
|
77
|
-
self.class.new(
|
78
|
-
type: @type,
|
79
|
-
graph: @graph,
|
80
|
-
resource_name: @resource_name,
|
81
|
-
parent: @parent,
|
82
|
-
order_by: fields,
|
83
|
-
filter: @filter
|
84
|
-
)
|
85
|
-
end
|
86
|
-
|
87
|
-
def valid_nested_property(field_names, parent_type)
|
88
|
-
field_name = field_names.shift
|
89
|
-
if field_name
|
90
|
-
property = parent_type.properties.find do |prop|
|
91
|
-
OData.convert_to_camel_case(prop.name) == OData.convert_to_camel_case(field_name)
|
92
|
-
end
|
93
|
-
property && valid_nested_property(field_names, property.type)
|
94
|
-
else
|
95
|
-
true
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def filter(new_filters)
|
100
|
-
self.class.new(
|
101
|
-
type: @type,
|
102
|
-
graph: @graph,
|
103
|
-
resource_name: @resource_name,
|
104
|
-
parent: @parent,
|
105
|
-
order_by: @order_by,
|
106
|
-
filter: merge_with_existing_filter(new_filters)
|
107
|
-
)
|
108
|
-
end
|
109
|
-
|
110
|
-
def merge_with_existing_filter(new_filters)
|
111
|
-
existing_filter = @filter || {}
|
112
|
-
if new_filters.respond_to?(:keys) && existing_filter.respond_to?(:keys)
|
113
|
-
existing_filter.merge(new_filters)
|
114
|
-
else
|
115
|
-
[stringify_filters(new_filters), stringify_filters(@filter)].compact.join(" and ")
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def stringify_filters(filters)
|
120
|
-
if filters.respond_to?(:keys)
|
121
|
-
filter_strings = filters.map do |k,v|
|
122
|
-
v = v.is_a?(String) ? "'#{v}'" : v
|
123
|
-
"#{OData.convert_to_camel_case(k)} eq #{v}"
|
124
|
-
end
|
125
|
-
filter_strings.sort.join(' and ')
|
126
|
-
else
|
127
|
-
filters
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
def create(attributes = {})
|
132
|
-
build(attributes).tap { |n| n.save }
|
133
|
-
end
|
134
|
-
|
135
|
-
def create!(attributes = {})
|
136
|
-
build(attributes).tap { |n| n.save! }
|
137
|
-
end
|
138
|
-
|
139
|
-
def build(attributes = {})
|
140
|
-
ClassBuilder
|
141
|
-
.get_namespaced_class(type.member_type.name)
|
142
|
-
.new(
|
143
|
-
graph: graph,
|
144
|
-
resource_name: @resource_name,
|
145
|
-
parent: self,
|
146
|
-
attributes: attributes,
|
147
|
-
).tap { |new_member|
|
148
|
-
@internal_values << new_member
|
149
|
-
}
|
150
|
-
end
|
151
|
-
|
152
|
-
def <<(new_member)
|
153
|
-
super
|
154
|
-
new_member.graph = graph
|
155
|
-
new_member.parent = self
|
156
|
-
new_member.save
|
157
|
-
self
|
158
|
-
end
|
159
|
-
|
160
|
-
def each(start = 0)
|
161
|
-
return to_enum(:each, start) unless block_given?
|
162
|
-
@next_link
|
163
|
-
Array(@internal_values[start..-1]).each do |element|
|
164
|
-
yield(element)
|
165
|
-
end
|
166
|
-
|
167
|
-
unless last?
|
168
|
-
start = [@internal_values.size, start].max
|
169
|
-
|
170
|
-
fetch_next_page
|
171
|
-
|
172
|
-
each(start, &Proc.new)
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
def collection?
|
177
|
-
true
|
178
|
-
end
|
179
|
-
|
180
|
-
def reload!
|
181
|
-
@next_link = query_path
|
182
|
-
@internal_values = []
|
183
|
-
fetch_next_page
|
184
|
-
end
|
185
|
-
|
186
|
-
private
|
187
|
-
|
188
|
-
def last?
|
189
|
-
@loaded || @internal_values.size >= 1000
|
190
|
-
end
|
191
|
-
|
192
|
-
def fetch_next_page
|
193
|
-
@next_link ||= query_path
|
194
|
-
result = begin
|
195
|
-
@graph.service.get(@next_link)
|
196
|
-
rescue OData::ClientError => e
|
197
|
-
if matches = /Unsupported sort property '([^']*)'/.match(e.message)
|
198
|
-
raise MicrosoftGraph::TypeError.new("Cannot sort by #{matches[1]}")
|
199
|
-
elsif /OrderBy not supported/.match(e.message)
|
200
|
-
if @order_by.length == 1
|
201
|
-
raise MicrosoftGraph::TypeError.new("Cannot sort by #{@order_by.first}")
|
202
|
-
else
|
203
|
-
raise MicrosoftGraph::TypeError.new("Cannot sort by at least one field requested")
|
204
|
-
end
|
205
|
-
else
|
206
|
-
raise e
|
207
|
-
end
|
208
|
-
end
|
209
|
-
@next_link = result[:attributes]['@odata.next_link']
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
end
|
1
|
+
class MicrosoftGraph
|
2
|
+
class CollectionAssociation < Collection
|
3
|
+
attr_reader :type
|
4
|
+
attr_reader :parent
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
@type = options[:type]
|
8
|
+
@graph = options[:graph]
|
9
|
+
@resource_name = options[:resource_name]
|
10
|
+
@parent = options[:parent]
|
11
|
+
@order_by = options[:order_by]
|
12
|
+
@filter = options[:filter]
|
13
|
+
@dirty = false
|
14
|
+
@loaded = false
|
15
|
+
@internal_values = []
|
16
|
+
|
17
|
+
raise MicrosoftGraph::TypeError.new("A collection cannot be both ordered and filtered.") if @order_by && @filter
|
18
|
+
@order_by && @order_by.each do |field|
|
19
|
+
field_name, direction = field.to_s.split(' ')
|
20
|
+
field_names = field_name.split("/")
|
21
|
+
unless valid_nested_property(field_names, @type)
|
22
|
+
raise MicrosoftGraph::TypeError.new("Tried to order by invalid field: #{field_name}")
|
23
|
+
end
|
24
|
+
if direction && ! %w(asc desc).include?(direction)
|
25
|
+
raise MicrosoftGraph::TypeError.new("Tried to order field #{field_name} by invalid direction: #{direction}")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
@filter && @filter.respond_to?(:keys) && @filter.keys.each do |field_name|
|
29
|
+
unless @type.properties.map(&:name).include? OData.convert_to_camel_case(field_name)
|
30
|
+
raise MicrosoftGraph::TypeError.new("Tried to filter by invalid field: #{field_name}")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def find(id)
|
37
|
+
if response = graph.service.get("#{path}/#{URI.escape(id.to_s)}")
|
38
|
+
klass = if member_type = specified_member_type(response)
|
39
|
+
ClassBuilder.get_namespaced_class(response)
|
40
|
+
else
|
41
|
+
default_member_class
|
42
|
+
end
|
43
|
+
klass.new(
|
44
|
+
graph: graph,
|
45
|
+
parent: self,
|
46
|
+
attributes: response[:attributes],
|
47
|
+
persisted: true
|
48
|
+
)
|
49
|
+
else
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def path
|
55
|
+
if parent && parent.path
|
56
|
+
"#{parent.path}/#{@resource_name}"
|
57
|
+
else
|
58
|
+
@resource_name
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def query_path
|
63
|
+
if @order_by
|
64
|
+
order_by_names = @order_by.map do |field|
|
65
|
+
URI.escape OData.convert_to_camel_case(field)
|
66
|
+
end
|
67
|
+
"#{path}?$orderby=#{order_by_names.join(',')}"
|
68
|
+
elsif @filter
|
69
|
+
escaped_filters = URI.escape(stringify_filters(@filter))
|
70
|
+
"#{path}?$filter=#{escaped_filters}"
|
71
|
+
else
|
72
|
+
path
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def order_by(*fields)
|
77
|
+
self.class.new(
|
78
|
+
type: @type,
|
79
|
+
graph: @graph,
|
80
|
+
resource_name: @resource_name,
|
81
|
+
parent: @parent,
|
82
|
+
order_by: fields,
|
83
|
+
filter: @filter
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
def valid_nested_property(field_names, parent_type)
|
88
|
+
field_name = field_names.shift
|
89
|
+
if field_name
|
90
|
+
property = parent_type.properties.find do |prop|
|
91
|
+
OData.convert_to_camel_case(prop.name) == OData.convert_to_camel_case(field_name)
|
92
|
+
end
|
93
|
+
property && valid_nested_property(field_names, property.type)
|
94
|
+
else
|
95
|
+
true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def filter(new_filters)
|
100
|
+
self.class.new(
|
101
|
+
type: @type,
|
102
|
+
graph: @graph,
|
103
|
+
resource_name: @resource_name,
|
104
|
+
parent: @parent,
|
105
|
+
order_by: @order_by,
|
106
|
+
filter: merge_with_existing_filter(new_filters)
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
def merge_with_existing_filter(new_filters)
|
111
|
+
existing_filter = @filter || {}
|
112
|
+
if new_filters.respond_to?(:keys) && existing_filter.respond_to?(:keys)
|
113
|
+
existing_filter.merge(new_filters)
|
114
|
+
else
|
115
|
+
[stringify_filters(new_filters), stringify_filters(@filter)].compact.join(" and ")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def stringify_filters(filters)
|
120
|
+
if filters.respond_to?(:keys)
|
121
|
+
filter_strings = filters.map do |k,v|
|
122
|
+
v = v.is_a?(String) ? "'#{v}'" : v
|
123
|
+
"#{OData.convert_to_camel_case(k)} eq #{v}"
|
124
|
+
end
|
125
|
+
filter_strings.sort.join(' and ')
|
126
|
+
else
|
127
|
+
filters
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def create(attributes = {})
|
132
|
+
build(attributes).tap { |n| n.save }
|
133
|
+
end
|
134
|
+
|
135
|
+
def create!(attributes = {})
|
136
|
+
build(attributes).tap { |n| n.save! }
|
137
|
+
end
|
138
|
+
|
139
|
+
def build(attributes = {})
|
140
|
+
ClassBuilder
|
141
|
+
.get_namespaced_class(type.member_type.name)
|
142
|
+
.new(
|
143
|
+
graph: graph,
|
144
|
+
resource_name: @resource_name,
|
145
|
+
parent: self,
|
146
|
+
attributes: attributes,
|
147
|
+
).tap { |new_member|
|
148
|
+
@internal_values << new_member
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
def <<(new_member)
|
153
|
+
super
|
154
|
+
new_member.graph = graph
|
155
|
+
new_member.parent = self
|
156
|
+
new_member.save
|
157
|
+
self
|
158
|
+
end
|
159
|
+
|
160
|
+
def each(start = 0)
|
161
|
+
return to_enum(:each, start) unless block_given?
|
162
|
+
@next_link ||= query_path
|
163
|
+
Array(@internal_values[start..-1]).each do |element|
|
164
|
+
yield(element)
|
165
|
+
end
|
166
|
+
|
167
|
+
unless last?
|
168
|
+
start = [@internal_values.size, start].max
|
169
|
+
|
170
|
+
fetch_next_page
|
171
|
+
|
172
|
+
each(start, &Proc.new)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def collection?
|
177
|
+
true
|
178
|
+
end
|
179
|
+
|
180
|
+
def reload!
|
181
|
+
@next_link = query_path
|
182
|
+
@internal_values = []
|
183
|
+
fetch_next_page
|
184
|
+
end
|
185
|
+
|
186
|
+
private
|
187
|
+
|
188
|
+
def last?
|
189
|
+
@loaded || @internal_values.size >= 1000
|
190
|
+
end
|
191
|
+
|
192
|
+
def fetch_next_page
|
193
|
+
@next_link ||= query_path
|
194
|
+
result = begin
|
195
|
+
@graph.service.get(@next_link)
|
196
|
+
rescue OData::ClientError => e
|
197
|
+
if matches = /Unsupported sort property '([^']*)'/.match(e.message)
|
198
|
+
raise MicrosoftGraph::TypeError.new("Cannot sort by #{matches[1]}")
|
199
|
+
elsif /OrderBy not supported/.match(e.message)
|
200
|
+
if @order_by.length == 1
|
201
|
+
raise MicrosoftGraph::TypeError.new("Cannot sort by #{@order_by.first}")
|
202
|
+
else
|
203
|
+
raise MicrosoftGraph::TypeError.new("Cannot sort by at least one field requested")
|
204
|
+
end
|
205
|
+
else
|
206
|
+
raise e
|
207
|
+
end
|
208
|
+
end
|
209
|
+
@next_link = result[:attributes]['@odata.next_link']
|
210
|
+
@next_link.sub!(MicrosoftGraph::BASE_URL, "") if @next_link
|
211
|
+
|
212
|
+
result[:attributes]['value'].each do |entity_hash|
|
213
|
+
klass =
|
214
|
+
if member_type = specified_member_type(entity_hash)
|
215
|
+
ClassBuilder.get_namespaced_class(member_type.name)
|
216
|
+
else
|
217
|
+
default_member_class
|
218
|
+
end
|
219
|
+
@internal_values.push klass.new(attributes: entity_hash, parent: self, persisted: true)
|
220
|
+
end
|
221
|
+
@loaded = @next_link.nil?
|
222
|
+
end
|
223
|
+
|
224
|
+
def default_member_class
|
225
|
+
ClassBuilder.get_namespaced_class(type.member_type.name)
|
226
|
+
end
|
227
|
+
|
228
|
+
def specified_member_type(entity_hash)
|
229
|
+
@graph.service.get_type_for_odata_response(entity_hash)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|