Class: Edi::Walmart::FeedSubmissionResultProcessor

Inherits:
BaseEdiService show all
Defined in:
app/services/edi/walmart/feed_submission_result_processor.rb

Constant Summary

Constants included from AddressAbbreviator

AddressAbbreviator::MAX_LENGTH

Instance Attribute Summary

Attributes inherited from BaseEdiService

#orchestrator

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 AddressAbbreviator

#abbreviate_street, #collect_street_originals, #record_address_abbreviation_notes

Methods inherited from BaseService

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

Constructor Details

This class inherits a constructor from Edi::BaseEdiService

Instance Method Details

#get_errors_from_feed_result(json_data) ⇒ Object



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
# File 'app/services/edi/walmart/feed_submission_result_processor.rb', line 62

def get_errors_from_feed_result(json_data)
  errs = []
  # see: oh wait there is no *&^%*^&% documentation!!!! Probably because this is a ridiculously stupid ^&%&^% implementation!
  # There's just this sample operation garbage-y response in postman (see my overview below and full details at the end)

  # errors: []
  # feedId:  <string>
  # feedStatus:  PROCESSED
  # ingestionErrors: {ingestionError: []}
  # itemsReceived: <integer>
  # itemsSucceeded:  <integer>
  # itemsFailed: <integer>
  # itemsProcessing: <integer>
  # offset:  <integer>
  # limit: <integer>
  # itemDetails: {itemIngestionStatus: []}

  hsh = JSON.parse(json_data).with_indifferent_access
  num_processed = hsh&.dig(:itemsReceived).to_i
  num_errors = hsh&.dig(:itemsFailed).to_i
  if num_processed > 0 && num_errors > 0
    # First deal with the &*^&*^ errors, I mean this is utter &^*&^& stupidity
    hsh&.dig(:errors)&.each do |err_hsh|
      errs << "Error code: #{err_hsh.dig(:code)}, field: #{err_hsh.dig(:field)}, description: #{err_hsh.dig(:description)}, info: #{err_hsh.dig(:info)}, severity: #{err_hsh.dig(:severity)}, category: #{err_hsh.dig(:category)}, component: #{err_hsh.dig(:component)}, type: #{err_hsh.dig(:type)}, serviceName: #{err_hsh.dig(:serviceName)}, gatewayErrorCategory: #{err_hsh.dig(:gatewayErrorCategory)}, causes: #{err_hsh.dig(:causes)}"
    end
    # Now deal with the &*^&*^ ingestion errors, more &^*&^& stupidity
    hsh&.dig(:ingestionErrors)&.dig(:ingestionError)&.each do |ing_err_hsh|
      errs << "Ingestion Error code: #{ing_err_hsh.dig(:code)}, type: #{ing_err_hsh.dig(:type)}, description: #{ing_err_hsh.dig(:description)}"
    end
    # Now deal with the &*^&*^ item details ingestion errors
    hsh&.dig(:itemDetails)&.dig(:itemIngestionStatus)&.each do |item_ing_status_hsh|
      next unless item_ing_status_hsh.dig(:ingestionStatus)&.upcase&.include?('ERROR')

      sku = item_ing_status_hsh.dig(:sku)
      item_ing_status_hsh.dig(:ingestionErrors)&.dig(:ingestionError)&.each do |item_ing_err_hsh|
        errs << "SKU: #{sku}, error code: #{item_ing_err_hsh.dig(:code)}, type: #{item_ing_err_hsh.dig(:type)}, description: #{item_ing_err_hsh.dig(:description)}"
      end
    end
  end
  errs
end

#instantiate_transporter(transporter, transporter_profile = nil) ⇒ Object



53
54
55
56
57
58
59
60
# File 'app/services/edi/walmart/feed_submission_result_processor.rb', line 53

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

#process(edi_communication_logs = nil) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
# File 'app/services/edi/walmart/feed_submission_result_processor.rb', line 5

def process(edi_communication_logs = nil)
  edi_communication_logs ||= EdiCommunicationLog
                             .where(state: 'processing')
                             .where(partner: orchestrator.partner)
                             .where.not(transaction_id: nil)
                             .order(:created_at)
  [edi_communication_logs].flatten.each do |ecl|
    logger.info "EdiCommunicationLog:#{ecl.id} - Retrieving API transaction result from #{orchestrator.partner} for transaction id: #{ecl.transaction_id}"
    begin
      transport = instantiate_transporter(orchestrator.transporter, orchestrator.transporter_profile)
      res = transport.send_data('', "#{orchestrator.feed_message_remote_path}/#{ecl.transaction_id}?includeDetails=true", 'GET') # with this, you only get the first 50 by default, see below (from Postman gobbledygook):
      # includeDetails: Includes details of each entity in the feed.
      # offset: The object response to start with, where 0 is the first entity that can be requested. It can only be used when includeDetails is set to true.
      # limit: The number of entities to be returned. It cannot be more than 50 entities. Use it only when the includeDetails is set to true.
      # Read the response body once (HTTP::Response::Body can only be consumed once)
      data = res[:http_result]&.body.to_s
      ecl.update(notes: "HTTP CODE: #{res[:http_result]&.code}, HTTP BODY: #{data}, Timestamp: #{Time.current.to_datetime.to_fs(:crm_default)}")
      logger.info "Result: HTTP CODE: #{res[:http_result]&.code}, HTTP BODY: #{data}"
      if res[:success] && data.present?
        json_hash = JSON.parse(data).with_indifferent_access
        status = json_hash.dig('feedStatus')
        timeout_time = (ecl.transmit_datetime || Time.current) + orchestrator.failure_timeout_in_minutes.minutes
        # See: https://developer.walmart.com/us-marketplace/docs/feeds-overview
        if status == 'PROCESSED'
          upload = Upload.uploadify_from_data(file_name: "#{ecl.transaction_id}.json", data: data, category: 'feed_document_result_json')
          if upload
            ecl.uploads << upload
            if (errors = get_errors_from_feed_result(data)).empty?
              ecl.complete!
            else
              ecl.update(notes: [ecl.notes, "There were #{errors.size} errors reported in feed result: #{errors.join('| ')}"].compact.join(' || '))
              ecl.error
            end
          else
            ecl.update(notes: [ecl.notes, "Could not upload feed result #{ecl.transaction_id}"].compact.join(' | '))
            ecl.error
          end
        elsif %w[ERROR].include?(status) || Time.current > timeout_time
          ecl.update(notes: [ecl.notes, "Timed out after #{orchestrator.failure_timeout_in_minutes} minutes"].compact.join(' | ')) if Time.current > timeout_time
          ecl.error!
        end
      else
        ecl.error
      end
    end
  end
end