Class: Edi::Amazon::FeedMessageSender

Inherits:
BaseEdiService show all
Defined in:
app/services/edi/amazon/feed_message_sender.rb

Overview

Service object: feed message sender.

Constant Summary

Constants included from Edi::AddressAbbreviator

Edi::AddressAbbreviator::MAX_LENGTH

Instance Attribute Summary

Attributes inherited from BaseEdiService

#orchestrator

Attributes inherited from BaseService

#options

Instance Method Summary collapse

Methods inherited from BaseEdiService

#duplicate_po_already_notified?, #initialize, #mark_duplicate_po_as_notified, #report_order_creation_issues, #safe_process_edi_communication_log

Methods included from Edi::AddressAbbreviator

#abbreviate_street, #collect_street_originals, #record_address_abbreviation_notes

Methods inherited from BaseService

#initialize, #log_debug, #log_error, #log_info, #log_warning, #logger, #tagged_logger

Constructor Details

This class inherits a constructor from Edi::BaseEdiService

Instance Method Details

#ecl_in_queueObject



134
135
136
137
138
139
# File 'app/services/edi/amazon/feed_message_sender.rb', line 134

def ecl_in_queue
  EdiCommunicationLog.requiring_processing
                     .where(partner: orchestrator.partner)
                     .where(category: feed_category)
                     .order(:created_at)
end

#instantiate_transporter(transporter, transporter_profile = nil, options = {}) ⇒ Object



141
142
143
144
145
146
147
148
149
150
# File 'app/services/edi/amazon/feed_message_sender.rb', line 141

def instantiate_transporter(transporter, transporter_profile = nil, options = {})
  case transporter
  when :http_seller_api
    Transport::HttpSellerApiConnection.new({ profile: transporter_profile }.merge(options))
  when :http_api_upload
    Transport::HttpApiUploadConnection.new({ profile: transporter_profile }.merge(options))
  else
    raise "Unknown transporter: #{transporter}"
  end
end

#process(edi_communication_logs = nil) ⇒ Object

res_feed_id = res[:feedId]



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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'app/services/edi/amazon/feed_message_sender.rb', line 67

def process(edi_communication_logs = nil)
  feed_transport = instantiate_transporter(orchestrator.transporter, orchestrator.transporter_profile)

  edi_communication_logs ||= ecl_in_queue
  [edi_communication_logs].flatten.each do |ecl|
    logger.info "Sending feed data to #{orchestrator.partner}"
    # STEP 1 Create Feed Document
    # VERY IMPORTANT contentType must match Content-Type header in the subsequent upload PUT request exactly!
    ct = if ecl.data_type == 'xml'
           'text/xml; charset=UTF-8'
         else
           'application/json'
         end
    data = %({'contentType':'#{ct}'})
    res = feed_transport.send_data(data, "#{orchestrator.feed_message_remote_path}/documents", 'POST')
    ecl.notes = "HTTP CODE: #{res[:http_result]&.status}, HTTP BODY: #{res[:http_result]&.body}, HTTP METHOD: 'POST', Timestamp: #{Time.current.to_datetime.to_fs(:crm_default)}"
    logger.info "Result: HTTP CODE: #{res[:http_result]&.status}, HTTP BODY: #{res[:http_result]&.body}"
    if res[:success] && (data = res[:http_result]&.body.to_s).present?
      json_hash = JSON.parse(data).with_indifferent_access
      url = json_hash[:url]
      res_feed_document_id = json_hash[:feedDocumentId]
      # STEP 2 & 3 Contruct and Upload Feed Document from the signed feed document upload url returned, no auth needed
      # VERY IMPORTANT Content-Type header must match contentType in the previous Create Feed Document request exactly!
      feed_document_uploader = instantiate_transporter(:http_api_upload, nil, { headers: { 'Content-Type': ct } })
      res = feed_document_uploader.send_data(ecl.data, url, 'POST')
      # Both transports are now Faraday-backed and expose #status: the
      # feed_document_uploader (HttpApiUploadConnection) and feed_transport
      # (HttpSellerApiConnection, migrated to faraday-retry).
      ecl.notes += " | HTTP CODE: #{res[:http_result]&.status}, HTTP BODY: #{res[:http_result]&.body}, Timestamp: #{Time.current.to_datetime.to_fs(:crm_default)}"
      if res[:success]
        # STEP 4 Create Feed
        data = %(
          {
            "feedType":"#{feed_type}",
            "marketplaceIds": ["#{orchestrator.marketplace}"],
            "inputFeedDocumentId": "#{res_feed_document_id}"
          }
        )
        res = feed_transport.send_data(data, "#{orchestrator.feed_message_remote_path}/feeds", 'POST')
        ecl.notes += " | HTTP CODE: #{res[:http_result]&.status}, HTTP BODY: #{res[:http_result]&.body}, Timestamp: #{Time.current.to_datetime.to_fs(:crm_default)}"
        logger.info "Result: HTTP CODE: #{res[:http_result]&.status}, HTTP BODY: #{res[:http_result]&.body}"
        if res[:success] && (data = res[:http_result]&.body.to_s).present?
          json_hash = JSON.parse(data).with_indifferent_access
          res_feed_id = json_hash[:feedId]
          ecl.transaction_id = res_feed_id
          ecl.transmit_datetime = Time.current
          ecl.start_process!
        else
          ecl.error
        end
      else
        ecl.error
      end
    else
      ecl.error
    end
  rescue HTTP::RateLimitExceededError => e
    retry_after_seconds = e.retry_after.to_i.positive? ? e.retry_after.to_i : 1.hour.to_i
    transmit_after_time = Time.current + retry_after_seconds
    ecl.update_columns(transmit_after: transmit_after_time)
    logger.warn "Amazon SP-API rate limited for ECL #{ecl.id} (#{feed_category}/#{orchestrator.partner}). " \
                "Scheduled retry via transmit_after: #{transmit_after_time.iso8601} " \
                "(#{retry_after_seconds}s from now). #{e.message}"
  end
  edi_communication_logs
end