Class: Haipa::Client::HaipaServiceClient

Inherits:
MsRest::ServiceClient
  • Object
show all
Defined in:
lib/haipa_rest/haipa_service_client.rb

Overview

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(credentials, options = nil) ⇒ HaipaServiceClient

Returns a new instance of HaipaServiceClient.



17
18
19
20
21
22
23
24
25
26
27
# File 'lib/haipa_rest/haipa_service_client.rb', line 17

def initialize(credentials, options = nil)
  super(credentials, options)
  # This is the current default for Haipa services, and content-type

  # and accept supported by Autorest

  @request_headers =  {
      'Content-Type' => 'application/json;charset=utf-8',
      'Accept' => 'application/json'
  }
  add_user_agent_information("haipa_rest/#{Haipa::Client::REST_VERSION}")
  add_user_agent_information("Haipa client for Ruby")
end

Instance Attribute Details

#api_versionString

Returns api version of the Haipa service in string format.

Returns:

  • (String)

    api version of the Haipa service in string format.



15
16
17
# File 'lib/haipa_rest/haipa_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/haipa_rest/haipa_service_client.rb', line 12

def long_running_operation_retry_timeout
  @long_running_operation_retry_timeout
end

Instance Method Details

#check_for_status_code_failure(haipa_response) ⇒ Object

Verifies for unexpected polling status code

Parameters:



122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/haipa_rest/haipa_service_client.rb', line 122

def check_for_status_code_failure(haipa_response)
  fail MsRest::ValidationError, 'Haipa response cannot be nil' if haipa_response.nil?
  fail MsRest::ValidationError, 'Haipa response cannot have empty response object' if haipa_response.response.nil?
  fail MsRest::ValidationError, 'Haipa response cannot have empty request object' if haipa_response.request.nil?

  status_code = haipa_response.response.status
  http_method = haipa_response.request.method

  fail HaipaOperationError, "Unexpected polling status code from long running operation #{status_code}" unless status_code === 200 || status_code === 202 ||
      (status_code === 201 && http_method === :put) ||
      (status_code === 204 && (http_method === :delete || http_method === :post))
end

#get_async_common(request) ⇒ MsRest::HttpOperationResponse

Retrieves data by given URL.

Parameters:

  • request (MsRest::HttpOperationRequest)

    the URL.

Returns:

  • (MsRest::HttpOperationResponse)

    the response.



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/haipa_rest/haipa_service_client.rb', line 259

def get_async_common(request)
  fail ValidationError, 'Request cannot be nil' if request.nil?

  request.middlewares = [[MsRest::RetryPolicyMiddleware, times: 3, retry: 0.02], [:cookie_jar]]
  request.headers.merge!({'x-ms-client-request-id' => SecureRandom.uuid}) unless request.headers.key?('x-ms-client-request-id')
  request.headers.merge!({'Content-Type' => 'application/json'}) unless request.headers.key?('Content-Type')

  # Send Request

  http_response = request.run_promise do |req|
    @credentials.sign_request(req) unless @credentials.nil?
  end.execute.value!

  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 HaipaOperationError.new request, http_response, error_data, "Long running operation failed with status #{status_code}"
  end

  result = MsRest::HttpOperationResponse.new(request, 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, result)
  end

  result
end

#get_async_with_async_operation_deserialization(request) ⇒ MsRest::HttpOperationResponse

Retrieves data by given URL.

Parameters:

  • request (MsRest::HttpOperationRequest)

    the URL.

Returns:

  • (MsRest::HttpOperationResponse)

    the response.



245
246
247
248
249
250
251
# File 'lib/haipa_rest/haipa_service_client.rb', line 245

def get_async_with_async_operation_deserialization(request)
  result = get_async_common(request)

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

  result
end

#get_async_with_custom_deserialization(request, custom_deserialization_block) ⇒ MsRest::HttpOperationResponse

Retrieves data by given URL.

Parameters:

  • request (MsRest::HttpOperationRequest)

    the URL.

  • custom_deserialization_block (Proc)

    function to perform deserialization of the HTTP response.

Returns:

  • (MsRest::HttpOperationResponse)

    the response.



225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/haipa_rest/haipa_service_client.rb', line 225

def get_async_with_custom_deserialization(request, custom_deserialization_block)
  result = get_async_common(request)

  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_long_running_operation_result(haipa_response, custom_deserialization_block, final_state_via = FinalStateVia::DEFAULT) ⇒ MsRest::HttpOperationResponse

Retrieves the result of ‘POST’,‘DELETE’,‘PUT’ or ‘PATCH’ operation. Performs polling of required.

Parameters:

Returns:

  • (MsRest::HttpOperationResponse)

    the response.



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
77
78
79
80
81
82
83
84
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
# File 'lib/haipa_rest/haipa_service_client.rb', line 37

