Class: Transport::MiraklTransporter

Inherits:
Object
  • Object
show all
Defined in:
app/services/transport/mirakl_transporter.rb

Overview

HTTP transporter for Mirakl marketplace integrations

Constant Summary collapse

VALID_HTTP_METHODS =

Valid http methods.

%w[get put post patch delete head].freeze
TIMEOUT_SECONDS =

Per-request timeout (seconds) for the Faraday-backed calls. #send_data
previously set this explicitly; #batch_download_data was unbounded — it now
shares the same 20s budget so a stalled poll can't hang the worker.

20

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ MiraklTransporter

Returns a new instance of MiraklTransporter.

Parameters:

  • options (Hash) (defaults to: {})

    connection configuration

Options Hash (options):

  • :profile (Symbol)

    configuration profile name whose headers
    are loaded from Heatwave::Configuration

  • :headers (Hash)

    extra request headers, merged over the profile headers

  • :logger (Logger)

    logger to use (defaults to Rails.logger)



21
22
23
24
25
26
27
28
29
30
31
# File 'app/services/transport/mirakl_transporter.rb', line 21

def initialize(options = {})
  @options = options
  # if there's a profile, grab the headers to send from it
  if (profile = @options[:profile])
    @headers = Heatwave::Configuration.fetch(profile&.to_sym)
    @headers = @headers.reject { |k| k == :hostname } # remove hostname which isn't used as a header
  end
  @headers ||= {}
  @headers = @headers.merge(options[:headers]) if options[:headers].present?
  @logger = options[:logger] || Rails.logger
end

Instance Attribute Details

#loggerObject (readonly)

Returns the value of attribute logger.



14
15
16
# File 'app/services/transport/mirakl_transporter.rb', line 14

def logger
  @logger
end

#profileObject (readonly)

Returns the value of attribute profile.



14
15
16
# File 'app/services/transport/mirakl_transporter.rb', line 14

def profile
  @profile
end

Instance Method Details

#batch_download_data(directory, pattern = nil) ⇒ Object

In one shot returns a hash, hash key is filename, hash value is content of file



92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'app/services/transport/mirakl_transporter.rb', line 92

def batch_download_data(directory, pattern = nil)
  data = {}
  logger.info "Attempting connection to http endpoint: #{directory}"
  # Retry transient connect failures (e.g. Faraday::ConnectionFailed "Network
  # is unreachable" reaching marketplace.castorama.fr / adeo-marketplace.mirakl.net)
  # instead of letting them surface as exceptions. EdiOrderFlowWorker polls this
  # path every ~5 minutes, so a momentary network blip should self-heal rather
  # than page. The :retry middleware on #connection provides the backoff the
  # Retryable wrapper used to. AppSignal #1916.
  response = connection.get(directory)
  data[pattern || 'response'] = response.body.to_s
  data
end

#rm(remote_file_path) ⇒ Object



106
107
108
# File 'app/services/transport/mirakl_transporter.rb', line 106

def rm(remote_file_path)
  # Do nothing
end

#send_data(data, remote_filepath, method = 'put') ⇒ Object



33
34
35
36
37
38
39
40
41
42
# File 'app/services/transport/mirakl_transporter.rb', line 33

def send_data(data, remote_filepath, method = 'put')
  method = method.downcase
  method = 'put' unless VALID_HTTP_METHODS.include?(method)
  body = data.present? ? JSON.parse(data) : nil
  # The :retry middleware on #connection replaces the old Retryable wrapper —
  # it backs off and retries the same transient transport failures in-band.
  http_res = connection.run_request(method.to_sym, remote_filepath, body, nil)
  logger.debug("HTTP request complete", url: remote_filepath, method: method, status: http_res&.status)
  { success: faraday_2xx?(http_res), http_result: http_res }
end

#send_file(remote_filepath, options) ⇒ Hash

Returns +{ success: Boolean, http_result: HTTP::Response }+.

Parameters:

  • remote_filepath (String)

    the absolute endpoint URL

  • options (Hash)

    multipart form fields for the POST

Options Hash (options):

  • :file (HTTP::FormData::File)

    the file part to upload

Returns:

  • (Hash)

    +{ success: Boolean, http_result: HTTP::Response }+



69
70
71
72
73
74
75
76
77
78
# File 'app/services/transport/mirakl_transporter.rb', line 69

def send_file(remote_filepath, options)
  http_obj = HTTP
  @headers.each do |k, v|
    http_obj = http_obj.headers(k.to_s.upcase.dasherize => v) # here the header key goes from say "x_houzz_api_ssl_token" to "X-HOUZZ-API-SSL-TOKEN"
  end

  http_res = http_obj.post(remote_filepath, form: options)
  logger.debug("HTTP file upload complete", url: remote_filepath, method: 'post', status: http_res&.code)
  { success: successful?(http_res), http_result: http_res }
end

#send_mirakl_data(data, remote_filepath, filename, options = {}) ⇒ Hash

Returns +{ success: Boolean, http_result: HTTP::Response }+.

Parameters:

  • data (String)

    the file content to send

  • remote_filepath (String)

    the absolute endpoint URL

  • filename (String)

    the filename for the multipart file part

  • options (Hash) (defaults to: {})

    additional multipart form fields for the POST

Options Hash (options):

  • :file (HTTP::FormData::File)

    the file part (set internally
    from +data+, overwriting any caller value)

Returns:

  • (Hash)

    +{ success: Boolean, http_result: HTTP::Response }+



51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'app/services/transport/mirakl_transporter.rb', line 51

def send_mirakl_data(data, remote_filepath, filename, options = {})
  http_obj = HTTP
  @headers.each do |k, v|
    http_obj = http_obj.headers(k.to_s.upcase.dasherize => v) # here the header key goes from say "x_houzz_api_ssl_token" to "X-HOUZZ-API-SSL-TOKEN"
  end
  # Build XML file to send
  io = StringIO.new(data)
  file = HTTP::FormData::File.new(io, filename:)
  options[:file] = file
  http_res = http_obj.post(remote_filepath, form: options)
  logger.debug("HTTP Mirakl request complete", url: remote_filepath, method: 'post', status: http_res&.code)
  { success: successful?(http_res), http_result: http_res }
end

#successful?(http_res) ⇒ Boolean

Returns:

  • (Boolean)


80
81
82
83
84
85
86
87
88
89
# File 'app/services/transport/mirakl_transporter.rb', line 80

def successful?(http_res)
  res = false
  ack_res = true
  if ['text/xml', 'text/csv'].include?(http_res.mime_type)
    res = true if (http_res.code >= 200) && (http_res.code < 300)
  elsif (http_res.code >= 200) && (http_res.code < 300) && ack_res
    res = true
  end
  res
end