Class: Edi::Wayfair::CatalogItemGroupUpdateSender

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

Overview

Updates market-specific catalog item GROUPS (Wayfair's variant listings)
via the Product Catalog Update API's updateMarketSpecificCatalogItemGroups
mutation — group name, marketing copy, feature bullets, option labels and
group media, keyed by the WRM display SKU (itemGroupId).

Scope write:catalog_item_group_product_update_market_specific was granted
2026-07-06 (both regions); write path proven the same day with a
validateOnly update on WRM10021 (requestId c4822725-…, COMPLETED, no
problems). Note: the mutation updates group CONTENT only — membership
(which SKUs belong to a group) still goes through Consolidate tickets, and
option AXES still derive from Wayfair's class standards.

CountryInput currently enumerates UNITED_STATES and UNITED_KINGDOM only.
Canada works via the canada credentials + CA supplierId with the
en-US/UNITED_STATES market context (probe-verified 2026-07-06,
COMPLETED); an en-CA locale is rejected downstream ("Error occurred
while validating/updating offering name"), so keep the en-US context
until Wayfair extends CountryInput.

Examples:

validate a rename for a Wayfair display-SKU group

sender = Edi::Wayfair::CatalogItemGroupUpdateSender.new(Edi::Wayfair::Orchestrator.build(:wayfair_us))
sender.process(item_group_id: 'WRM10021', item_group_name: 'Grande Towel Warmer', validate_only: true)

Defined Under Namespace

Classes: GroupStatusResult, GroupUpdateResult

Constant Summary collapse

CATALOG_UPDATE_API_URL =

Product Catalog Update API endpoint (same host as the media sender).

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

Async mutation; poll the returned requestId via
Edi::Wayfair::CatalogItemUpdateSender#check_status / statusOfUpdateRequest.

<<~GRAPHQL.squish
  mutation UpdateMarketSpecificCatalogItemGroups($input: UpdateMarketSpecificCatalogItemGroupsInput!) {
    updateCatalogEntitiesMutations {
      updateMarketSpecificCatalogItemGroups(input: $input) {
        requestId
      }
    }
  }
GRAPHQL
STATUS_QUERY =

statusOfUpdateRequest on the Product Catalog Update API. Note this is
NOT interchangeable with Edi::Wayfair::CatalogItemUpdateSender#check_status — that
one targets the sandbox item-update endpoint and a different input type.

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

Constants included from CatalogApiTransport

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

Constants included from RequestIdentifiable

RequestIdentifiable::REQUEST_ID_HEADERS

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

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

Methods included from RequestIdentifiable

#partner_request_id

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:) ⇒ GroupStatusResult

Polls the async result of a group update.

Parameters:

  • request_id (String)

    requestId returned by #process

Returns:

  • (GroupStatusResult)

    status e.g. 'COMPLETED'; problems carry
    per-entity failures even when the request itself succeeds



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'app/services/edi/wayfair/catalog_item_group_update_sender.rb', line 119

def check_status(request_id:)
  transport = build_catalog_transport
  vars = { input: { requestId: request_id, supplierId: orchestrator.try(:warehouse_code).to_s } }
  result = transport.send_request('POST', CATALOG_UPDATE_API_URL,
                                  { query: STATUS_QUERY, variables: vars }.to_json,
                                  transport.build_request_headers)
  body = result[:parsed_res] || begin
    JSON.parse(result[:http_res].body)
  rescue StandardError
    {}
  end
  payload = body.dig('data', 'statusOfUpdateRequest') || {}
  GroupStatusResult.new(status: payload['status'], problems: Array(payload['problems']),
                        errors: Array(body['errors']).pluck('message'))
end

#process(item_group_id:, item_group_name: nil, marketing_copy: nil, feature_bullets: nil, option_content: nil, media_content: nil, validate_only: false) ⇒ GroupUpdateResult

Sends one group update.

Parameters:

  • item_group_id (String)

    the WRM display SKU / Wayfair itemGroupId
    (item-level marketplace data — read it from the members'
    catalog_items.parent_sku)

  • item_group_name (String, nil) (defaults to: nil)

    new display name for the group

  • marketing_copy (String, nil) (defaults to: nil)
  • feature_bullets (String, nil) (defaults to: nil)
  • option_content (Array<Hash>, nil) (defaults to: nil)

    [{ optionId: Integer, optionName: String }]

  • media_content (Array<Hash>, nil) (defaults to: nil)

    [{ mediaType: 'IMAGE'|'VIDEO'|'DOCUMENT', mediaUrl: String }]

  • validate_only (Boolean) (defaults to: false)

    server-side dry run

Returns:



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

def process(item_group_id:, item_group_name: nil, marketing_copy: nil, feature_bullets: nil,
            option_content: nil, media_content: nil, validate_only: false)
  return GroupUpdateResult.new(success: false, request_id: nil, errors: ['item_group_id is required']) if item_group_id.blank?

  group_update = {
    itemGroupId: item_group_id,
    itemGroupName: item_group_name,
    marketingCopy: marketing_copy,
    featureBullets: feature_bullets,
    optionContent: option_content,
    mediaContent: media_content
  }.compact
  return GroupUpdateResult.new(success: false, request_id: nil, errors: ['nothing to update']) if group_update.keys == [:itemGroupId]

  input = {
    supplierId: orchestrator.try(:warehouse_code).to_s,
    marketContext: { locale: 'en-US', country: 'UNITED_STATES', brand: 'WAYFAIR' },
    validateOnly: validate_only,
    catalogItemGroupsToUpdate: [group_update]
  }

  transport = build_catalog_transport
  result = transport.send_request('POST', CATALOG_UPDATE_API_URL,
                                  { query: UPDATE_GROUPS_MUTATION, variables: { input: input } }.to_json,
                                  transport.build_request_headers)
  body = result[:parsed_res] || begin
    JSON.parse(result[:http_res].body)
  rescue StandardError
    {}
  end
  request_id = body.dig('data', 'updateCatalogEntitiesMutations', 'updateMarketSpecificCatalogItemGroups', 'requestId')
  errors = Array(body['errors']).pluck('message')

  log_group_update(item_group_id, request_id, errors, validate_only)
  GroupUpdateResult.new(success: request_id.present?, request_id: request_id, errors: errors)
end