Class: MsRestAzure::AzureServiceClient

Inherits:
MsRest::ServiceClient
  • Object
show all
Defined in:
lib/ms_rest_azure/azure_service_client.rb

Overview

Class which represents a point of access to the REST API.

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#api_versionString

Returns api version of the Azure in string format.

Returns:

  • (String)

    api version of the Azure in string format.



15
16
17
# File 'lib/ms_rest_azure/azure_service_client.rb', line 15

def api_version
  @api_version
end

#long_running_operation_retry_timeoutInteger

Returns execution interval for long running operations.

Returns:

  • (Integer)

    execution interval for long running operations.



12
13
14
# File 'lib/ms_rest_azure/azure_service_client.rb', line 12

def long_running_operation_retry_timeout
  @long_running_operation_retry_timeout
end

Instance Method Details

#get_async_common(operation_url, custom_headers) ⇒ MsRest::HttpOperationResponse

Retrives data by given URL.

Parameters:

  • operation_url (String)

    the URL.

  • custom_headers (String)

    headers to apply to the HTTP request.

Returns:

  • (MsRest::HttpOperationResponse)

    the response.



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/ms_rest_azure/azure_service_client.rb', line 275

def get_async_common(operation_url, custom_headers)
  fail ValidationError, 'Operation url cannot be nil' if operation_url.nil?

  url = URI(operation_url.gsub(' ', '%20'))

  fail URI::Error unless url.to_s =~ /\A#{URI::regexp}\z/

  # Create HTTP transport object
  connection = Faraday.new(:url => url) do |faraday|
    faraday.use MsRest::RetryPolicyMiddleware, times: 3, retry: 0.02
    faraday.use :cookie_jar
    faraday.adapter Faraday.default_adapter
  end

  request_headers = Hash.new
  request_headers['x-ms-client-request-id'] = SecureRandom.uuid
  request_headers['Content-Type'] = 'application/json'

  unless custom_headers.nil?
    custom_headers.each do |key, value|
      request_headers[key] = value
    end
  end

  # Send Request
  http_response = connection.get do |request|
    request.headers = request_headers
    @credentials.sign_request(request) unless @credentials.nil?
  end

  status_code = http_response.status

  if (status_code != 200 && status_code != 201 && status_code != 202 && status_code != 204)
    json_error_data = JSON.load(http_response.body)
    error_data = CloudErrorData.deserialize_object(json_error_data)

    fail AzureOperationError.new connection, http_response, error_data, "Long running operation failed with status #{status_code}"
  end

  result = MsRest::HttpOperationResponse.new(connection, http_response, http_response.body)

  begin
    result.body = JSON.load(http_response.body) unless http_response.body.to_s.empty?
  rescue Exception => e
    fail MsRest::DeserializationError.new("Error occured in deserializing the response", e.message, e.backtrace, http_response.body)
  end

  result
end

#get_async_with_async_operation_deserialization(operation_url, custom_headers) ⇒ MsRest::HttpOperationResponse

Retrives data by given URL.

Parameters:

  • operation_url (String)

    the URL.

  • custom_headers (String)

    headers to apply to the HTTP request.

Returns:

  • (MsRest::HttpOperationResponse)

    the response.



262
263
264
265
266
267
# File 'lib/ms_rest_azure/azure_service_client.rb', line 262

def get_async_with_async_operation_deserialization(operation_url, custom_headers)
  result = get_async_common(operation_url, custom_headers)

  result.body = AsyncOperationStatus.deserialize_object(result.body)
  result
end

#get_async_with_custom_deserialization(operation_url, custom_headers, custom_deserialization_block) ⇒ MsRest::HttpOperationResponse

Retrives data by given URL.

Parameters:

  • operation_url (String)

    the URL.

  • custom_headers (String)

    headers to apply to the HTTP request.

  • custom_deserialization_block (Proc)

    function to perform deserialization of the HTTP response.

Returns:

  • (MsRest::HttpOperationResponse)

    the response.



242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/ms_rest_azure/azure_service_client.rb', line 242

def get_async_with_custom_deserialization(operation_url, custom_headers, custom_deserialization_block)
  result = get_async_common(operation_url, custom_headers)

  if (!result.body.nil? && !custom_deserialization_block.nil?)
    begin
      result.body = custom_deserialization_block.call(result.body)
    rescue Exception => e
      fail MsRest::DeserializationError.new("Error occured in deserializing the response", e.message, e.backtrace, http_response.body)
    end
  end

  result
end

#get_post_or_delete_operation_result(azure_response, custom_headers, custom_deserialization_block) ⇒ MsRest::HttpOperationResponse

Retrieves the result of ‘POST’ or ‘DELETE’ operations. Perfroms polling of required.

