odata4 0.7.0

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 (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