softlayer_api 1.0.8 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|