Class: Edi::Wayfair::CatalogItemUpdateSender

Inherits:
BaseEdiService show all
Includes:
CatalogApiTransport
Defined in:
app/services/edi/wayfair/catalog_item_update_sender.rb

Overview

Sends catalog item updates to Wayfair using the Product Catalog Update API
Uses the updateMarketSpecificCatalogItems mutation

API Documentation: https://developer.wayfair.io/posts/catalog-product-update

Usage:
orchestrator = Edi::Wayfair::Orchestrator.build(:wayfair_us)
sender = CatalogItemUpdateSender.new(orchestrator)
result = sender.process(catalog_item: catalog_item)

Defined Under Namespace

Classes: StatusResult, UpdateResult

Constant Summary collapse

CATALOG_UPDATE_API_URL =

Product Catalog Update API endpoints

'https://api.wayfair.io/v1/product-catalog-api/graphql'
SANDBOX_CATALOG_UPDATE_API_URL =

URL for sandbox catalog update api.

'https://api.wayfair.io/sandbox/v1/product-catalog-api/graphql'
UPDATE_CATALOG_ITEMS_MUTATION =

GraphQL mutation for updating catalog items.
Live-schema notes (verified 2026-06-12 via sandbox introspection): the
response field is requestId (not requestHandle).

<<~GRAPHQL.squish
  mutation UpdateMarketSpecificCatalogItems($input: UpdateMarketSpecificCatalogItemsInput!) {
    updateCatalogEntitiesMutations {
      updateMarketSpecificCatalogItems(input: $input) {
        requestId
      }
    }
  }
GRAPHQL
STATUS_QUERY =

GraphQL query to check update status. statusOfUpdateRequest takes a
StatusOfUpdateRequestInput ({ requestId, supplierId }) and reports the
async validation outcome (status COMPLETED / BLOCKED / PROCESSING …)
with per-entity problems.

<<~GRAPHQL.squish
  query StatusOfUpdateRequest($input: StatusOfUpdateRequestInput!) {
    statusOfUpdateRequest(input: $input) {
      requestId
      validationOnly
      status
      problems {
        code
        title
        detail
        catalogEntityIdentifier
        catalogEntityPropertyId
        inputValue
      }
    }
  }
GRAPHQL

Constants included from CatalogApiTransport

Edi::Wayfair::CatalogApiTransport::CATALOG_API_BASE, Edi::Wayfair::CatalogApiTransport::CATALOG_AUTH_URL

Constants included from AddressAbbreviator

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 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

#check_status(request_id) ⇒ StatusResult

Check the status of a previous update request

Parameters:

  • request_id (String)

    The request id from a previous update

Returns:



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'app/services/edi/wayfair/catalog_item_update_sender.rb', line 122

def check_status(request_id)
  transport = build_catalog_transport
  variables = { input: { requestId: request_id, supplierId: orchestrator.try(:warehouse_code).to_s } }
  query_payload = build_graphql_payload(STATUS_QUERY, variables)

  result = transport.send_request('POST', catalog_api_url, query_payload, transport.build_request_headers)
  http_response = result[:http_res]

  unless http_response&.status&.between?(200, 299)
    return StatusResult.new(
      success: false,
      status: 'ERROR',
      message: "HTTP error: #{http_response&.status}",
      details: nil
    )
  end

  json_data = JSON.parse(http_response.body.to_s).with_indifferent_access

  if json_data[:errors].present?
    return StatusResult.new(
      success: false,
      status: 'ERROR',
      message: json_data[:errors].first&.dig('message'),
      details: json_data[:errors]
    )
  end

  status_data = json_data.dig(:data, :statusOfUpdateRequest)
  problems = Array(status_data&.dig(:problems))

  StatusResult.new(
    success: true,
    status: status_data&.dig(:status),
    message: problems.first&.then { |p| [p[:code], p[:detail].presence || p[:title]].compact.join(': ') },
    details: status_data
  )
end

#process(catalog_item:, attributes: nil, dry_run: false, validate_only: false) ⇒ UpdateResult

Send a catalog item update to Wayfair

Parameters:

  • catalog_item (CatalogItem)

    The catalog item to update

  • attributes (Array<String>, nil) (defaults to: nil)

    Optional list of attribute names to send

  • dry_run (Boolean) (defaults to: false)

    If true, only generates payload without sending

  • validate_only (Boolean) (defaults to: false)

    If true, Wayfair validates the update
    server-side without applying it (wire-level validateOnly)

Returns:



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
# File 'app/services/edi/wayfair/catalog_item_update_sender.rb', line 73

def process(catalog_item:, attributes: nil, dry_run: false, validate_only: false)
  taxonomy_category_id = catalog_item.wayfair_taxonomy_category_id_in_effect
  wayfair_schema = orchestrator.wayfair_schema_for(taxonomy_category_id)

  unless wayfair_schema
    return UpdateResult.new(
      success: false,
      request_id: nil,
      errors: ['No taxonomy schema available. Please sync catalog item first.'],
      ecl: nil,
      payload: nil
    )
  end

  # Generate the update payload
  generator = ListingGenerator::ListingGenerator.new(catalog_item, wayfair_schema: wayfair_schema)
  item_payload = generator.generate(attributes: attributes)

  unless item_payload
    return UpdateResult.new(
      success: false,
      request_id: nil,
      errors: generator.errors,
      ecl: nil,
      payload: nil
    )
  end

  if dry_run
    logger.info "Wayfair Catalog Update [DRY RUN]: Would send #{item_payload[:attributes].size} attributes for #{item_payload[:supplierPartNumber]}"
    return UpdateResult.new(
      success: true,
      request_id: 'dry_run',
      errors: [],
      ecl: nil,
      payload: item_payload
    )
  end

  # Build the full mutation input
  mutation_input = build_mutation_input(item_payload, taxonomy_category_id: taxonomy_category_id, validate_only: validate_only)

  # Send to Wayfair
  send_update(catalog_item, mutation_input, item_payload)
end