Parameters:

  • azure_response (MsRestAzure::AzureOperationResponse)

    response from Azure service.

  • custom_headers (Proc)

    custom method for polling.

  • custom_deserialization_block (Proc)

    custom logic for response deserialization.

Returns:

  • (MsRest::HttpOperationResponse)

    the response.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/ms_rest_azure/azure_service_client.rb', line 85

def get_post_or_delete_operation_result(azure_response, custom_headers, custom_deserialization_block)
  fail MsRest::ValidationError, 'Azure response cannot be nil' if azure_response.nil?
  fail MsRest::ValidationError, 'Azure response cannot have empty response object' if azure_response.response.nil?

  status_code = azure_response.response.status

  if (status_code != 200 && status_code != 202 && status_code != 204)
    fail AzureOperationError, "Unexpected polling status code from long running operation #{status_code}"
  end

  polling_state = PollingState.new(azure_response, @long_running_operation_retry_timeout)

  if (!AsyncOperationStatus.is_terminal_status(polling_state.status))
    task = Concurrent::TimerTask.new do
      begin
        if !polling_state.azure_async_operation_header_link.nil?
          update_state_from_azure_async_operation_header(polling_state, custom_headers)
        elsif !polling_state.location_header_link.nil?
          update_state_from_location_header_on_post_or_delete(polling_state, custom_headers, custom_deserialization_block)
        else
          task.shutdown
          fail AzureOperationError, 'Location header is missing from long running operation'
        end

        if (AsyncOperationStatus.is_terminal_status(polling_state.status))
          task.shutdown
        end
      rescue Exception => e
        task.shutdown
        e
      end
    end

    polling_delay = polling_state.get_delay
    polling_delay = 0.1 if polling_delay.nil? || polling_delay == 0

    task.execution_interval = polling_delay
    task.execute
    task.wait_for_termination

    polling_error = task.value
    fail polling_error if polling_error.is_a?(Exception)
  end

  if (AsyncOperationStatus.is_failed_status(polling_state.status))
    fail polling_state.get_operation_error
  end

  return polling_state.get_operation_response
end

#get_put_operation_result(azure_response, custom_headers, custom_deserialization_block) ⇒ MsRest::HttpOperationResponse

Retrieves the result of ‘PUT’ operation. Perfroms polling of required.

Parameters:

  • azure_response (MsRestAzure::AzureOperationResponse)

    response from Azure service.

  • custom_headers (Hash)

    custom HTTP headers to apply to HTTP requests.

  • custom_deserialization_block (Proc)

    custom logic for response deserialization.

Returns:

  • (MsRest::HttpOperationResponse)

    the response.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/ms_rest_azure/azure_service_client.rb', line 24

def get_put_operation_result(azure_response, custom_headers, custom_deserialization_block)
  fail MsRest::ValidationError, 'Azure response cannot be nil' if azure_response.nil?

  status_code = azure_response.response.status

  if (status_code != 200 && status_code != 201 && status_code != 202)
    fail AzureOperationError, "Unexpected polling status code from long running operation #{status_code}"
  end

  polling_state = PollingState.new(azure_response, @long_running_operation_retry_timeout)
  operation_url = azure_response.request.url_prefix.to_s

  if (!AsyncOperationStatus.is_terminal_status(polling_state.status))
    task = Concurrent::TimerTask.new do
      begin
        if !polling_state.azure_async_operation_header_link.nil?
          update_state_from_azure_async_operation_header(polling_state, custom_headers)
        elsif !polling_state.location_header_link.nil?
          update_state_from_location_header_on_put(polling_state, custom_headers, custom_deserialization_block)
        else
          update_state_from_get_resource_operation(operation_url, polling_state, custom_headers, custom_deserialization_block)
        end

        if (AsyncOperationStatus.is_terminal_status(polling_state.status))
          task.shutdown
        end
      rescue Exception => e
        task.shutdown
        e
      end
    end

    polling_delay = polling_state.get_delay
    polling_delay = 0.1 if polling_delay.nil? || polling_delay == 0

    task.execution_interval = polling_delay
    task.execute
    task.wait_for_termination

    polling_error = task.value
    fail polling_error if polling_error.is_a?(Exception)
  end

  if (AsyncOperationStatus.is_successful_status(polling_state.status) && polling_state.resource.nil?)
    update_state_from_get_resource_operation(operation_url, polling_state, custom_headers, custom_deserialization_block)
  end

  if (AsyncOperationStatus.is_failed_status(polling_state.status))
    fail polling_state.get_operation_error
  end

  return polling_state.get_operation_response
end

#update_state_from_azure_async_operation_header(polling_state, custom_headers) ⇒ Object

Updates polling state from Azure async operation header.

Parameters:

  • polling_state (MsRestAzure::PollingState)

    polling state.

  • custom_headers (Hash)

    custom headers to apply to HTTP request.



