delphix 0.1.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.
@@ -0,0 +1,68 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'securerandom'
21
+
22
+ module Delphix
23
+ module Utils
24
+ # Return the date and time in "HTTP-date" format as defined by RFC 7231.
25
+ #
26
+ # @return [Date,Time] in "HTTP-date" format
27
+ def utc_httpdate
28
+ Time.now.utc.httpdate
29
+ end
30
+
31
+ # Generates a uniq UUID, this is then used to identify this session.
32
+ #
33
+ # @return [String]
34
+ # Memoize a uniq UUID used to identify this session.
35
+ #
36
+ def request_id
37
+ @uuid ||= SecureRandom.uuid
38
+ end
39
+ end
40
+ end
41
+
42
+ class Hash
43
+ # Returns a new Hash, recursively downcasing and converting all
44
+ # keys to symbols.
45
+ #
46
+ # @return [Hash]
47
+ #
48
+ def recursively_normalize_keys
49
+ recursively_transform_keys { |key| key.downcase.to_sym rescue key }
50
+ end
51
+
52
+ private # P R O P R I E T À P R I V A T A divieto di accesso
53
+
54
+ # support methods for recursively transforming nested hashes and arrays
55
+ #
56
+ def _recursively_transform_keys_in_object(object, &block)
57
+ case object
58
+ when Hash
59
+ object.each_with_object({}) do |(key, val), result|
60
+ result[yield(key)] = _recursively_transform_keys_in_object(val, &block)
61
+ end
62
+ when Array
63
+ object.map { |e| _recursively_transform_keys_in_object(e, &block) }
64
+ else
65
+ object
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ # The version number of the Delphix Gem
21
+ #
22
+ # @return [String]
23
+ #
24
+ # @api public
25
+ module Delphix
26
+ # Contains information about this gem's version
27
+ module Version
28
+ MAJOR = 0
29
+ MINOR = 1
30
+ PATCH = 0
31
+
32
+ # Returns a version string by joining MAJOR, MINOR, and PATCH with '.'
33
+ #
34
+ # @example
35
+ # Version.string # => '1.0.1'
36
+ #
37
+ def self.string
38
+ [MAJOR, MINOR, PATCH].join('.')
39
+ end
40
+ end
41
+
42
+ VERSION = Delphix::Version.string
43
+ end
@@ -0,0 +1,164 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ module Delphix
21
+ class WebClient
22
+ def self.request(method, url, headers, body, timeout, &callback)
23
+ request = Delphix::WebRequest.new(method, url, headers, body)
24
+
25
+ if callback
26
+ Thread.new do
27
+ callback.call(internal_request(request, timeout))
28
+ end
29
+ else
30
+ internal_request(request, timeout)
31
+ end
32
+ end
33
+
34
+ def self.internal_request(request, timeout)
35
+ HTTP_HEADERS.each { |key, value| request.add_header(key, value) }
36
+ response = nil
37
+
38
+ begin
39
+ case request.method
40
+ when :get
41
+ response = RestClient::Request.execute(
42
+ method: :get,
43
+ url: request.url,
44
+ headers: request.headers,
45
+ timeout: timeout
46
+ )
47
+ when :post
48
+ response = RestClient::Request.execute(
49
+ method: :post,
50
+ url: request.url,
51
+ payload: request.body,
52
+ headers: request.headers,
53
+ timeout: timeout
54
+ )
55
+ when :delete
56
+ response = RestClient::Request.execute(
57
+ method: :delete,
58
+ url: request.url,
59
+ payload: request.body,
60
+ headers: request.headers,
61
+ timeout: timeout
62
+ )
63
+ end
64
+ rescue RestClient::RequestTimeout
65
+ raise 'Request Timeout'
66
+ rescue RestClient::Exception => e
67
+ response = e.response
68
+ end
69
+
70
+ Delphix::WebResponse.new(response)
71
+ end
72
+ end
73
+
74
+ # @param [String, Symbol] name
75
+ # A string or symbol used to identify the key portion of the HTTP headers.
76
+ # @param [String, Symbol, Array] value
77
+ # A string, symbol or array containing the values of the HTTP header.
78
+ #
79
+ # @return [Hash]
80
+ # The result of the key/value pair.
81
+ #
82
+ def self.default_header(name, value)
83
+ @@default_headers[name] = value
84
+ end
85
+
86
+ # @return [Hash]
87
+ # The HTTP headers sent with a POST/GET/DELETE.
88
+ #
89
+ def self.clear_default_headers
90
+ @@default_headers = {}
91
+ end
92
+
93
+ # @param [Integer] seconds
94
+ # Set the number of seconds to wait for an HTTP timeout.
95
+ #
96
+ # @return [undefined]
97
+ #
98
+ def self.timeout(seconds)
99
+ @@timeout = seconds
100
+ end
101
+
102
+ # Define the #get, #post, and #delete helper methods for sending HTTP
103
+ # requests to the Delphix engine. You shouldn't need to use these methods
104
+ # directly, but they can be useful for debugging.
105
+ #
106
+ # The following HTTP methods are supported by the Delphix Appliance:
107
+ #
108
+ # GET - Retrieve data from the server where complex input is not needed.
109
+ # All GET requests are guaranteed to be read-only, but not all
110
+ # read-only requests are required to use GET. Simple input
111
+ # (strings, number, boolean values) can be passed as query
112
+ # parameters.
113
+ # POST - Issue a read/write operation, or make a read-only call that
114
+ # requires complex input. The optional body of the call is
115
+ # expressed as JSON.
116
+ # DELETE - Delete an object on the system. For languages that don't provide
117
+ # a native wrapper for DELETE, or for delete operations with
118
+ # optional input, all delete operations can also be invoked as POST
119
+ # to the same URL with /delete appended to it.
120
+ #
121
+ # Each method returns a hash that responds to #code, #headers, #body and
122
+ # #raw_body obtained from parsing the JSON object in the response body.
123
+ #
124
+ # @param url [String<URL>] url the url of where to send the request
125
+ # @param [Hash{Symbol => String}] parameters key-value data of the HTTP
126
+ # API request
127
+ # @param [Block] block block to execute when the request returns
128
+ # @return [Fixnum, #code] the response code from Delphix engine
129
+ # @return [Hash, #headers] headers, beautified with symbols and underscores
130
+ # @return [Hash, #body] body parsed response body where applicable (JSON
131
+ # responses are parsed to Objects/Associative Arrays)
132
+ # @return [Hash, #raw_body] raw_body un-parsed response body
133
+ #
134
+ # @api semipublic
135
+ [:get, :post, :delete].each do |method|
136
+ define_singleton_method(method) do |url, parameters = {}, &callback|
137
+ WebClient.request(method.to_sym, url, @@default_headers,
138
+ parameters.to_json, @@timeout, &callback)
139
+ end
140
+ end
141
+
142
+ module InstanceMethods
143
+ private
144
+
145
+ # Returns the current api endpoint, if present.
146
+ #
147
+ # @return [nil, String] the current endpoint
148
+ def endpoint
149
+ nil
150
+ end
151
+ end
152
+
153
+ # @!classmethods
154
+ module ClassMethods
155
+ # When present, lets you specify the api for the given client.
156
+ #
157
+ # @param [String, nil] value the endpoint to use.
158
+ # @example Setting a string endpoint endpoint '/resources/json/delphix'
159
+ # @example Unsetting the string endpoint endpoint nil
160
+ def endpoint(value = nil)
161
+ define_method(:endpoint) { value }
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,96 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'base64'
21
+ require 'addressable/uri'
22
+
23
+ module Delphix
24
+ class WebRequest
25
+ include Delphix::Utils
26
+
27
+ # @!attribute [r] method
28
+ # The HTTP method used, 'GET', 'POST', or 'DELETE'.
29
+ # @return [String]
30
+ attr_reader :method
31
+ # @!attribute [r] url
32
+ # The API path/URL to POST, GET, DELETE from/to.
33
+ # @return [String]
34
+ attr_reader :url
35
+ # @!attribute [r] headers
36
+ # The HTTP headers, symbolized and underscored.
37
+ # @return [Hash]
38
+ attr_reader :headers
39
+ # @!attribute [r] body
40
+ # The parsed response body where applicable (JSON responses are parsed
41
+ # to Objects/Associative Arrays).
42
+ # @return [Hash]
43
+ attr_reader :body
44
+ # @!attribute [r] all
45
+ # The raw_body un-parsed response body.
46
+ # @return [JSON]
47
+ attr_reader :all
48
+
49
+ def add_header(name, value)
50
+ @headers[name] = value
51
+ end
52
+
53
+ def initialize(method, url, headers = {}, body = nil)
54
+ @method = method
55
+
56
+ if (method == :get)
57
+ if body.is_a?(Hash) && body.length > 0
58
+ if url.include? '?'
59
+ url += '&'
60
+ else
61
+ url += '?'
62
+ end
63
+
64
+ uri = Addressable::URI.new
65
+ uri.query_values = body
66
+ url += uri.query
67
+ end
68
+ else
69
+ @body = body
70
+ end
71
+
72
+ unless url =~ URI.regexp
73
+ raise 'Invalid URL: ' + url
74
+ end
75
+
76
+ @url = url.gsub(/\s+/, '%20')
77
+
78
+ @headers = {
79
+ 'Date' => utc_httpdate,
80
+ 'Request-ID' => request_id
81
+ }
82
+
83
+ headers.each_pair { |key, value| @headers[key.downcase] = value }
84
+
85
+ Delphix.last_request = {
86
+ headers: @headers,
87
+ method: @method,
88
+ url: @url
89
+ }
90
+ begin
91
+ Delphix.last_request[:body] = JSON.parse(@body) if body.length > 2
92
+ rescue Exception
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,89 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'json'
21
+
22
+ module Delphix
23
+ class WebResponse
24
+ include Net::HTTPHeader
25
+
26
+ # @!attribute [r] code
27
+ # The HTTP response code from Delphix engine.
28
+ # @return [#code]
29
+ attr_reader :code
30
+ # @!attribute [r] method
31
+ # The raw_body un-parsed response body from the Delphix engine.
32
+ # @return [#raw_body]
33
+ attr_reader :raw_body
34
+ # @!attribute [r] method
35
+ # The parsed response body where applicable (JSON responses are parsed
36
+ # to Objects/Associative Arrays).
37
+ # @return [#body]
38
+ attr_reader :body
39
+ # @!attribute [r] method
40
+ # The HTTP headers, symbolized and underscored.
41
+ # @return [#headers]
42
+ attr_reader :headers
43
+ # @!attribute [r] method
44
+ # The current session cookies.
45
+ # @return [#raw_body]
46
+ attr_reader :cookies
47
+
48
+ def initialize(response)
49
+ @code = response.code
50
+ @headers = response.headers
51
+ @raw_body = response
52
+ @body = @raw_body
53
+ @cookies = response.cookies
54
+
55
+ Delphix.last_response = {
56
+ code: response.code,
57
+ headers: response.headers,
58
+ body: JSON.parse(response.body),
59
+ cookies: response.cookies,
60
+ description: response.description
61
+ }.recursively_normalize_keys
62
+
63
+ begin
64
+ @body = JSON.parse(@raw_body)
65
+ rescue Exception
66
+ end
67
+ end
68
+
69
+ def ==(other)
70
+ @headers == other
71
+ end
72
+
73
+ def inspect
74
+ @headers.inspect
75
+ end
76
+
77
+ def method_missing(name, *args, &block)
78
+ if @headers.respond_to?(name)
79
+ @headers.send(name, *args, &block)
80
+ else
81
+ super
82
+ end
83
+ end
84
+
85
+ def respond_to?(method)
86
+ super || @headers.respond_to?(method)
87
+ end
88
+ end
89
+ end
data/lib/delphix.rb ADDED
@@ -0,0 +1,205 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'rest-client'
21
+ require_relative 'delphix/utils'
22
+ require_relative 'delphix/web_client'
23
+ require_relative 'delphix/web_request'
24
+ require_relative 'delphix/web_response'
25
+ require_relative 'delphix/version'
26
+
27
+ # A library for supporting connections to the Delphix API. The module Delphix
28
+ # is based on the singleton pattern, which restricts instantiation of a class
29
+ # to only one instance that is globally available.
30
+ #
31
+ # @example
32
+ # To establish a session with the Delphix appliance and get a cookie for
33
+ # your effort you issue the following sequence:
34
+ #
35
+ # * Configure the Delphix client to use the specified version of the API, in
36
+ # hash format as `{ major: 1, micro: 0, minor: 0 }`, for example:
37
+ # Delphix.api_version = {
38
+ # type: 'APIVersion',
39
+ # major: { major: 1, micro: 0, minor: 0 }
40
+ # }
41
+ # * Specify the Delphix server to connect to.
42
+ # Delphix.server = 'delphix.example.com'
43
+ # * Specify the Delphix username to connect with.
44
+ # Delphix.api_user = 'delphix'
45
+ # * Specify the Delphix password to use.
46
+ # Delphix.api_passwd = 'delphix'
47
+ # * Now you have an established session, go crazy.
48
+ #
49
+ module Delphix
50
+ include Utils
51
+
52
+ API_ENDPOINT = '/resources/json/delphix'
53
+ HTTP_HEADERS = {
54
+ 'Accept' => 'application/json; charset=UTF-8',
55
+ 'Content-Type' => 'application/json; charset=UTF-8',
56
+ 'User-Agent' => 'Delphix-Ruby-Client/1.0.0'
57
+ }
58
+
59
+ # Default timeout value in seconds.
60
+ @@timeout = 10
61
+
62
+ # Default headers
63
+ @@default_headers = {}
64
+
65
+ class << self
66
+ # @!attribute [rw] last_request
67
+ # @return [Hash] retruns the last request
68
+ attr_accessor :last_request
69
+ # @!attribute [rw] last_response
70
+ # @return [Hash] retruns the last response
71
+ attr_accessor :last_response
72
+ # @!attribute [rw] session
73
+ # @return [Hash] retruns current session state
74
+ # @return [#code] the response code from Delphix engine
75
+ # @return [#headers] beautified with symbols and underscores
76
+ # @return [#body] parsed response body
77
+ # @return [#raw_body] un-parsed response body
78
+ attr_accessor :session
79
+ # @!attribute [rw] api_version
80
+ # @return [Hash] containing the major, minor and micro version numbers.
81
+ attr_accessor :api_version
82
+ # @!attribute [rw] server
83
+ # @return [String] Delphix server address
84
+ attr_accessor :server
85
+ # @!attribute [rw] api_user
86
+ # @return [String] username to authenticate with
87
+ attr_accessor :api_user
88
+ # @!attribute [rw] api_passwd
89
+ # @return [String] password for authentication
90
+ attr_accessor :api_passwd
91
+ # @!attribute [rw] verbose
92
+ # @return [Nothing] enables verbosity
93
+ attr_accessor :verbose
94
+ end
95
+
96
+ # Returns the API endpoint for a given resource namespace by combining the
97
+ # server address with the appropriate HTTP headers.
98
+ #
99
+ # @param resource [Resource] namespace
100
+ #
101
+ # @return [URL] return the URL for the API endpoint
102
+ #
103
+ # @api public
104
+ def self.api_url(resource = nil)
105
+ 'http://' + @server + resource
106
+ end
107
+
108
+ def self.session
109
+ Delphix.default_header(:cookies, cookies)
110
+ @session ||= login(@api_user, @api_passwd)
111
+ end
112
+
113
+ # Establish a session with the Delphix engine and return an identifier
114
+ # through browser cookies. This session will be reused in subsequent calls,
115
+ # the same session credentials and state are preserved without requiring a
116
+ # re-authentication call. Sessions do not persisit between incovations.
117
+ #
118
+ # @return [Hash] cookies
119
+ # containing the new session cookies
120
+ #
121
+ # @api public
122
+ def self.cookies
123
+ @resp ||= Delphix.post session_url,
124
+ type: 'APISession', version: @api_version
125
+ @resp.cookies
126
+ end
127
+
128
+ # Authenticates the session so that API calls can be made. Only supports basic
129
+ # password authentication.
130
+ #
131
+ # @param [String] user
132
+ # user name to authenticate with
133
+ # @param [String] passwd
134
+ # password to authenticate with
135
+ #
136
+ # @return [Fixnum, #code]
137
+ # the response code from Delphix engine
138
+ # @return [Hash, #headers]
139
+ # headers, beautified with symbols and underscores
140
+ # @return [Hash, #body] body
141
+ # parsed response body where applicable (JSON responses are parsed to
142
+ # Objects/Associative Arrays)
143
+ # @return [Hash, #raw_body] raw_body
144
+ # un-parsed response body
145
+ #
146
+ # @api public
147
+ def self.login(user = @api_user, passwd = @api_passwd)
148
+ Delphix.post login_url,
149
+ type: 'LoginRequest', username: user, password: passwd
150
+ end
151
+
152
+ # @!method session
153
+ # A helper method to return the URL for the resource by using the
154
+ # resource_url shorthand.
155
+ # @return [String] The API path to session.
156
+ #
157
+ # @!method login
158
+ # A helper method to return the URL for the resource by using the
159
+ # resource_url shorthand.
160
+ # @return [String] The API path to login.
161
+ #
162
+ # @!method environment
163
+ # A helper method to return the URL for the resource by using the
164
+ # resource_url shorthand.
165
+ # @return [String] The API path to environment.
166
+ #
167
+ # @!method alert
168
+ # A helper method to return the URL for the resource by using the
169
+ # resource_url shorthand.
170
+ # @return [String] The API path to alert.
171
+ #
172
+ # @!method database
173
+ # A helper method to return the URL for the resource by using the
174
+ # resource_url shorthand.
175
+ # @return [String] The API path to database.
176
+ #
177
+ # @!method source
178
+ # A helper method to return the URL for the resource by using the
179
+ # resource_url shorthand.
180
+ # @return [String] The API path to source.
181
+ #
182
+ [:session, :login, :environment, :alert, :database, :source].each do |name|
183
+ define_singleton_method(name.to_s + '_url') do
184
+ api_url('/resources/json/delphix/' + name.to_s)
185
+ end
186
+ end
187
+
188
+ # @return [String] object inspection
189
+ # @!visibility private
190
+ def inspect
191
+ instance_variables.inject([
192
+ "\n#<#{self.class}:0x#{object_id.to_s(16)}>",
193
+ "\tInstance variables:"
194
+ ]) do |result, item|
195
+ result << "\t\t#{item} = #{instance_variable_get(item)}"
196
+ result
197
+ end.join("\n")
198
+ end
199
+
200
+ # @return [String] string of instance
201
+ # @!visibility private
202
+ def to_s
203
+ "<#{self.class}:0x#{object_id.to_s(16)} session=#{@session}>"
204
+ end
205
+ end