IEK connector
core/ingestion/infra/iek — второй reference-коннектор ADR-0024 (после
ETM). IEK проверяет архитектурную гибкость на трёх осях:
- HTTP Basic Auth вместо login-эндпоинта → SessionManager без сетевых вызовов, AuthBucket не задействован.
- Product endpoint
/api/products: scheduled refresh забирает полный catalog snapshot безart, а Connector.Fetch использует/api/products?art=<sku>&entity=allдля diagnostic/per-SKU lookup. - Optional stock endpoint
/api/commerce/Remains: stock/remains не входит в публичную документацию IEK API и выключен по умолчанию. Если конкретный tenant/credential получил рабочий endpoint, оператор включаетSUPPLIER_IEK_REMAINS_ENABLED=true; тогда commerce refresh забирает полный snapshot остатков безart, аConnector.Fetchможет запросить остатки SKU черезart=<sku>.
Transport + endpoints
| Path | Purpose |
|---|---|
GET /api/products?format=json | Scheduled full catalog snapshot; каждая строка уже содержит price fields. |
GET /api/products?art=<sku>&entity=all&format=json | Product lookup. Возвращает JSON-массив, где первый элемент содержит имя, цену, НДС, бренд, media и ETIM-характеристики. entity=all обязателен для полного payload. |
GET /api/commerce/Remains?format=json | Optional scheduled commerce snapshot остатков; используется только при SUPPLIER_IEK_REMAINS_ENABLED=true. |
GET /api/commerce/Remains?art=<sku>&format=json | Optional per-SKU lookup остатков для live/proposal paths. |
Рабочий prod-host сейчас https://lk.iek.ru; https://www.iek.ru/api/products
отвечает redirect’ом туда. Коннектор принимает и корневой host
https://lk.iek.ru, и полный endpoint https://lk.iek.ru/api/products.
Запрос всегда несёт Authorization: Basic <base64(login:password)>.
Scheduled refresh не работает SKU-by-SKU: BulkSnapshotSource вызывает
/api/products?format=json без art. Если SUPPLIER_IEK_REMAINS_ENABLED=true,
commerce job дополнительно вызывает /api/commerce/Remains?format=json и
раскладывает stock rows по SKU как KindRemains payload. При дефолтном
false IEK commerce пишет price/goods observation без stock axis и не
помечает тик partial из-за недокументированного endpoint. *_CATALOG_MAX_ITEMS=0
означает не ограничивать export; положительный лимит допустим только для
smoke/debug.
iter-1-b не потребляет /api/ddp, /api/services, /api/seriesspecifications
— добавится в Phase 5, когда появятся полные справочники.
Capabilities (статичное значение)
Определено в backend/internal/core/ingestion/infra/iek/capabilities.go:
- Transport: REST
- PriceSet: Net + Gross (вычисляются из
price+vat+vat_included), ListTarif (активируется когдаsalepriceперекрываетprice), VatRate как percent. RetailRec отсутствует. - Stock: false by default, StockForecast: false — публичный IEK API не
документирует stock/remains. При
SUPPLIER_IEK_REMAINS_ENABLED=trueconnector advertisesStock=true, текущие остатки идут из/api/commerce/Remains; forecast/delivery отсутствуют. - AsyncJobs: false — нет async-репортов.
- Media: images + documents;
watermarked=false(IEK-активы открытые). - MultiCurrency: только RUB.
Session policy
- Нет логина. Basic Auth несётся на каждом запросе.
SessionManager.AcquireвозвращаетSessionHandleс кредами вOpaqueбез сетевого вызова и без TTL (ValidUntil = +100 лет). SessionManager.Release— no-op.AuthBucketне задействован (тикер никогда не ждёт на логине).
Live contract notes
/api/products?format=jsonвернул500rows за ~0.6s (2026-05-07)./api/commerce/Remains?format=jsonотвечает Basic Auth challenge на unauthenticated probe, но с текущим DB credential authenticated live probe возвращает404 Route GET:/api/commerce/Remains?... not found(2026-05-30). До появления tenant-specific рабочего endpoint держатьSUPPLIER_IEK_REMAINS_ENABLED=false.priceприходит number илиnull;salepriceв sample былnull.
Rate limits
| Bucket | Лимит | Scope |
|---|---|---|
| DataBucket[goods/remains] | 1 req / 1s | /api/products; /api/commerce/Remains только при включенном Remains |
IEK публично не документирует свои лимиты — взят консервативный
1 rps дефолт. При появлении данных от IEK — обновить
backend/internal/core/ingestion/infra/iek/di.go (параметр
ratebuckets.NewMemoryBucket).
AuthBucket / AsyncPoll / другие классы — не применимы.
Error taxonomy mapping (ADR-0024 §4)
| HTTP | ConnectorErrorCode | Поведение оркестратора |
|---|---|---|
| 200 | (none) | сохраняем payload под KindGoods |
200 + [] | NotFound | пишем пустое observation (§3.1.3); SKU остаётся в seed |
| 401 | AuthRejected | возвращаем наверх без повторов (iter-1-b: ручной resume) |
| 429 | RateLimited | skip SKU этого тика; повтор в следующем |
| 4xx (прочее) | Permanent | observation с source_outcome=partial |
| 5xx | Transient | повторить по RetryPolicy (1→2→4s) |
Отдельных ETM-nuances вроде «403 → SessionExpired» здесь не требуется — IEK сессий нет.
EgressPolicy
FixedIPRequired = false — IEK API публичный, whitelist IP не требует.
Контраст с ETM (FixedIPRequired = true). Запуск в prod не требует
предварительной регистрации IP.
Imitation service
deploy/docker/resources/mock-iek/ — Go stdlib HTTP server под
compose profile test. Эмулирует /api/products и
/api/commerce/Remains, проверяет Basic Auth (любой непустой Basic
валиден), специальные SKU: IEK-NOTFOUND → 200 [], IEK-RATELIMITED → 429.
docker compose --profile test up -d mock-iek— поднять.- Порт 9100 внутри сети compose (публикация в host не настроена).
- Fixtures — в
deploy/docker/resources/mock-iek/fixtures/; три happy-path SKU (IEK-ABC123,IEK-DEF456,IEK-GHI789). - ADR-0033: mock — merge blocker. Реальный IEK sandbox запускаем
только через
TRACIUM_IEK_REAL=1вручную.
Seed enumerator
SeedEnumerator читает backend/config/iek-seed.yaml — 30 синтетических
SKU. Первые 5 — документированные маркеры (happy + два error-пути),
остальные 25 — placeholder’ы IEK-XYZ001..IEK-XYZ025. Phase 5
заменит seed-enumerator на dictionary-driven (полный каталог через
/api/ddp + /api/products).
Env: SUPPLIER_IEK_SEED_FILE (default backend/config/iek-seed.yaml).
Конфигурация (env)
| Env | Default | Назначение |
|---|---|---|
SUPPLIER_IEK_BASE_URL | — | Точка входа IEK API (mock-iek в dev, https://lk.iek.ru или полный https://lk.iek.ru/api/products в prod) |
SUPPLIER_IEK_LOGIN | — | Обязателен при непустом BaseURL |
SUPPLIER_IEK_PASSWORD | — | Обязателен при непустом BaseURL |
SUPPLIER_IEK_SEED_FILE | backend/config/iek-seed.yaml | Путь к seed-yaml |
SUPPLIER_IEK_REMAINS_ENABLED | false | Включает optional stock axis /api/commerce/Remains; держать false, пока live credential не подтвердил endpoint. |
SUPPLIER_IEK_REMAINS_PATH | /api/commerce/Remains | Path optional stock endpoint при включенном SUPPLIER_IEK_REMAINS_ENABLED. |
SUPPLIER_SYNC_ENABLED | etm | CSV-список поставщиков в тике. Для активации IEK — etm,iek |
Пустой BaseURL → IEK infra-module отдаёт zero-value ConnectorBundle,
ingestion-aggregator его фильтрует, тикер молча пропускает supplier
(сам supplier-sync стартует нормально).
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 - IEK public API: https://www.iek.ru/products/api/intro/
- runbook
docs/docs/30-services/ingestion/supplier-sync-runbook.md - provider-contract suite:
backend/test/provider-contract/iek_connector_contract_test.go