def get_long_running_operation_result(haipa_response, custom_deserialization_block, final_state_via = FinalStateVia::DEFAULT)
  check_for_status_code_failure(haipa_response)

  http_method = haipa_response.request.method

  polling_state = PollingState.new(haipa_response, @long_running_operation_retry_timeout)
  request = haipa_response.request

  if !AsyncOperationStatus.is_terminal_status(polling_state.status)
    task = Concurrent::TimerTask.new do
      begin
        if !polling_state.Haipa_async_operation_header_link.nil?
          update_state_from_Haipa_async_operation_header(polling_state.get_request(headers: request.headers, base_uri: request.base_uri, user_agent_extended: user_agent_extended), polling_state)
        elsif !polling_state.location_header_link.nil?
          update_state_from_location_header(polling_state.get_request(headers: request.headers, base_uri: request.base_uri, user_agent_extended: user_agent_extended), polling_state, custom_deserialization_block, final_state_via)
        elsif http_method === :put
          get_request = MsRest::HttpOperationRequest.new(request.base_uri, request.build_path.to_s, :get, {query_params: request.query_params, headers: request.headers, user_agent_extended: user_agent_extended})
          update_state_from_get_resource_operation(get_request, polling_state, custom_deserialization_block)
        else
          task.shutdown
          if final_state_via == FinalStateVia::LOCATION
            if !polling_state.response.body.to_s.empty?
              body = JSON.load(polling_state.response.body)
              polling_state.resource = custom_deserialization_block.call(body)
            else
              fail HaipaOperationError, 'Location header is missing from long running operation'
            end
          else
            fail HaipaOperationError, 'Location header is missing from long running operation'
          end
        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)
    # Process long-running PUT/PATCH

    if (http_method === :put || http_method === :patch) && polling_state.resource.nil?
      get_request = MsRest::HttpOperationRequest.new(request.base_uri, request.build_path.to_s, :get, {query_params: request.query_params, headers: request.headers})
      update_state_from_get_resource_operation(get_request, polling_state, custom_deserialization_block)
    end

    if final_state_via == FinalStateVia::LOCATION
      if((http_method === :post || http_method === :delete) && !polling_state.location_header_link.nil?)
        update_state_from_location_header(polling_state.get_request(headers: request.headers, base_uri: request.base_uri, user_agent_extended: user_agent_extended), polling_state, custom_deserialization_block, final_state_via)
      end
    end

    # Process long-running POST/DELETE operation with schema defined on success status codes

    if (http_method === :post || http_method === :delete) && custom_deserialization_block && polling_state.response
      unless polling_state.response.body.to_s.empty?
        body = JSON.load(polling_state.response.body)
        polling_state.resource = custom_deserialization_block.call(body)
      end
    end
  end

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

  polling_state.get_operation_response
end

#update_state_from_get_resource_operation(request, polling_state, custom_deserialization_block) ⇒ Object

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

Parameters:

  • request (MsRest::HttpOperationRequest)

    The url retrieve data from.

  • polling_state (Haipa::Client::PollingState)

    polling state to update.

  • custom_deserialization_block (Proc)

    custom deserialization method for parsing response.



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/haipa_rest/haipa_service_client.rb', line 141

def update_state_from_get_resource_operation(request, polling_state, custom_deserialization_block)
  result = get_async_with_custom_deserialization(request, custom_deserialization_block)

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

  # On non flattened resource, we should find provisioning_state inside 'properties'

  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
  # On flattened resource, we should find provisioning_state at the top level

  elsif result.body.respond_to?(:provisioning_state) && !result.body.provisioning_state.nil?
    polling_state.status = result.body.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_Haipa_async_operation_header(request, polling_state) ⇒ Object

Updates polling state from Haipa async operation header.

Parameters:



204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/haipa_rest/haipa_service_client.rb', line 204

def update_state_from_Haipa_async_operation_header(request, polling_state)
  result = get_async_with_async_operation_deserialization(request)

  fail HaipaOperationError, '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

  polling_state
end

#update_state_from_location_header(request, polling_state, custom_deserialization_block, final_state_via = FinalStateVia::DEFAULT) ⇒ Object

Updates polling state based on location header for HTTP requests.

Parameters:

  • request (MsRest::HttpOperationRequest)

    The url retrieve data from.

  • polling_state (Haipa::Client::PollingState)

    polling state to update.

  • custom_deserialization_block (Proc)

    custom deserialization method for parsing response.

  • final_state_via (Haipa::Client::FinalStateVia) (defaults to: FinalStateVia::DEFAULT)

    Final State via value



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
198
# File 'lib/haipa_rest/haipa_service_client.rb', line 173

def update_state_from_location_header(request, polling_state, custom_deserialization_block, final_state_via = FinalStateVia::DEFAULT)
  result = get_async_with_custom_deserialization(request, custom_deserialization_block)

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

  if status_code === 202
    polling_state.status = AsyncOperationStatus::IN_PROGRESS_STATUS
  elsif status_code === 200 || (status_code === 201 && http_method === :put) ||
      (status_code === 204 && (http_method === :delete || http_method === :post || http_method === :get))
    polling_state.status = AsyncOperationStatus::SUCCESS_STATUS

    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
  elsif final_state_via == FinalStateVia::LOCATION && status_code === 404 && http_method === :delete && !polling_state.Haipa_async_operation_header_link.nil? && !polling_state.location_header_link.nil?
    polling_state.status = AsyncOperationStatus::SUCCESS_STATUS
  else
    fail HaipaOperationError, "The response from long running operation does not have a valid status code. Method: #{http_method}, Status Code: #{status_code}"
  end
end