frodo 0.10.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.
- checksums.yaml +7 -0
- data/.autotest +2 -0
- data/.circleci/config.yml +54 -0
- data/.gitignore +24 -0
- data/.gitlab-ci.yml +9 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +75 -0
- data/CHANGELOG.md +163 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +23 -0
- data/README.md +479 -0
- data/Rakefile +7 -0
- data/TODO.md +55 -0
- data/frodo.gemspec +39 -0
- data/images/frodo.jpg +0 -0
- data/lib/frodo/abstract_client.rb +11 -0
- data/lib/frodo/client.rb +6 -0
- data/lib/frodo/concerns/api.rb +292 -0
- data/lib/frodo/concerns/authentication.rb +32 -0
- data/lib/frodo/concerns/base.rb +84 -0
- data/lib/frodo/concerns/caching.rb +26 -0
- data/lib/frodo/concerns/connection.rb +79 -0
- data/lib/frodo/concerns/verbs.rb +68 -0
- data/lib/frodo/config.rb +143 -0
- data/lib/frodo/entity.rb +335 -0
- data/lib/frodo/entity_container.rb +75 -0
- data/lib/frodo/entity_set.rb +131 -0
- data/lib/frodo/errors.rb +70 -0
- data/lib/frodo/middleware/authentication/token.rb +13 -0
- data/lib/frodo/middleware/authentication.rb +87 -0
- data/lib/frodo/middleware/authorization.rb +18 -0
- data/lib/frodo/middleware/caching.rb +30 -0
- data/lib/frodo/middleware/custom_headers.rb +14 -0
- data/lib/frodo/middleware/gzip.rb +33 -0
- data/lib/frodo/middleware/instance_url.rb +20 -0
- data/lib/frodo/middleware/logger.rb +42 -0
- data/lib/frodo/middleware/multipart.rb +64 -0
- data/lib/frodo/middleware/odata_headers.rb +13 -0
- data/lib/frodo/middleware/raise_error.rb +47 -0
- data/lib/frodo/middleware.rb +33 -0
- data/lib/frodo/navigation_property/proxy.rb +80 -0
- data/lib/frodo/navigation_property.rb +29 -0
- data/lib/frodo/properties/binary.rb +50 -0
- data/lib/frodo/properties/boolean.rb +37 -0
- data/lib/frodo/properties/collection.rb +50 -0
- data/lib/frodo/properties/complex.rb +114 -0
- data/lib/frodo/properties/date.rb +27 -0
- data/lib/frodo/properties/date_time.rb +83 -0
- data/lib/frodo/properties/date_time_offset.rb +17 -0
- data/lib/frodo/properties/decimal.rb +54 -0
- data/lib/frodo/properties/enum.rb +62 -0
- data/lib/frodo/properties/float.rb +67 -0
- data/lib/frodo/properties/geography/base.rb +162 -0
- data/lib/frodo/properties/geography/line_string.rb +33 -0
- data/lib/frodo/properties/geography/point.rb +31 -0
- data/lib/frodo/properties/geography/polygon.rb +38 -0
- data/lib/frodo/properties/geography.rb +13 -0
- data/lib/frodo/properties/guid.rb +17 -0
- data/lib/frodo/properties/integer.rb +107 -0
- data/lib/frodo/properties/number.rb +14 -0
- data/lib/frodo/properties/string.rb +72 -0
- data/lib/frodo/properties/time.rb +40 -0
- data/lib/frodo/properties/time_of_day.rb +27 -0
- data/lib/frodo/properties.rb +32 -0
- data/lib/frodo/property.rb +139 -0
- data/lib/frodo/property_registry.rb +41 -0
- data/lib/frodo/query/criteria/comparison_operators.rb +49 -0
- data/lib/frodo/query/criteria/date_functions.rb +61 -0
- data/lib/frodo/query/criteria/geography_functions.rb +21 -0
- data/lib/frodo/query/criteria/lambda_operators.rb +27 -0
- data/lib/frodo/query/criteria/string_functions.rb +40 -0
- data/lib/frodo/query/criteria.rb +92 -0
- data/lib/frodo/query/in_batches.rb +58 -0
- data/lib/frodo/query.rb +221 -0
- data/lib/frodo/railtie.rb +19 -0
- data/lib/frodo/schema/complex_type.rb +79 -0
- data/lib/frodo/schema/enum_type.rb +95 -0
- data/lib/frodo/schema.rb +164 -0
- data/lib/frodo/service.rb +199 -0
- data/lib/frodo/service_registry.rb +52 -0
- data/lib/frodo/version.rb +3 -0
- data/lib/frodo.rb +67 -0
- data/spec/fixtures/auth_success_response.json +11 -0
- data/spec/fixtures/error.json +11 -0
- data/spec/fixtures/files/entity_to_xml.xml +18 -0
- data/spec/fixtures/files/error.xml +5 -0
- data/spec/fixtures/files/metadata.xml +150 -0
- data/spec/fixtures/files/metadata_with_error.xml +157 -0
- data/spec/fixtures/files/product_0.json +10 -0
- data/spec/fixtures/files/product_0.xml +28 -0
- data/spec/fixtures/files/products.json +106 -0
- data/spec/fixtures/files/products.xml +308 -0
- data/spec/fixtures/files/supplier_0.json +26 -0
- data/spec/fixtures/files/supplier_0.xml +32 -0
- data/spec/fixtures/leads.json +923 -0
- data/spec/fixtures/refresh_error_response.json +8 -0
- data/spec/frodo/abstract_client_spec.rb +13 -0
- data/spec/frodo/client_spec.rb +57 -0
- data/spec/frodo/concerns/authentication_spec.rb +79 -0
- data/spec/frodo/concerns/base_spec.rb +68 -0
- data/spec/frodo/concerns/caching_spec.rb +40 -0
- data/spec/frodo/concerns/connection_spec.rb +65 -0
- data/spec/frodo/config_spec.rb +127 -0
- data/spec/frodo/entity/shared_examples.rb +83 -0
- data/spec/frodo/entity_container_spec.rb +38 -0
- data/spec/frodo/entity_set_spec.rb +169 -0
- data/spec/frodo/entity_spec.rb +153 -0
- data/spec/frodo/errors_spec.rb +48 -0
- data/spec/frodo/middleware/authentication/token_spec.rb +87 -0
- data/spec/frodo/middleware/authentication_spec.rb +83 -0
- data/spec/frodo/middleware/authorization_spec.rb +17 -0
- data/spec/frodo/middleware/custom_headers_spec.rb +21 -0
- data/spec/frodo/middleware/gzip_spec.rb +68 -0
- data/spec/frodo/middleware/instance_url_spec.rb +27 -0
- data/spec/frodo/middleware/logger_spec.rb +21 -0
- data/spec/frodo/middleware/odata_headers_spec.rb +15 -0
- data/spec/frodo/middleware/raise_error_spec.rb +66 -0
- data/spec/frodo/navigation_property/proxy_spec.rb +46 -0
- data/spec/frodo/navigation_property_spec.rb +55 -0
- data/spec/frodo/properties/binary_spec.rb +50 -0
- data/spec/frodo/properties/boolean_spec.rb +72 -0
- data/spec/frodo/properties/collection_spec.rb +44 -0
- data/spec/frodo/properties/date_spec.rb +23 -0
- data/spec/frodo/properties/date_time_offset_spec.rb +30 -0
- data/spec/frodo/properties/date_time_spec.rb +23 -0
- data/spec/frodo/properties/decimal_spec.rb +50 -0
- data/spec/frodo/properties/float_spec.rb +45 -0
- data/spec/frodo/properties/geography/line_string_spec.rb +33 -0
- data/spec/frodo/properties/geography/point_spec.rb +29 -0
- data/spec/frodo/properties/geography/polygon_spec.rb +55 -0
- data/spec/frodo/properties/geography/shared_examples.rb +72 -0
- data/spec/frodo/properties/guid_spec.rb +17 -0
- data/spec/frodo/properties/integer_spec.rb +58 -0
- data/spec/frodo/properties/string_spec.rb +46 -0
- data/spec/frodo/properties/time_of_day_spec.rb +23 -0
- data/spec/frodo/properties/time_spec.rb +15 -0
- data/spec/frodo/property_registry_spec.rb +16 -0
- data/spec/frodo/property_spec.rb +71 -0
- data/spec/frodo/query/criteria_spec.rb +229 -0
- data/spec/frodo/query_spec.rb +156 -0
- data/spec/frodo/schema/complex_type_spec.rb +97 -0
- data/spec/frodo/schema/enum_type_spec.rb +112 -0
- data/spec/frodo/schema_spec.rb +113 -0
- data/spec/frodo/service_registry_spec.rb +19 -0
- data/spec/frodo/service_spec.rb +153 -0
- data/spec/frodo/usage_example_spec.rb +161 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/support/coverage.rb +2 -0
- data/spec/support/fixture_helpers.rb +14 -0
- data/spec/support/middleware.rb +19 -0
- metadata +479 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Frodo
|
|
2
|
+
class Query
|
|
3
|
+
class Criteria
|
|
4
|
+
module StringFunctions
|
|
5
|
+
# Sets up a `contains` function criterium.
|
|
6
|
+
# @param str [to_s]
|
|
7
|
+
# @return [self]
|
|
8
|
+
def contains(str)
|
|
9
|
+
set_function_and_argument(:contains, str)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Sets up a `startswith` function criterium.
|
|
13
|
+
# @param str [to_s]
|
|
14
|
+
# @return [self]
|
|
15
|
+
def startswith(str)
|
|
16
|
+
set_function_and_argument(:startswith, str)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Sets up a `endswith` function criterium.
|
|
20
|
+
# @param str [to_s]
|
|
21
|
+
# @return [self]
|
|
22
|
+
def endswith(str)
|
|
23
|
+
set_function_and_argument(:endswith, str)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Applies the `tolower` function to the property.
|
|
27
|
+
# @return [self]
|
|
28
|
+
def tolower
|
|
29
|
+
set_function_and_argument(:tolower, nil)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Applies the `toupper` function to the property.
|
|
33
|
+
# @return [self]
|
|
34
|
+
def toupper
|
|
35
|
+
set_function_and_argument(:toupper, nil)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require 'frodo/query/criteria/comparison_operators'
|
|
2
|
+
require 'frodo/query/criteria/string_functions'
|
|
3
|
+
require 'frodo/query/criteria/date_functions'
|
|
4
|
+
require 'frodo/query/criteria/geography_functions'
|
|
5
|
+
require 'frodo/query/criteria/lambda_operators'
|
|
6
|
+
|
|
7
|
+
module Frodo
|
|
8
|
+
class Query
|
|
9
|
+
# Represents a discreet criteria within an Frodo::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,58 @@
|
|
|
1
|
+
module Frodo
|
|
2
|
+
class Query
|
|
3
|
+
module InBatches
|
|
4
|
+
DEFAULT_BATCH_SIZE = 10
|
|
5
|
+
|
|
6
|
+
# Process results in batches.
|
|
7
|
+
#
|
|
8
|
+
# When a block is given, yields `Frodo::Query::Result`
|
|
9
|
+
# objects of specified batch size to the block.
|
|
10
|
+
#
|
|
11
|
+
# service['Products'].query.in_batches(of: 10) do |batch|
|
|
12
|
+
# batch.count # batch size (10 except for last batch)
|
|
13
|
+
# batch.is_a? Frodo::Query::Result # true
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# Returns an Enumerator to process results individually.
|
|
17
|
+
#
|
|
18
|
+
# service['Products'].query.in_batches.each do |entity|
|
|
19
|
+
# entity.is_a? Frodo::Entity # true
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# @param of: [int] batch size
|
|
23
|
+
# @return [Enumerator]
|
|
24
|
+
def in_batches(of: DEFAULT_BATCH_SIZE, &block)
|
|
25
|
+
per_page = of
|
|
26
|
+
|
|
27
|
+
if block_given?
|
|
28
|
+
each_batch(of, &block)
|
|
29
|
+
else
|
|
30
|
+
Enumerator.new do |result|
|
|
31
|
+
each_batch(of) do |batch|
|
|
32
|
+
batch.each { |entity| result << entity }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def each_batch(per_page, &block)
|
|
41
|
+
page = 0
|
|
42
|
+
|
|
43
|
+
loop do
|
|
44
|
+
batch = get_paginated_entities(per_page, page)
|
|
45
|
+
break if batch.empty?
|
|
46
|
+
|
|
47
|
+
yield batch
|
|
48
|
+
|
|
49
|
+
page += 1
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def get_paginated_entities(per_page, page)
|
|
54
|
+
skip(per_page * page).limit(per_page).execute
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
data/lib/frodo/query.rb
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
require 'frodo/query/criteria'
|
|
2
|
+
require 'frodo/query/in_batches'
|
|
3
|
+
|
|
4
|
+
module Frodo
|
|
5
|
+
# Frodo::Query provides the query interface for requesting Entities matching
|
|
6
|
+
# specific criteria from an Frodo::EntitySet. This class should not be
|
|
7
|
+
# instantiated directly, but can be. Normally you will access a Query by
|
|
8
|
+
# first asking for one from the Frodo::EntitySet you want to query.
|
|
9
|
+
class Query
|
|
10
|
+
attr_reader :options
|
|
11
|
+
|
|
12
|
+
# Create a new Query for the provided EntitySet
|
|
13
|
+
# @param entity_set [Frodo::EntitySet]
|
|
14
|
+
# @param options [Hash] Query options
|
|
15
|
+
def initialize(entity_set, options = {})
|
|
16
|
+
@entity_set = entity_set
|
|
17
|
+
@options = options
|
|
18
|
+
setup_empty_criteria_set
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Instantiates an Frodo::Query::Criteria for the named property.
|
|
22
|
+
# @param property [to_s]
|
|
23
|
+
def [](property)
|
|
24
|
+
property_instance = @entity_set.new_entity.get_property(property)
|
|
25
|
+
property_instance = property if property_instance.nil?
|
|
26
|
+
Frodo::Query::Criteria.new(property: property_instance)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Build a query to find an entity with the supplied key value.
|
|
30
|
+
# @param key [to_s] primary key to lookup
|
|
31
|
+
# @return the path and querystring [String]
|
|
32
|
+
def find(key)
|
|
33
|
+
entity = @entity_set.new_entity
|
|
34
|
+
key_property = entity.get_property(entity.primary_key)
|
|
35
|
+
key_property.value = key
|
|
36
|
+
|
|
37
|
+
pathname = "#{entity_set.name}(#{key_property.url_value})"
|
|
38
|
+
|
|
39
|
+
select_criteria = if list_criteria(:select)
|
|
40
|
+
list_criteria(:select).map { |k, v| "#{k}=#{v}" }.join('&')
|
|
41
|
+
end
|
|
42
|
+
[pathname, select_criteria].compact.join('?')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Adds a filter criteria to the query.
|
|
46
|
+
# For filter syntax see https://msdn.microsoft.com/en-us/library/gg309461.aspx
|
|
47
|
+
# Syntax:
|
|
48
|
+
# Property Operator Value
|
|
49
|
+
#
|
|
50
|
+
# For example:
|
|
51
|
+
# Name eq 'Customer Service'
|
|
52
|
+
#
|
|
53
|
+
# Operators:
|
|
54
|
+
# eq, ne, gt, ge, lt, le, and, or, not
|
|
55
|
+
#
|
|
56
|
+
# Value
|
|
57
|
+
# can be 'null', can use single quotes
|
|
58
|
+
# @param criteria
|
|
59
|
+
def where(criteria)
|
|
60
|
+
criteria_set[:filter] << criteria
|
|
61
|
+
self
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Adds a fulltext search term to the query
|
|
65
|
+
# NOTE: May not be implemented by the service
|
|
66
|
+
# @param term [String]
|
|
67
|
+
def search(term)
|
|
68
|
+
criteria_set[:search] << term
|
|
69
|
+
self
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Adds a filter criteria to the query with 'and' logical operator.
|
|
73
|
+
# @param criteria
|
|
74
|
+
#def and(criteria)
|
|
75
|
+
#
|
|
76
|
+
#end
|
|
77
|
+
|
|
78
|
+
# Adds a filter criteria to the query with 'or' logical operator.
|
|
79
|
+
# @param criteria
|
|
80
|
+
#def or(criteria)
|
|
81
|
+
#
|
|
82
|
+
#end
|
|
83
|
+
|
|
84
|
+
# Specify properties to order the result by.
|
|
85
|
+
# Can use 'desc' like 'Name desc'
|
|
86
|
+
# @param properties [Array<Symbol>]
|
|
87
|
+
# @return [self]
|
|
88
|
+
def order_by(*properties)
|
|
89
|
+
criteria_set[:orderby] += properties
|
|
90
|
+
self
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Specify associations to expand in the result.
|
|
94
|
+
# @param associations [Array<Symbol>]
|
|
95
|
+
# @return [self]
|
|
96
|
+
def expand(*associations)
|
|
97
|
+
criteria_set[:expand] += associations
|
|
98
|
+
self
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Specify properties to select within the result.
|
|
102
|
+
# @param properties [Array<Symbol>]
|
|
103
|
+
# @return [self]
|
|
104
|
+
def select(*properties)
|
|
105
|
+
criteria_set[:select] += properties
|
|
106
|
+
self
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Add skip criteria to query.
|
|
110
|
+
# @param value [to_i]
|
|
111
|
+
# @return [self]
|
|
112
|
+
def skip(value)
|
|
113
|
+
criteria_set[:skip] = value.to_i
|
|
114
|
+
self
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Add limit criteria to query.
|
|
118
|
+
# @param value [to_i]
|
|
119
|
+
# @return [self]
|
|
120
|
+
def limit(value)
|
|
121
|
+
criteria_set[:top] = value.to_i
|
|
122
|
+
self
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Add inline count criteria to query.
|
|
126
|
+
# Not Supported in CRM2011
|
|
127
|
+
# @return [self]
|
|
128
|
+
def include_count
|
|
129
|
+
criteria_set[:inline_count] = true
|
|
130
|
+
self
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Convert Query to string.
|
|
134
|
+
# @return [String]
|
|
135
|
+
def to_s
|
|
136
|
+
criteria = params.map { |k, v| "#{k}=#{v}" }.join('&')
|
|
137
|
+
[entity_set.name, params.any? ? criteria : nil].compact.join('?')
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Build a query to count of an entity set
|
|
141
|
+
# @return [Integer]
|
|
142
|
+
def count
|
|
143
|
+
"#{entity_set.name}/$count"
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# The EntitySet for this query.
|
|
147
|
+
# @return [Frodo::EntitySet]
|
|
148
|
+
# @api private
|
|
149
|
+
def entity_set
|
|
150
|
+
@entity_set
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# The parameter hash for this query.
|
|
154
|
+
# @return [Hash] Params hash
|
|
155
|
+
def params
|
|
156
|
+
assemble_criteria || {}
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# The service for this query
|
|
160
|
+
# @return [Frodo::Service]
|
|
161
|
+
# @api private
|
|
162
|
+
def service
|
|
163
|
+
@service ||= entity_set.service
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
private
|
|
167
|
+
|
|
168
|
+
attr_reader :criteria_set
|
|
169
|
+
|
|
170
|
+
def setup_empty_criteria_set
|
|
171
|
+
@criteria_set = {
|
|
172
|
+
filter: [],
|
|
173
|
+
search: [],
|
|
174
|
+
select: [],
|
|
175
|
+
expand: [],
|
|
176
|
+
orderby: [],
|
|
177
|
+
skip: 0,
|
|
178
|
+
top: 0,
|
|
179
|
+
inline_count: false
|
|
180
|
+
}
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def assemble_criteria
|
|
184
|
+
[
|
|
185
|
+
filter_criteria,
|
|
186
|
+
search_criteria,
|
|
187
|
+
list_criteria(:orderby),
|
|
188
|
+
list_criteria(:expand),
|
|
189
|
+
list_criteria(:select),
|
|
190
|
+
inline_count_criteria,
|
|
191
|
+
paging_criteria(:skip),
|
|
192
|
+
paging_criteria(:top)
|
|
193
|
+
].compact.reduce(&:merge)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def filter_criteria
|
|
197
|
+
return nil if criteria_set[:filter].empty?
|
|
198
|
+
filters = criteria_set[:filter].collect(&:to_s)
|
|
199
|
+
{ '$filter' => filters.join(' and ') }
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def search_criteria
|
|
203
|
+
return nil if criteria_set[:search].empty?
|
|
204
|
+
filters = criteria_set[:search].collect(&:to_s)
|
|
205
|
+
{ '$search' => filters.join(' AND ') }
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def list_criteria(name)
|
|
209
|
+
return nil if criteria_set[name].empty?
|
|
210
|
+
{ "$#{name}" => criteria_set[name].join(',') }
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def inline_count_criteria
|
|
214
|
+
criteria_set[:inline_count] ? { '$count' => 'true' } : nil
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def paging_criteria(name)
|
|
218
|
+
criteria_set[name] == 0 ? nil : { "$#{name}" => criteria_set[name] }
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Frodo
|
|
2
|
+
class Railtie < Rails::Railtie
|
|
3
|
+
config.before_initialize do
|
|
4
|
+
::Frodo::Railtie.load_configuration!
|
|
5
|
+
::Frodo::Railtie.setup_service_registry!
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# Looks for config/odata.yml and loads the configuration.
|
|
9
|
+
def self.load_configuration!
|
|
10
|
+
# TODO Implement Rails configuration loading
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Examines the loaded configuration and populates the
|
|
14
|
+
# Frodo::ServiceRegistry accordingly.
|
|
15
|
+
def self.setup_service_registry!
|
|
16
|
+
# TODO Populate Frodo::ServiceRegistry based on configuration
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module Frodo
|
|
2
|
+
class Schema
|
|
3
|
+
# ComplexTypes are used in Frodo to either encapsulate richer data types for
|
|
4
|
+
# use as Entity properties. ComplexTypes are composed of properties the same
|
|
5
|
+
# way that Entities are and, so, the interface for working with the various
|
|
6
|
+
# properties of a ComplexType mimics that of Entities.
|
|
7
|
+
class ComplexType
|
|
8
|
+
# Creates a new ComplexType based on the supplied options.
|
|
9
|
+
# @param type_xml [Nokogiri::XML::Element]
|
|
10
|
+
# @param service [Frodo::Service]
|
|
11
|
+
def initialize(type_definition, schema)
|
|
12
|
+
@type_definition = type_definition
|
|
13
|
+
@schema = schema
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# The name of the ComplexType
|
|
17
|
+
# @return [String]
|
|
18
|
+
def name
|
|
19
|
+
@name ||= type_definition.attributes['Name'].value
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Returns the namespaced type for the ComplexType.
|
|
23
|
+
# @return [String]
|
|
24
|
+
def type
|
|
25
|
+
"#{namespace}.#{name}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns the namespace this ComplexType belongs to.
|
|
29
|
+
# @return [String]
|
|
30
|
+
def namespace
|
|
31
|
+
@namespace ||= service.namespace
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Returns this ComplexType's properties.
|
|
35
|
+
# @return [Hash<String, Frodo::Property>]
|
|
36
|
+
def properties
|
|
37
|
+
@properties ||= collect_properties
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Returns a list of this ComplexType's property names.
|
|
41
|
+
# @return [Array<String>]
|
|
42
|
+
def property_names
|
|
43
|
+
@property_names ||= properties.keys
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Returns the property class that implements this `ComplexType`.
|
|
47
|
+
# @return [Class < Frodo::Properties::Complex]
|
|
48
|
+
def property_class
|
|
49
|
+
@property_class ||= lambda { |type, complex_type|
|
|
50
|
+
klass = Class.new ::Frodo::Properties::Complex
|
|
51
|
+
klass.send(:define_method, :type) { type }
|
|
52
|
+
klass.send(:define_method, :complex_type) { complex_type }
|
|
53
|
+
klass
|
|
54
|
+
}.call(type, self)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def schema
|
|
60
|
+
@schema
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def service
|
|
64
|
+
@schema.service
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def type_definition
|
|
68
|
+
@type_definition
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def collect_properties
|
|
72
|
+
Hash[type_definition.xpath('./Property').map do |property_xml|
|
|
73
|
+
property_name, property = schema.send(:process_property_from_xml, property_xml)
|
|
74
|
+
[property_name, property]
|
|
75
|
+
end]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
module Frodo
|
|
2
|
+
class Schema
|
|
3
|
+
# Enumeration types are nominal types that represent a series of related values.
|
|
4
|
+
# Enumeration types expose these related values as members of the enumeration.
|
|
5
|
+
class EnumType
|
|
6
|
+
# Creates a new EnumType based on the supplied options.
|
|
7
|
+
# @param type_xml [Nokogiri::XML::Element]
|
|
8
|
+
# @param service [Frodo::Service]
|
|
9
|
+
# @return [self]
|
|
10
|
+
def initialize(type_definition, schema)
|
|
11
|
+
@type_definition = type_definition
|
|
12
|
+
@schema = schema
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# The name of the EnumType
|
|
16
|
+
# @return [String]
|
|
17
|
+
def name
|
|
18
|
+
options['Name']
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Returns the namespaced type for the EnumType.
|
|
22
|
+
# @return [String]
|
|
23
|
+
def type
|
|
24
|
+
"#{namespace}.#{name}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Whether this EnumType supports setting multiple values.
|
|
28
|
+
# @return [Boolean]
|
|
29
|
+
def is_flags?
|
|
30
|
+
options['IsFlags'] == 'true'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# The underlying type of this EnumType.
|
|
34
|
+
# @return [String]
|
|
35
|
+
def underlying_type
|
|
36
|
+
options['UnderlyingType'] || 'Edm.Int32'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Returns the namespace this EnumType belongs to.
|
|
40
|
+
# @return [String]
|
|
41
|
+
def namespace
|
|
42
|
+
@namespace ||= service.namespace
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Returns the members of this EnumType and their values.
|
|
46
|
+
# @return [Hash]
|
|
47
|
+
def members
|
|
48
|
+
@members ||= collect_members
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Returns the property class that implements this `EnumType`.
|
|
52
|
+
# @return [Class < Frodo::Properties::Enum]
|
|
53
|
+
def property_class
|
|
54
|
+
@property_class ||= lambda { |type, members, is_flags|
|
|
55
|
+
klass = Class.new ::Frodo::Properties::Enum
|
|
56
|
+
klass.send(:define_method, :type) { type }
|
|
57
|
+
klass.send(:define_method, :members) { members }
|
|
58
|
+
klass.send(:define_method, :is_flags?) { is_flags }
|
|
59
|
+
klass
|
|
60
|
+
}.call(type, members, is_flags?)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Returns the value of the requested member.
|
|
64
|
+
# @param member_name [to_s]
|
|
65
|
+
# @return [*]
|
|
66
|
+
def [](member_name)
|
|
67
|
+
members.invert[member_name.to_s]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def service
|
|
73
|
+
@schema.service
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def type_definition
|
|
77
|
+
@type_definition
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def options
|
|
81
|
+
@options = type_definition.attributes.map do |name, attr|
|
|
82
|
+
[name, attr.value]
|
|
83
|
+
end.to_h
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def collect_members
|
|
87
|
+
Hash[type_definition.xpath('./Member').map.with_index do |member_xml, index|
|
|
88
|
+
member_name = member_xml.attributes['Name'].value
|
|
89
|
+
member_value = member_xml.attributes['Value'].andand.value.andand.to_i
|
|
90
|
+
[member_value || index, member_name]
|
|
91
|
+
end]
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|