odata4 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +2 -0
  3. data/.gitignore +24 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +75 -0
  8. data/CHANGELOG.md +120 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE.txt +23 -0
  11. data/README.md +287 -0
  12. data/Rakefile +7 -0
  13. data/TODO.md +55 -0
  14. data/lib/odata4.rb +37 -0
  15. data/lib/odata4/complex_type.rb +76 -0
  16. data/lib/odata4/complex_type/property.rb +114 -0
  17. data/lib/odata4/entity.rb +319 -0
  18. data/lib/odata4/entity_set.rb +162 -0
  19. data/lib/odata4/enum_type.rb +95 -0
  20. data/lib/odata4/enum_type/property.rb +62 -0
  21. data/lib/odata4/navigation_property.rb +29 -0
  22. data/lib/odata4/navigation_property/proxy.rb +76 -0
  23. data/lib/odata4/properties.rb +25 -0
  24. data/lib/odata4/properties/binary.rb +50 -0
  25. data/lib/odata4/properties/boolean.rb +37 -0
  26. data/lib/odata4/properties/date.rb +27 -0
  27. data/lib/odata4/properties/date_time.rb +83 -0
  28. data/lib/odata4/properties/date_time_offset.rb +17 -0
  29. data/lib/odata4/properties/decimal.rb +50 -0
  30. data/lib/odata4/properties/float.rb +67 -0
  31. data/lib/odata4/properties/geography.rb +13 -0
  32. data/lib/odata4/properties/geography/base.rb +162 -0
  33. data/lib/odata4/properties/geography/line_string.rb +33 -0
  34. data/lib/odata4/properties/geography/point.rb +31 -0
  35. data/lib/odata4/properties/geography/polygon.rb +38 -0
  36. data/lib/odata4/properties/guid.rb +17 -0
  37. data/lib/odata4/properties/integer.rb +107 -0
  38. data/lib/odata4/properties/number.rb +14 -0
  39. data/lib/odata4/properties/string.rb +72 -0
  40. data/lib/odata4/properties/time.rb +40 -0
  41. data/lib/odata4/properties/time_of_day.rb +27 -0
  42. data/lib/odata4/property.rb +118 -0
  43. data/lib/odata4/property_registry.rb +41 -0
  44. data/lib/odata4/query.rb +231 -0
  45. data/lib/odata4/query/criteria.rb +92 -0
  46. data/lib/odata4/query/criteria/comparison_operators.rb +49 -0
  47. data/lib/odata4/query/criteria/date_functions.rb +61 -0
  48. data/lib/odata4/query/criteria/geography_functions.rb +21 -0
  49. data/lib/odata4/query/criteria/lambda_operators.rb +27 -0
  50. data/lib/odata4/query/criteria/string_functions.rb +40 -0
  51. data/lib/odata4/query/in_batches.rb +58 -0
  52. data/lib/odata4/query/result.rb +84 -0
  53. data/lib/odata4/query/result/atom.rb +41 -0
  54. data/lib/odata4/query/result/json.rb +42 -0
  55. data/lib/odata4/railtie.rb +19 -0
  56. data/lib/odata4/service.rb +344 -0
  57. data/lib/odata4/service_registry.rb +52 -0
  58. data/lib/odata4/version.rb +3 -0
  59. data/odata4.gemspec +34 -0
  60. data/spec/fixtures/files/entity_to_xml.xml +17 -0
  61. data/spec/fixtures/files/metadata.xml +150 -0
  62. data/spec/fixtures/files/product_0.json +10 -0
  63. data/spec/fixtures/files/product_0.xml +28 -0
  64. data/spec/fixtures/files/products.json +106 -0
  65. data/spec/fixtures/files/products.xml +308 -0
  66. data/spec/fixtures/files/supplier_0.json +26 -0
  67. data/spec/fixtures/files/supplier_0.xml +32 -0
  68. data/spec/fixtures/vcr_cassettes/complex_type_specs.yml +127 -0
  69. data/spec/fixtures/vcr_cassettes/entity_set_specs.yml +1348 -0
  70. data/spec/fixtures/vcr_cassettes/entity_set_specs/bad_entry.yml +183 -0
  71. data/spec/fixtures/vcr_cassettes/entity_set_specs/existing_entry.yml +256 -0
  72. data/spec/fixtures/vcr_cassettes/entity_set_specs/new_entry.yml +185 -0
  73. data/spec/fixtures/vcr_cassettes/entity_specs.yml +285 -0
  74. data/spec/fixtures/vcr_cassettes/navigation_property_proxy_specs.yml +346 -0
  75. data/spec/fixtures/vcr_cassettes/query/result_specs.yml +189 -0
  76. data/spec/fixtures/vcr_cassettes/query_specs.yml +663 -0
  77. data/spec/fixtures/vcr_cassettes/service_registry_specs.yml +129 -0
  78. data/spec/fixtures/vcr_cassettes/service_specs.yml +127 -0
  79. data/spec/fixtures/vcr_cassettes/usage_example_specs.yml +749 -0
  80. data/spec/odata4/complex_type_spec.rb +116 -0
  81. data/spec/odata4/entity/shared_examples.rb +82 -0
  82. data/spec/odata4/entity_set_spec.rb +168 -0
  83. data/spec/odata4/entity_spec.rb +151 -0
  84. data/spec/odata4/enum_type_spec.rb +134 -0
  85. data/spec/odata4/navigation_property/proxy_spec.rb +44 -0
  86. data/spec/odata4/navigation_property_spec.rb +55 -0
  87. data/spec/odata4/properties/binary_spec.rb +50 -0
  88. data/spec/odata4/properties/boolean_spec.rb +72 -0
  89. data/spec/odata4/properties/date_spec.rb +23 -0
  90. data/spec/odata4/properties/date_time_offset_spec.rb +30 -0
  91. data/spec/odata4/properties/date_time_spec.rb +23 -0
  92. data/spec/odata4/properties/decimal_spec.rb +24 -0
  93. data/spec/odata4/properties/float_spec.rb +45 -0
  94. data/spec/odata4/properties/geography/line_string_spec.rb +33 -0
  95. data/spec/odata4/properties/geography/point_spec.rb +29 -0
  96. data/spec/odata4/properties/geography/polygon_spec.rb +55 -0
  97. data/spec/odata4/properties/geography/shared_examples.rb +72 -0
  98. data/spec/odata4/properties/guid_spec.rb +17 -0
  99. data/spec/odata4/properties/integer_spec.rb +58 -0
  100. data/spec/odata4/properties/string_spec.rb +46 -0
  101. data/spec/odata4/properties/time_of_day_spec.rb +23 -0
  102. data/spec/odata4/properties/time_spec.rb +15 -0
  103. data/spec/odata4/property_registry_spec.rb +16 -0
  104. data/spec/odata4/property_spec.rb +32 -0
  105. data/spec/odata4/query/criteria_spec.rb +229 -0
  106. data/spec/odata4/query/result_spec.rb +53 -0
  107. data/spec/odata4/query_spec.rb +196 -0
  108. data/spec/odata4/service_registry_spec.rb +18 -0
  109. data/spec/odata4/service_spec.rb +80 -0
  110. data/spec/odata4/usage_example_spec.rb +176 -0
  111. data/spec/spec_helper.rb +32 -0
  112. data/spec/support/coverage.rb +2 -0
  113. data/spec/support/vcr.rb +9 -0
  114. metadata +380 -0
