softlayer_api 1.0.8 → 2.0.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 +4 -4
- data/README.textile +116 -66
- data/examples/{accountInformation.rb → account_info.rb} +6 -4
- data/examples/{createTicket.rb → create_ticket.rb} +6 -3
- data/examples/{openTickets.rb → open_tickets.rb} +6 -4
- data/examples/ticket_info.rb +8 -8
- data/lib/softlayer/APIParameterFilter.rb +131 -0
- data/lib/softlayer/Client.rb +100 -0
- data/lib/softlayer/Config.rb +77 -0
- data/lib/softlayer/ObjectFilter.rb +232 -0
- data/lib/softlayer/Service.rb +321 -0
- data/lib/softlayer/base.rb +11 -7
- data/lib/softlayer/object_mask_helpers.rb +60 -13
- data/lib/softlayer_api.rb +6 -1
- metadata +21 -17
- data/lib/softlayer/service.rb +0 -467
@@ -0,0 +1,131 @@
|
|
1
|
+
|
2
|
+
module SoftLayer
|
3
|
+
# An <tt>APIParameterFilter</tt> is an intermediary object that understands how
|
4
|
+
# to accept the other API parameter filters and carry their values to
|
5
|
+
# <tt>method_missing</tt> in <tt>Service</tt>. Instances of this class are created
|
6
|
+
# internally by the <tt>Service</tt> in its handling of a method call and you
|
7
|
+
# should not have to create instances of this class directly.
|
8
|
+
#
|
9
|
+
# Instead, to use an API filter, you add a filter method to the call
|
10
|
+
# chain when you call a method through a <tt>SoftLayer::Service</tt>
|
11
|
+
#
|
12
|
+
# For example, given a <tt>SoftLayer::Service</tt> instance called <tt>account_service</tt>
|
13
|
+
# you could take advantage of the API filter that identifies a particular
|
14
|
+
# object known to that service using the <tt>object_with_id</tt> method :
|
15
|
+
#
|
16
|
+
# account_service.object_with_id(91234).getSomeAttribute
|
17
|
+
#
|
18
|
+
# The invocation of <tt>object_with_id</tt> will cause an instance of this
|
19
|
+
# class to be created with the service as its target.
|
20
|
+
#
|
21
|
+
class APIParameterFilter
|
22
|
+
attr_reader :target
|
23
|
+
attr_reader :parameters
|
24
|
+
|
25
|
+
def initialize(target, starting_parameters = nil)
|
26
|
+
@target = target
|
27
|
+
@parameters = starting_parameters || {}
|
28
|
+
end
|
29
|
+
|
30
|
+
# Adds an API filter that narrows the scope of a call to an object with
|
31
|
+
# a particular ID. For example, if you want to get the ticket
|
32
|
+
# with an ID of 12345 from the ticket service you might use
|
33
|
+
#
|
34
|
+
# ticket_service.object_with_id(12345).getObject
|
35
|
+
def object_with_id(value)
|
36
|
+
# we create a new object in case the user wants to store off the
|
37
|
+
# filter chain and reuse it later
|
38
|
+
APIParameterFilter.new(self.target, @parameters.merge({ :server_object_id => value }))
|
39
|
+
end
|
40
|
+
|
41
|
+
# Use this as part of a method call chain to add an object mask to
|
42
|
+
# the request. The arguments to object mask should be well formed
|
43
|
+
# Extended Object Mask strings:
|
44
|
+
#
|
45
|
+
# ticket_service.object_mask(
|
46
|
+
# "mask[createDate, modifyDate]",
|
47
|
+
# "mask(SoftLayer_Some_Type).aProperty").getObject
|
48
|
+
#
|
49
|
+
# The object_mask becomes part of the request sent to the server
|
50
|
+
#
|
51
|
+
def object_mask(*args)
|
52
|
+
raise ArgumentError, "object_mask expects well-formatted root object mask strings" if args.empty? || (1 == args.count && !args[0])
|
53
|
+
raise ArgumentError, "object_mask expects well-formatted root object mask strings" if args.find { |arg| !(arg.kind_of?(String)) }
|
54
|
+
|
55
|
+
object_mask = (@parameters[:object_mask] || []) + args
|
56
|
+
|
57
|
+
# we create a new object in case the user wants to store off the
|
58
|
+
# filter chain and reuse it later
|
59
|
+
APIParameterFilter.new(self.target, @parameters.merge({ :object_mask => object_mask }));
|
60
|
+
end
|
61
|
+
|
62
|
+
# Adds a result limit which helps you page through a long list of entities
|
63
|
+
#
|
64
|
+
# The offset is the index of the first item you wish to have returned
|
65
|
+
# The limit describes how many items you wish the call to return.
|
66
|
+
#
|
67
|
+
# For example, if you wanted to get five open tickets from the account
|
68
|
+
# starting with the tenth item in the open tickets list you might call
|
69
|
+
#
|
70
|
+
# account_service.result_limit(10, 5).getOpenTickets
|
71
|
+
def result_limit(offset, limit)
|
72
|
+
# we create a new object in case the user wants to store off the
|
73
|
+
# filter chain and reuse it later
|
74
|
+
APIParameterFilter.new(self.target, @parameters.merge({ :result_offset => offset, :result_limit => limit }))
|
75
|
+
end
|
76
|
+
|
77
|
+
# Adds an object_filter to the result. An Object Filter allows you
|
78
|
+
# to specify criteria which are used to filter the results returned
|
79
|
+
# by the server.
|
80
|
+
def object_filter(filter)
|
81
|
+
raise ArgumentError, "Object mask expects mask properties" if filter.nil?
|
82
|
+
|
83
|
+
# we create a new object in case the user wants to store off the
|
84
|
+
# filter chain and reuse it later
|
85
|
+
APIParameterFilter.new(self.target, @parameters.merge({:object_filter => filter}));
|
86
|
+
end
|
87
|
+
|
88
|
+
# A utility method that returns the server object ID (if any) stored
|
89
|
+
# in this parameter set.
|
90
|
+
def server_object_id
|
91
|
+
self.parameters[:server_object_id]
|
92
|
+
end
|
93
|
+
|
94
|
+
# a utility method that returns the object mask (if any) stored
|
95
|
+
# in this parameter set.
|
96
|
+
def server_object_mask
|
97
|
+
self.parameters[:object_mask]
|
98
|
+
end
|
99
|
+
|
100
|
+
# a utility method that returns the starting index of the result limit (if any) stored
|
101
|
+
# in this parameter set.
|
102
|
+
def server_result_limit
|
103
|
+
self.parameters[:result_limit]
|
104
|
+
end
|
105
|
+
|
106
|
+
# a utility method that returns the starting index of the result limit offset (if any) stored
|
107
|
+
# in this parameter set.
|
108
|
+
def server_result_offset
|
109
|
+
self.parameters[:result_offset]
|
110
|
+
end
|
111
|
+
|
112
|
+
def server_object_filter
|
113
|
+
self.parameters[:object_filter]
|
114
|
+
end
|
115
|
+
|
116
|
+
# This allows the filters to be used at the end of a long chain of calls that ends
|
117
|
+
# at a service.
|
118
|
+
def method_missing(method_name, *args, &block)
|
119
|
+
puts "SoftLayer::APIParameterFilter#method_missing called #{method_name}, #{args.inspect}" if $DEBUG
|
120
|
+
|
121
|
+
if(!block && method_name.to_s.match(/[[:alnum:]]+/))
|
122
|
+
result = @target.call_softlayer_api_with_params(method_name, self, args)
|
123
|
+
else
|
124
|
+
result = super
|
125
|
+
end
|
126
|
+
|
127
|
+
result
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end # module SoftLayer
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module SoftLayer
|
2
|
+
# Initialize an instance of the Client class. You pass in the service name
|
3
|
+
# and optionally hash arguments specifying how the client should access the
|
4
|
+
# SoftLayer API.
|
5
|
+
#
|
6
|
+
# The following symbols can be used as hash arguments to pass options to the constructor:
|
7
|
+
# - <tt>:username</tt> - a non-empty string providing the username to use for requests to the service
|
8
|
+
# - <tt>:api_key</tt> - a non-empty string providing the api key to use for requests to the service
|
9
|
+
# - <tt>:endpoint_url</tt> - a non-empty string providing the endpoint URL to use for requests to the service
|
10
|
+
#
|
11
|
+
# If any of the options above are missing then the constructor will try to use the corresponding
|
12
|
+
# global variable declared in the SoftLayer Module:
|
13
|
+
# - <tt>$SL_API_USERNAME</tt>
|
14
|
+
# - <tt>$SL_API_KEY</tt>
|
15
|
+
# - <tt>$SL_API_BASE_URL</tt>
|
16
|
+
#
|
17
|
+
class Client
|
18
|
+
# A username passed as authentication for each request. Cannot be emtpy or nil.
|
19
|
+
attr_reader :username
|
20
|
+
|
21
|
+
# An API key passed as part of the authentication of each request. Cannot be emtpy or nil.
|
22
|
+
attr_reader :api_key
|
23
|
+
|
24
|
+
# The base URL for requests that are passed to the server. Cannot be emtpy or nil.
|
25
|
+
attr_reader :endpoint_url
|
26
|
+
|
27
|
+
# A string passsed as the value for the User-Agent header when requests are sent to SoftLayer API.
|
28
|
+
attr_accessor :user_agent
|
29
|
+
|
30
|
+
def initialize(options = {})
|
31
|
+
@services = { }
|
32
|
+
|
33
|
+
settings = Config.client_settings(options)
|
34
|
+
|
35
|
+
# pick up the username from the options, the global, or assume no username
|
36
|
+
@username = settings[:username] || ""
|
37
|
+
|
38
|
+
# do a similar thing for the api key
|
39
|
+
@api_key = settings[:api_key] || ""
|
40
|
+
|
41
|
+
# and the endpoint url
|
42
|
+
@endpoint_url = settings[:endpoint_url] || API_PUBLIC_ENDPOINT
|
43
|
+
|
44
|
+
@user_agent = settings[:user_agent] || "softlayer_api gem/#{SoftLayer::VERSION} (Ruby #{RUBY_PLATFORM}/#{RUBY_VERSION})"
|
45
|
+
|
46
|
+
raise "A SoftLayer Client requires a username" if !@username || @username.empty?
|
47
|
+
raise "A SoftLayer Client requires an api_key" if !@api_key || @api_key.empty?
|
48
|
+
raise "A SoftLayer Clietn requires an enpoint URL" if !@endpoint_url || @endpoint_url.empty?
|
49
|
+
end
|
50
|
+
|
51
|
+
# return a hash of the authentication headers for the client
|
52
|
+
def authentication_headers
|
53
|
+
{
|
54
|
+
"authenticate" => {
|
55
|
+
"username" => @username,
|
56
|
+
"apiKey" => @api_key
|
57
|
+
}
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns a service with the given name.
|
62
|
+
#
|
63
|
+
# If a service has already been created by this client that same service
|
64
|
+
# will be returned each time it is called for by name. Otherwise the system
|
65
|
+
# will try to construct a new service object and return that.
|
66
|
+
#
|
67
|
+
#
|
68
|
+
# If the service has to be created then the service_options will be passed
|
69
|
+
# along to the creative function. However, when returning a previously created
|
70
|
+
# Service, the service_options will be ignored.
|
71
|
+
#
|
72
|
+
# If the service_name provided does not start with 'SoftLayer__' that prefix
|
73
|
+
# will be added
|
74
|
+
def service_named(service_name, service_options = {})
|
75
|
+
raise ArgumentError,"Please provide a service name" if service_name.nil? || service_name.empty?
|
76
|
+
|
77
|
+
# strip whitespace from service_name and
|
78
|
+
# ensure that it start with "SoftLayer_".
|
79
|
+
#
|
80
|
+
# if it does not, then add it
|
81
|
+
service_name.strip!
|
82
|
+
if not service_name =~ /\ASoftLayer_/
|
83
|
+
service_name = "SoftLayer_#{service_name}"
|
84
|
+
end
|
85
|
+
|
86
|
+
# if we've already created this service, just return it
|
87
|
+
# otherwise create a new service
|
88
|
+
service_key = service_name.to_sym
|
89
|
+
if !@services.has_key?(service_key)
|
90
|
+
@services[service_key] = SoftLayer::Service.new(service_name, {:client => self}.merge(service_options))
|
91
|
+
end
|
92
|
+
|
93
|
+
@services[service_key]
|
94
|
+
end
|
95
|
+
|
96
|
+
def [](service_name)
|
97
|
+
service_named(service_name)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
|
23
|
+
require 'configparser'
|
24
|
+
|
25
|
+
module SoftLayer
|
26
|
+
class Config
|
27
|
+
def Config.globals_settings
|
28
|
+
result = {}
|
29
|
+
result[:username] = $SL_API_USERNAME if $SL_API_USERNAME
|
30
|
+
result[:api_key] = $SL_API_KEY if $SL_API_KEY
|
31
|
+
result[:endpoint_url] = $SL_API_BASE_URL ? $SL_API_BASE_URL : API_PUBLIC_ENDPOINT
|
32
|
+
result
|
33
|
+
end
|
34
|
+
|
35
|
+
def Config.environment_settings
|
36
|
+
result = {}
|
37
|
+
result[:username] = ENV["SL_USERNAME"] if ENV["SL_USERNAME"]
|
38
|
+
result[:api_key] = ENV["SL_API_KEY"] if ENV["SL_API_KEY"]
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
42
|
+
FILE_LOCATIONS = ['/etc/softlayer.conf', '~/.softlayer']
|
43
|
+
|
44
|
+
def Config.file_settings(*additional_files)
|
45
|
+
result = {}
|
46
|
+
|
47
|
+
search_path = FILE_LOCATIONS
|
48
|
+
search_path = search_path + additional_files if additional_files
|
49
|
+
search_path = search_path.map { |file_path| File.expand_path(file_path) }
|
50
|
+
|
51
|
+
search_path.each do |file_path|
|
52
|
+
|
53
|
+
if File.readable? file_path
|
54
|
+
config = ConfigParser.new file_path
|
55
|
+
softlayer_section = config["softlayer"]
|
56
|
+
|
57
|
+
if softlayer_section
|
58
|
+
result[:username] = softlayer_section['username'] if softlayer_section['username']
|
59
|
+
result[:endpoint_url] = softlayer_section['endpoint_url'] if softlayer_section['endpoint_url']
|
60
|
+
result[:api_key] = softlayer_section['api_key'] if softlayer_section['api_key']
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
result
|
66
|
+
end
|
67
|
+
|
68
|
+
def Config.client_settings(provided_settings = {})
|
69
|
+
settings = file_settings
|
70
|
+
settings.merge! environment_settings
|
71
|
+
settings.merge! globals_settings
|
72
|
+
settings.merge! provided_settings
|
73
|
+
|
74
|
+
settings
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
# of this software and associated documentation files (the "Software"), to deal
|
6
|
+
# in the Software without restriction, including without limitation the rights
|
7
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
# copies of the Software, and to permit persons to whom the Software is
|
9
|
+
# furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be included in
|
12
|
+
# all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
# THE SOFTWARE.
|
21
|
+
#
|
22
|
+
|
23
|
+
module SoftLayer
|
24
|
+
OBJECT_FILTER_OPERATORS = [
|
25
|
+
'*=', # Contains (ignoring case)
|
26
|
+
'^=', # Begins with (ignoring case)
|
27
|
+
'$=', # Ends with (ignoring_case)
|
28
|
+
'_=', # Matches (ignoring case)
|
29
|
+
'!=', # Is not Equal To (case sensitive)
|
30
|
+
'<=', # Less than or Equal To (case sensitive)
|
31
|
+
'>=', # Greater than or Equal To (case sensitive)
|
32
|
+
'<', # Less Than (case sensitive)
|
33
|
+
'>', # Greater Than (case sensitive)
|
34
|
+
'~', # Contains (case sensitive)
|
35
|
+
'!~' # Does not Contain (case sensitive)
|
36
|
+
]
|
37
|
+
|
38
|
+
# A class whose instances represent an Object Filter operation.
|
39
|
+
class ObjectFilterOperation
|
40
|
+
attr_reader :operator
|
41
|
+
attr_reader :value
|
42
|
+
|
43
|
+
def initialize(operator, value)
|
44
|
+
raise ArgumentException, "An unknown operator was given" if !OBJECT_FILTER_OPERATORS.include?(operator.strip)
|
45
|
+
raise ArgumentException, "Expected a value" if value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
46
|
+
|
47
|
+
@operator = operator.strip
|
48
|
+
@value = value.strip
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_h
|
52
|
+
{ 'operation' => "#{operator} #{value}"}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Routines that are valid within the block provided to a call to
|
57
|
+
# ObjectFilter.build.
|
58
|
+
class ObjectFilterBlockHandler
|
59
|
+
# contains wihout considering case
|
60
|
+
def contains(value)
|
61
|
+
ObjectFilterOperation.new('*=', value)
|
62
|
+
end
|
63
|
+
|
64
|
+
# case insensitive begins with
|
65
|
+
def begins_with(value)
|
66
|
+
ObjectFilterOperation.new('^=', value)
|
67
|
+
end
|
68
|
+
|
69
|
+
# case insensitive ends with
|
70
|
+
def ends_with(value)
|
71
|
+
ObjectFilterOperation.new('$=', value)
|
72
|
+
end
|
73
|
+
|
74
|
+
# matches exactly (ignoring case)
|
75
|
+
def is(value)
|
76
|
+
ObjectFilterOperation.new('_=', value)
|
77
|
+
end
|
78
|
+
|
79
|
+
def is_not(value)
|
80
|
+
ObjectFilterOperation.new('!=', value)
|
81
|
+
end
|
82
|
+
|
83
|
+
def is_greater_than(value)
|
84
|
+
ObjectFilterOperation.new('>', value)
|
85
|
+
end
|
86
|
+
|
87
|
+
def is_less_than(value)
|
88
|
+
ObjectFilterOperation.new('<', value)
|
89
|
+
end
|
90
|
+
|
91
|
+
def is_greater_or_equal_to(value)
|
92
|
+
ObjectFilterOperation.new('>=', value)
|
93
|
+
end
|
94
|
+
|
95
|
+
def is_less_or_equal_to(value)
|
96
|
+
ObjectFilterOperation.new('<=', value)
|
97
|
+
end
|
98
|
+
|
99
|
+
def contains_exactly(value)
|
100
|
+
ObjectFilterOperation.new('~', value)
|
101
|
+
end
|
102
|
+
|
103
|
+
def does_not_contain(value)
|
104
|
+
ObjectFilterOperation.new('!~', value)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# An ObjectFilter is a hash that, when asked to provide
|
109
|
+
# an value for an unknown key, will create a sub element
|
110
|
+
# at that key that is itself an object filter. So if you
|
111
|
+
# start with an empty object filter and ask for <tt>object_filter["foo"]</tt>
|
112
|
+
# then foo will be +added+ to the object and the value of that
|
113
|
+
# key will be an Object Filter <tt>{ "foo" => {} }</tt>
|
114
|
+
#
|
115
|
+
# This allows you to create object filters by chaining [] calls:
|
116
|
+
# object_filter["foo"]["bar"]["baz"] = 3 yields {"foo" => { "bar" => {"baz" => 3}}}
|
117
|
+
#
|
118
|
+
class ObjectFilter < Hash
|
119
|
+
# The default initialize for a hash is overridden
|
120
|
+
# so that object filters create sub-filters when asked
|
121
|
+
# for missing keys.
|
122
|
+
def initialize
|
123
|
+
super do |hash, key|
|
124
|
+
hash[key] = ObjectFilter.new
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Builds an object filter with the given key path, a dot separated list of property keys.
|
129
|
+
# The filter itself can be provided as a query string (in the query parameter)
|
130
|
+
# or by providing a block that calls routines in the ObjectFilterBlockHandler class.
|
131
|
+
def self.build(key_path, query = nil, &block)
|
132
|
+
raise ArgumentError, "The key path to build cannot be empty" if !key_path
|
133
|
+
|
134
|
+
# Split the keypath into its constituent parts and notify the user
|
135
|
+
# if there are no parts
|
136
|
+
keys = key_path.split('.')
|
137
|
+
raise ArgumentError, "The key path to build cannot be empty" if keys.empty?
|
138
|
+
|
139
|
+
# This will be the result of the build
|
140
|
+
result = ObjectFilter.new
|
141
|
+
|
142
|
+
# chase down the key path to the last-but-one key
|
143
|
+
current_level = result
|
144
|
+
while keys.count > 1
|
145
|
+
current_level = current_level[keys.shift]
|
146
|
+
end
|
147
|
+
|
148
|
+
# if there is a block, then the query will come from
|
149
|
+
# calling the block. We warn in debug mode if you override a
|
150
|
+
# query that was passed directly with the value from a block.
|
151
|
+
if block
|
152
|
+
$stderr.puts "The query from the block passed to ObjectFilter:build will override the query passed as a parameter" if $DEBUG && query
|
153
|
+
block_handler = ObjectFilterBlockHandler.new
|
154
|
+
query = block_handler.instance_eval(&block)
|
155
|
+
end
|
156
|
+
|
157
|
+
# If we have a query, we assign it's value to the last key
|
158
|
+
# otherwise, we build an emtpy filter at the bottom
|
159
|
+
if query
|
160
|
+
case
|
161
|
+
when query.kind_of?(Numeric)
|
162
|
+
current_level[keys.shift] = { 'operation' => query }
|
163
|
+
when query.kind_of?(SoftLayer::ObjectFilterOperation)
|
164
|
+
current_level[keys.shift] = query.to_h
|
165
|
+
when query.kind_of?(String)
|
166
|
+
current_level[keys.shift] = query_to_filter_operation(query)
|
167
|
+
when query.kind_of?(Hash)
|
168
|
+
current_level[keys.shift] = query
|
169
|
+
else
|
170
|
+
current_level[keys.shift]
|
171
|
+
end
|
172
|
+
else
|
173
|
+
current_level[keys.shift]
|
174
|
+
end
|
175
|
+
|
176
|
+
result
|
177
|
+
end
|
178
|
+
|
179
|
+
# This method tries to simplify creating a correct object filter structure
|
180
|
+
# by allowing the caller to provide a string in a simple query language.
|
181
|
+
# It then translates that string into an Object Filter operation structure
|
182
|
+
#
|
183
|
+
# Object Filter comparisons are done using operators. Some operators make
|
184
|
+
# case sensitive comparisons and some do not. The general form of an Object
|
185
|
+
# Filter operation is an operator follwed by the value used in the comparison.
|
186
|
+
# e.g.
|
187
|
+
# "*= smaug"
|
188
|
+
#
|
189
|
+
# The query language also accepts some aliases using asterisks
|
190
|
+
# in a regular-expression-like way. Those aliases look like:
|
191
|
+
#
|
192
|
+
# 'value' Exact value match (translates to '_= value')
|
193
|
+
# 'value*' Begins with value (translates to '^= value')
|
194
|
+
# '*value' Ends with value (translates to '$= value')
|
195
|
+
# '*value*' Contains value (translates to '*= value')
|
196
|
+
#
|
197
|
+
def self.query_to_filter_operation(query)
|
198
|
+
if query.kind_of? String then
|
199
|
+
query.strip!
|
200
|
+
|
201
|
+
begin
|
202
|
+
return { 'operation' => Integer(query) }
|
203
|
+
rescue
|
204
|
+
end
|
205
|
+
|
206
|
+
operator = OBJECT_FILTER_OPERATORS.find do | operator_string |
|
207
|
+
query[0 ... operator_string.length] == operator_string
|
208
|
+
end
|
209
|
+
|
210
|
+
if operator then
|
211
|
+
operation = "#{operator} #{query[operator.length..-1].strip}"
|
212
|
+
else
|
213
|
+
case query
|
214
|
+
when /\A\*(.*)\*\Z/
|
215
|
+
operation = "*= #{$1}"
|
216
|
+
when /\A\*(.*)/
|
217
|
+
operation = "$= #{$1}"
|
218
|
+
when /\A(.*)\*\Z/
|
219
|
+
operation = "^= #{$1}"
|
220
|
+
else
|
221
|
+
operation = "_= #{query}"
|
222
|
+
end #case
|
223
|
+
end #if
|
224
|
+
else
|
225
|
+
operation = query.to_i
|
226
|
+
end # query is string
|
227
|
+
|
228
|
+
{ 'operation' => operation }
|
229
|
+
end # query_to_filter_operation
|
230
|
+
|
231
|
+
end # ObjectFilter
|
232
|
+
end # SoftLayer
|