203
204
205
206
207
208
209
210
211
212
213
# File 'lib/ms_rest_azure/azure_service_client.rb', line 203

def update_state_from_azure_async_operation_header(polling_state, custom_headers)
  result = get_async_with_async_operation_deserialization(polling_state.azure_async_operation_header_link, custom_headers)

  fail AzureOperationError, 'The response from long running operation does not contain a body' if result.body.nil? || result.body.status.nil?

  polling_state.status = result.body.status
  polling_state.error_data = result.body.error
  polling_state.response = result.response
  polling_state.request = result.request
  polling_state.resource = nil
end

#update_state_from_get_resource_operation(operation_url, polling_state, custom_headers, custom_deserialization_block) ⇒ Object

Updates polling state based on location header for PUT HTTP requests.

Parameters:

  • operation_url (String)

    The url retrieve data from.

  • polling_state (MsRestAzure::PollingState)

    polling state to update.

  • custom_headers (Hash)

    custom headers to apply to HTTP request.

  • custom_deserialization_block (Proc)

    custom deserialization method for parsing response.



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/ms_rest_azure/azure_service_client.rb', line 143

def update_state_from_get_resource_operation(operation_url, polling_state, custom_headers, custom_deserialization_block)
  result = get_async_with_custom_deserialization(operation_url, custom_headers, custom_deserialization_block)

  fail AzureOperationError, 'The response from long running operation does not contain a body' if result.response.body.nil? || result.response.body.empty?

  if (result.body.respond_to?(:properties) && result.body.properties.respond_to?(:provisioning_state) && !result.body.properties.provisioning_state.nil?)
    polling_state.status = result.body.properties.provisioning_state
  else
    polling_state.status = AsyncOperationStatus::SUCCESS_STATUS
  end

  error_data = CloudErrorData.new
  error_data.code = polling_state.status
  error_data.message = "Long running operation failed with status #{polling_state.status}"

  polling_state.error_data = error_data
  polling_state.update_response(result.response)
  polling_state.request = result.request
  polling_state.resource = result.body
end

#update_state_from_location_header_on_post_or_delete(polling_state, custom_headers, custom_deserialization_block) ⇒ Object

Updates polling state based on location header for POST and DELETE HTTP requests.

Parameters:

  • polling_state (MsRestAzure::PollingState)
    description
  • custom_headers (Hash)

    custom headers to apply to HTTP requests.

  • custom_deserialization_block (Proc)

    custom deserialization method for parsing response.



220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/ms_rest_azure/azure_service_client.rb', line 220

def update_state_from_location_header_on_post_or_delete(polling_state, custom_headers, custom_deserialization_block)
  result = get_async_with_custom_deserialization(polling_state.location_header_link, custom_headers, custom_deserialization_block)

  polling_state.update_response(result.response)
  polling_state.request = result.request
  status_code = result.response.status

  if (status_code == 202)
    polling_state.status = AsyncOperationStatus::IN_PROGRESS_STATUS
  elsif (status_code == 200 || status_code == 201 || status_code == 204)
    polling_state.status = AsyncOperationStatus::SUCCESS_STATUS
    polling_state.resource = result.body
  end
end

#update_state_from_location_header_on_put(polling_state, custom_headers, custom_deserialization_block) ⇒ Object

Updates polling state based on location header for PUT HTTP requests.

Parameters:

  • polling_state (MsRestAzure::PollingState)

    polling state to update.

  • custom_headers (Hash)

    custom headers to apply to HTTP request.

  • custom_deserialization_block (Proc)

    custom deserialization method for parsing response.



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/ms_rest_azure/azure_service_client.rb', line 169

def update_state_from_location_header_on_put(polling_state, custom_headers, custom_deserialization_block)
  result = get_async_with_custom_deserialization(polling_state.location_header_link, custom_headers, custom_deserialization_block)

  polling_state.update_response(result.response)
  polling_state.request = result.response

  status_code = result.response.status

  if (status_code == 202)
    polling_state.status = AsyncOperationStatus::IN_PROGRESS_STATUS
  elsif (status_code == 200 || status_code == 201)
    fail AzureOperationError, 'The response from long running operation does not contain a body' if result.body.nil?

    # In 202 pattern on PUT ProvisioningState may not be present in
    # the response. In that case the assumption is the status is Succeeded.
    if (result.body.respond_to?(:properties) && result.body.properties.respond_to?(:provisioning_state) && !result.body.properties.provisioning_state.nil?)
      polling_state.status = result.body.properties.provisioning_state
    else
      polling_state.status = AsyncOperationStatus::SUCCESS_STATUS
    end

    error_data = CloudErrorData.new
    error_data.code = polling_state.status
    error_data.message = "Long running operation failed with status #{polling_state.status}"

    polling_state.error_data = error_data
    polling_state.resource = result.body
  end
end