@@ -0,0 +1,41 @@
1
+ require 'singleton'
2
+
3
+ module OData4
4
+ # Provides a registry for keeping track of various property types used by
5
+ # OData4.
6
+ class PropertyRegistry
7
+ include Singleton
8
+
9
+ # Add a property type to the registry
10
+ #
11
+ # @param type_name [String] property type name to register
12
+ # @param klass [Class] Ruby class to use for the specified type
13
+ def add(type_name, klass)
14
+ properties[type_name] = klass
15
+ end
16
+
17
+ # Lookup a property by name and get the Ruby class to use for its instances
18
+ #
19
+ # @param type_name [String] the type name to lookup
20
+ # @return [Class, nil] the proper class or nil
21
+ def [](type_name)
22
+ properties[type_name]
23
+ end
24
+
25
+ # (see #add)
26
+ def self.add(type_name, klass)
27
+ OData4::PropertyRegistry.instance.add(type_name, klass)
28
+ end
29
+
30
+ # (see #[])
31
+ def self.[](type_name)
32
+ OData4::PropertyRegistry.instance[type_name]
33
+ end
34
+
35
+ private
36
+
37
+ def properties
38
+ @properties ||= {}
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,231 @@
1
+ module OData4
2
+ # OData4::Query provides the query interface for requesting Entities matching
3
+ # specific criteria from an OData4::EntitySet. This class should not be
4
+ # instantiated directly, but can be. Normally you will access a Query by
5
+ # first asking for one from the OData4::EntitySet you want to query.
6
+ class Query
7
+ attr_reader :options
8
+
9
+ include InBatches
10
+
11
+ # Create a new Query for the provided EntitySet
12
+ # @param entity_set [OData4::EntitySet]
13
+ # @param options [Hash] Query options
14
+ def initialize(entity_set, options = {})
15
+ @entity_set = entity_set
16
+ @options = options
17
+ setup_empty_criteria_set
18
+ end
19
+
20
+ # Instantiates an OData4::Query::Criteria for the named property.
21
+ # @param property [to_s]
22
+ def [](property)
23
+ property_instance = @entity_set.new_entity.get_property(property)
24
+ property_instance = property if property_instance.nil?
25
+ OData4::Query::Criteria.new(property: property_instance)
26
+ end
27
+
28
+ # Find the Entity with the supplied key value.
29
+ # @param key [to_s] primary key to lookup
30
+ # @return [OData4::Entity,nil]
31
+ def find(key)
32
+ entity = @entity_set.new_entity
33
+ key_property = entity.get_property(entity.primary_key)
34
+ key_property.value = key
35
+
36
+ pathname = "#{entity_set.name}(#{key_property.url_value})"
37
+ query = [pathname, assemble_criteria].compact.join('?')
38
+ execute(query).first
39
+ end
40
+
41
+ # Adds a filter criteria to the query.
42
+ # For filter syntax see https://msdn.microsoft.com/en-us/library/gg309461.aspx
43
+ # Syntax:
44
+ # Property Operator Value
45
+ #
46
+ # For example:
47
+ # Name eq 'Customer Service'
48
+ #
49
+ # Operators:
50
+ # eq, ne, gt, ge, lt, le, and, or, not
51
+ #
52
+ # Value
53
+ # can be 'null', can use single quotes
54
+ # @param criteria
55
+ def where(criteria)
56
+ criteria_set[:filter] << criteria
57
+ self
58
+ end
59
+
60
+ # Adds a fulltext search term to the query
61
+ # NOTE: May not be implemented by the service
62
+ # @param term [String]
63
+ def search(term)
64
+ criteria_set[:search] << term
65
+ self
66
+ end
67
+
68
+ # Adds a filter criteria to the query with 'and' logical operator.
69
+ # @param criteria
70
+ #def and(criteria)
71
+ #
72
+ #end
73
+
74
+ # Adds a filter criteria to the query with 'or' logical operator.
75
+ # @param criteria
76
+ #def or(criteria)
77
+ #
78
+ #end
79
+
80
+ # Specify properties to order the result by.
81
+ # Can use 'desc' like 'Name desc'
82
+ # @param properties [Array<Symbol>]
83
+ # @return [self]
84
+ def order_by(*properties)
85
+ criteria_set[:orderby] += properties
86
+ self
87
+ end
88
+
89
+ # Specify associations to expand in the result.
90
+ # @param associations [Array<Symbol>]
91
+ # @return [self]
92
+ def expand(*associations)
93
+ criteria_set[:expand] += associations
94
+ self
95
+ end
96
+
97
+ # Specify properties to select within the result.
98
+ # @param properties [Array<Symbol>]
99
+ # @return [self]
100
+ def select(*properties)
101
+ criteria_set[:select] += properties
102
+ self
103
+ end
104
+
105
+ # Add skip criteria to query.
106
+ # @param value [to_i]
107
+ # @return [self]
108
+ def skip(value)
109
+ criteria_set[:skip] = value.to_i
110
+ self
111
+ end
112
+
113
+ # Add limit criteria to query.
114
+ # @param value [to_i]
115
+ # @return [self]
116
+ def limit(value)
117
+ criteria_set[:top] = value.to_i
118
+ self
119
+ end
120
+
121
+ # Add inline count criteria to query.
122
+ # Not Supported in CRM2011
123
+ # @return [self]
124
+ def include_count
125
+ criteria_set[:inline_count] = true
126
+ self
127
+ end
128
+
129
+ # Convert Query to string.
130
+ # @return [String]
131
+ def to_s
132
+ [entity_set.name, assemble_criteria].compact.join('?')
133
+ end
134
+
135
+ # Execute the query.
136
+ # @return [OData4::Query::Result]
137
+ def execute(query = self.to_s)
138
+ response = service.execute(query, options)
139
+ OData4::Query::Result.new(self, response)
140
+ end
141
+
142
+ # Executes the query to get a count of entities.
143
+ # @return [Integer]
144
+ def count
145
+ url_chunk = ["#{entity_set.name}/$count", assemble_criteria].compact.join('?')
146
+ response = service.execute(url_chunk)
147
+ # Some servers (*cough* Microsoft *cough*) seem to
148
+ # return extraneous characters in the response.
149
+ response.body.scan(/\d+/).first.to_i
150
+ end
151
+
152
+ # Checks whether a query will return any results by calling #count
153
+ # @return [Boolean]
154
+ def empty?
155
+ self.count == 0
156
+ end
157
+
158
+ # The EntitySet for this query.
159
+ # @return [OData4::EntitySet]
160
+ # @api private
161
+ def entity_set
162
+ @entity_set
163
+ end
164
+
165
+ # The service for this query
166
+ # @return [OData4::Service]
167
+ # @api private
168
+ def service
169
+ @service ||= entity_set.service
170
+ end
171
+
172
+ private
173
+
174
+ def criteria_set
175
+ @criteria_set
176
+ end
177
+
178
+ def setup_empty_criteria_set
179
+ @criteria_set = {
180
+ filter: [],
181
+ search: [],
182
+ select: [],
183
+ expand: [],
184
+ orderby: [],
185
+ skip: 0,
186
+ top: 0,
187
+ inline_count: false
188
+ }
189
+ end
190
+
191
+ def assemble_criteria
192
+ criteria = [
193
+ filter_criteria,
194
+ search_criteria,
195
+ list_criteria(:orderby),
196
+ list_criteria(:expand),
197
+ list_criteria(:select),
198
+ inline_count_criteria,
199
+ paging_criteria(:skip),
200
+ paging_criteria(:top)
201
+ ].compact!
202
+
203
+ criteria.empty? ? nil : criteria.join('&')
204
+ end
205
+
206
+ def filter_criteria
207
+ return nil if criteria_set[:filter].empty?
208
+ filters = criteria_set[:filter].collect(&:to_s)
209
+ "$filter=#{filters.join(' and ')}"
210
+ end
211
+
212
+ def search_criteria
213
+ return nil if criteria_set[:search].empty?
214
+ filters = criteria_set[:search].collect(&:to_s)
215
+ "$search=#{filters.join(' AND ')}"
216
+ end
217
+
218
+ def list_criteria(name)
219
+ criteria_set[name].empty? ? nil : "$#{name}=#{criteria_set[name].join(',')}"
220
+ end
221
+
222
+ # inlinecount not supported by Microsoft CRM 2011
223
+ def inline_count_criteria
224
+ criteria_set[:inline_count] ? '$count=true' : nil
225
+ end
226
+
227
+ def paging_criteria(name)
228
+ criteria_set[name] == 0 ? nil : "$#{name}=#{criteria_set[name]}"
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,92 @@
1
+ require 'odata4/query/criteria/comparison_operators'
2
+ require 'odata4/query/criteria/string_functions'
3
+ require 'odata4/query/criteria/date_functions'
4
+ require 'odata4/query/criteria/geography_functions'
5
+ require 'odata4/query/criteria/lambda_operators'
6
+
7
+ module OData4
8
+ class Query
9
+ # Represents a discreet criteria within an OData4::Query. Should not,
10
+ # normally, be instantiated directly.
11
+ class Criteria
12
+ # The property name that is the target of the criteria.
13
+ attr_reader :property
14
+ # The operator of the criteria.
15
+ attr_reader :operator
16
+ # The value of the criteria.
17
+ attr_reader :value
18
+ # A function to apply to the property.
19
+ attr_reader :function
20
+ # An optional argument to the function.
21
+ attr_reader :argument
22
+
23
+ # Initializes a new criteria with provided options.
24
+ # @param options [Hash]
25
+ def initialize(options = {})
26
+ @property = options[:property]
27
+ @operator = options[:operator]
28
+ @function = options[:function]
29
+ @argument = options[:argument]
30
+ @value = options[:value]
31
+ end
32
+
33
+ include ComparisonOperators
34
+ include StringFunctions
35
+ include DateFunctions
36
+ include GeographyFunctions
37
+ include LambdaOperators
38
+
39
+ # Returns criteria as query-ready string.
40
+ def to_s
41
+ query = function ? function_expression : property_name
42
+
43
+ if operator && !lambda_operator?
44
+ "#{query} #{operator} #{url_value(value)}"
45
+ else
46
+ query
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def property_name
53
+ property.name
54
+ rescue NoMethodError
55
+ property.to_s
56
+ end
57
+
58
+ def function_expression
59
+ return lambda_expression if lambda_operator?
60
+
61
+ if argument
62
+ "#{function}(#{property_name},#{url_value(argument)})"
63
+ else
64
+ "#{function}(#{property_name})"
65
+ end
66
+ end
67
+
68
+ def lambda_expression
69
+ "#{property_name}/#{function}(d:d/#{argument} #{operator} #{url_value(value)})"
70
+ end
71
+
72
+ def url_value(value)
73
+ property.value = value
74
+ property.url_value
75
+ rescue
76
+ value
77
+ end
78
+
79
+ def set_operator_and_value(operator, value)
80
+ @operator = operator
81
+ @value = value
82
+ self
83
+ end
84
+
85
+ def set_function_and_argument(function, argument)
86
+ @function = function
87
+ @argument = argument
88
+ self
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,49 @@
1
+ module OData4
2
+ class Query
3
+ class Criteria
4
+ module ComparisonOperators
5
+ # Sets up equality operator.
6
+ # @param value [to_s]
7
+ # @return [self]
8
+ def eq(value)
9
+ set_operator_and_value(:eq, value)
10
+ end
11
+
12
+ # Sets up non-equality operator.
13
+ # @param value [to_s]
14
+ # @return [self]
15
+ def ne(value)
16
+ set_operator_and_value(:ne, value)
17
+ end
18
+
19
+ # Sets up greater-than operator.
20
+ # @param value [to_s]
21
+ # @return [self]
22
+ def gt(value)
23
+ set_operator_and_value(:gt, value)
24
+ end
25
+
26
+ # Sets up greater-than-or-equal operator.
27
+ # @param value [to_s]
28
+ # @return [self]
29
+ def ge(value)
30
+ set_operator_and_value(:ge, value)
31
+ end
32
+
33
+ # Sets up less-than operator.
34
+ # @param value [to_s]
35
+ # @return [self]
36
+ def lt(value)
37
+ set_operator_and_value(:lt, value)
38
+ end
39
+
40
+ # Sets up less-than-or-equal operator.
41
+ # @param value [to_s]
42
+ # @return [self]
43
+ def le(value)
44
+ set_operator_and_value(:le, value)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,61 @@
1
+ module OData4
2
+ class Query
3
+ class Criteria
4
+ module DateFunctions
5
+ # Applies the `year` function.
6
+ # @return [self]
7
+ def year
8
+ set_function_and_argument(:year, nil)
9
+ end
10
+
11
+ # Applies the `month` function.
12
+ # @return [self]
13
+ def month
14
+ set_function_and_argument(:month, nil)
15
+ end
16
+
17
+ # Applies the `day` function.
18
+ # @return [self]
19
+ def day
20
+ set_function_and_argument(:day, nil)
21
+ end
22
+
23
+ # Applies the `hour` function.
24
+ # @return [self]
25
+ def hour
26
+ set_function_and_argument(:hour, nil)
27
+ end
28
+
29
+ # Applies the `minute` function.
30
+ # @return [self]
31
+ def minute
32
+ set_function_and_argument(:minute, nil)
33
+ end
34
+
35
+ # Applies the `second` function.
36
+ # @return [self]
37
+ def second
38
+ set_function_and_argument(:second, nil)
39
+ end
40
+
41
+ # Applies the `fractionalseconds` function.
42
+ # @return [self]
43
+ def fractionalseconds
44
+ set_function_and_argument(:fractionalseconds, nil)
45
+ end
46
+
47
+ # Applies the `date` function.
48
+ # @return [self]
49
+ def date
50
+ set_function_and_argument(:date, nil)
51
+ end
52
+
53
+ # Applies the `time` function.
54
+ # @return [self]
55
+ def time
56
+ set_function_and_argument(:time, nil)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end