ETM connector

core/ingestion/infra/etm — iter-1 reference implementation of ADR-0024 supplier-connector contract.

Transport + endpoints

Base URL is environment-selected: local mock http://mock-etm:9000, ETM test https://itest2.etm.ru/api/v1, ETM prod https://ipro.etm.ru/api/v1.

PathKindPurpose
POST /user/loginauthexchange login/password for session token
async catalog reportcataloglive enumerator получает catalog snapshot через ETM report
GET /goods/{sku}goodsdiagnostic/single SKU product descriptor + characteristics
GET /goods/{sku1,sku2,...}/pricepricescheduled refresh batch по 25 SKU
GET /goods/remainsremainsscheduled refresh warehouse stock export for all goods
GET /goods/{sku}/remainsremainsdiagnostic/single SKU fallback

Login uses form-urlencoded log/pwd. All data-plane requests carry type=etm&session-id=<id> in the query string after first login.

Capabilities (static)

Defined in backend/internal/core/ingestion/infra/etm/capabilities.go:

  • Transport: REST
  • PriceSet: net + gross + list_tarif + retail_rec (no VAT rate)
  • Stock + StockForecast: true; warehouse kinds regional_center, logistics_center, pickup_office, manufacturer_warehouse
  • AsyncJobs: declared true (seed enumerator does not trigger iter-1)
  • Media: images + videos + certificates; watermarked=true; unwatermarked gate = requires_agreement
  • MultiCurrency: RUB only

Session policy

  • Login gated by AuthBucket (1 req / 120s).
  • SessionHandle cached under tracium:etm:session:<credential_ref> in Redis (TTL 2h). In-memory fallback when Redis nil.
  • 403 on a Fetch → re-login single retry (session invalidated server- side).
  • 3 consecutive AuthRejected after fresh login → mark supplier failing (iter-1: log + stop, manual resume).

Rate limits

BucketLimitScope
AuthBucket1 req / 120slogin only, blocking wait
DataBucket[goods]1 req / 1sper request
DataBucket[price]1 req / 1sper request
DataBucket[remains]1 req / 1sper request
DataBucket[async_poll]1 req / 1sdeclared; Phase 5 usage

Data-bucket exhaustion returns ConnectorError{Code:RateLimited} and orchestrator skips the SKU for the current tick.

Scheduled refresh не работает SKU-by-SKU: catalog приходит report snapshot, prices идут batch по 25 SKU, stock берется bulk endpoint /goods/remains и фильтруется по текущему catalog snapshot. При *_CATALOG_MAX_ITEMS=0 это полный snapshot, а не window. В live local-prod прогоне 2026-05-06: 1000 catalog items, 1000 price payloads за 318691 ms, stock export за 2672 ms, 24 SKU с ненулевым stock payload. raw_remains=1000 означает обработанную stock axis; пустой stock для SKU не является ошибкой.

Live contract 2026-05-07: /goods/{sku1,...,sku50}/price вернул 50 rows за ~18.6s; price fields (price, pricewnds, price_tarif, price_retail) в этом ответе number. /goods/remains вернул 11588 rows за ~3s; GdsCode, StoreCode, RemInfo приходят string и преобразуются bulk source’ом в InfoStores для stock mapper.

Error taxonomy mapping (ADR-0024 §4)

HTTPConnectorErrorCodeOrchestrator behaviour
200(none)store payload
401AuthRejectedsurface to SessionManager → retry once then fail tick
403SessionExpiredinvalidate cache; re-login single retry
404NotFoundwrite empty-map observation (§3.1.3); SKU stays in seed
429RateLimitedskip SKU this tick; retry next tick
4xx (other)Permanentwrite observation with source_outcome=partial
5xxTransientretry with exp-backoff (1→2→4s)

EgressPolicy

FixedIPRequired=true for real ETM. dev-stack (mock-ETM) ignores; prod deploy runbook (docs/docs/30-services/ingestion/supplier-sync-runbook.md) gates on whitelist verification.

Imitation service

deploy/docker/resources/mock-etm/ — Go stdlib HTTP server under compose profile test. Covers happy path + NOTFOUND/RATELIMITED special SKUs. Mandated by ADR-0033. Live validation against ETM test or prod is gated behind TRACIUM_ETM_REAL=1 in the provider-contract suite.

Seed enumerator

SeedEnumerator reads backend/config/etm-seed.yaml — 30 synthetic SKUs iter-1. Full-catalog enumerator arrives Phase 5.

References

  • ADR-0024 supplier connector contract
  • ADR-0033 supplier imitation services mandated
  • spec docs/superpowers/specs/2026-04-20-ingestion-phase1-a-design.md §3.1
  • runbook docs/docs/30-services/ingestion/supplier-sync-runbook.md
  • detailed transport reference: ./etm/spec.md, ./etm/mapping.md, ./etm/rate-budget.md

в этой папке 5 элементов