Ako Deedspan funguje a ako ho prevádzkovať

Deedspan je integračný framework pre Magento so zdrojovým kódom k dispozícii. Tu nájdete, z čoho sa inštalácia skladá, čo obsahuje projekt, základné pojmy, prvú integráciu, príkazy, prevádzku a rozšírenia.

Ako Deedspan funguje

Deedspan prenáša dáta medzi e-shopom a jeho ERP, skladom, účtovníctvom, PIM alebo CRM. Každý systém sa napája na spoločný dátový model nezávislý od e-shopu. Každé prostredie obchodníka má vlastnú inštaláciu, staging a produkcia sú dve inštalácie toho istého projektu.

Čo sa stane so záznamom

  1. Spúšťač začne beh flow.
  2. Načíta sa zdroj: súbory, výsledok dopytu, odpoveď API alebo dáta z webhooku.
  3. Každý riadok sa namapuje na záznam spoločného modelu, napríklad stock_level.
  4. Voliteľné PHP hooky záznamy upravia, rozdelia alebo preskočia.
  5. Nezmenené záznamy sa označia a neposielajú sa.
  6. Cieľ overí, že odkazované SKU existujú.
  7. Konektor zapisuje hromadne, najviac 1 000 záznamov v dávke.
  8. Pri každom zázname sa uloží výsledok.

Z čoho sa inštalácia skladá

ČasťNa čo slúži
AplikáciaJadro (deedhold/deedspan, Symfony bundle) a váš projekt
WorkerySpúšťajú behy, spracúvajú dávky a behy dokončujú; môže ich bežať viac
PlánovačSpúšťa flow podľa cronu; beží práve jeden
MariaDBBehy, výsledky záznamov, čo sa naposledy odoslalo, dáta správ, šifrované pripojenia
RabbitMQPovinný; nesie všetky fázy behu
Archív súborovŠifrované súbory na disku alebo v úložisku kompatibilnom s S3
Modul pre MagentoZachytávanie zmien, podpísané upozornenia, hromadný zápis skladu, stĺpec v prehľade objednávok

Čo obsahuje projekt

Malý projekt pre Composer založený z kostry. Je v git repozitári obchodníka a nasadzuje ho CI.

acme-integrations/

deedspan.yaml     pripojenia a balíky typov záznamov
flows/            jeden YAML súbor na každý flow
records/          vlastné typy záznamov a polia navyše
connectors/       konektory len pre tento projekt
lookups/          prevodové tabuľky v CSV
src/              PHP hooky, operácie, prenosy
samples/          vzorové súbory pre skúšky naprázdno
composer.json     jadro a doplnky; composer.lock je v gite
Dockerfile        runtime image a vaše balíky
compose.yaml      app, worker, scheduler, MariaDB, RabbitMQ

deedspan.yaml

Pripojenia k ostatným systémom a vypnuté balíky typov záznamov. Pri prístupových údajoch je len názov premennej prostredia, takže sa heslá nedostanú do gitu, do flow ani k hookom.

deedspan.yaml

connections:
  erp-ftp:
    settings: { host: sftp.erp.example, user: deedspan, root: /upload }
    secrets: { password: ERP_FTP_PASSWORD }
  shop:
    settings: { url: "https://shop.example.com" }
    secrets:
      consumer_key: MAGENTO_CONSUMER_KEY
      consumer_secret: MAGENTO_CONSUMER_SECRET
      token: MAGENTO_TOKEN
      token_secret: MAGENTO_TOKEN_SECRET
records:
  disabled: [pricing]

Prevodové tabuľky

CSV s hlavičkou key,value, z ktorého číta lookup().

lookups/warehouses.csv

key,value
01,main
02,east

Základné pojmy

Flow a spúšťanie

Každá integrácia sa skladá z jedného alebo viacerých flow. Flow prenáša jeden typ záznamov jedným smerom: zdroj, mapovanie, voliteľné hooky a cieľ. Uvádza, ktorému systému dáta patria (owner:), a dva flow nesmú zapisovať rovnaké pole do rovnakého cieľa.

flows/prices-from-erp.yaml

flow: prices-from-erp
owner: erp
record: price
triggers:
  schedule: "0 * * * *"
source:
  transport: sftp
  connection: erp-ftp
  path: /export/prices_*.csv
  format: { type: csv, delimiter: ";" }
map:
  sku:      "trim(row['sku'])"
  channel:  "lower(trim(row['channel']))"
  base:     "decimal(row['price'], ',')"
  currency: "upper(trim(row['currency']))"
target:
  connector: magento
  operation: price.update
  connection: shop
  channels: { eu: eu_store, uk: uk_store }
rules: { unknown_references: fail, compare_with: target }

Flow sa spúšťa jedným zo štyroch spôsobov, ktoré sa zapisujú pod triggers:.

SpúšťačFlow sa spustí, keď
schedulenastane čas podľa cronu, napríklad "*/15 * * * *"
shop_signalmodul v Magente ohlási zmenené objednávky alebo produkty
webhookiný systém zavolá podpísaný POST /webhooks/<name>
ručnevždy: z príkazového riadka, cez API, naprázdno alebo ako opakovanie

Webhook má dva režimy. V režime signal len oznámi, čo sa zmenilo, a flow si aktuálne dáta načíta sám, takže duplicitné alebo oneskorené volania nevadia. V režime payload nesie dáta priamo volanie; uložia sa a flow ich číta cez source: { transport: webhook }. Webhooky sa občas stratia, preto k nim nechajte bežať aj pomalý plán:

triggers

triggers:
  webhook: { name: payments, mode: payload }
  schedule: "0 3 * * *"     # nočná kontrola
KľúčObsahuje
sourcePrenos a formát, alebo stream konektora. group_by spojí susedné riadky do jedného záznamu.
mapJeden výraz na každé pole.
hooksPHP triedy v bodoch after_read, after_map, before_send, on_error.
targetOperácia konektora, alebo prenos, ktorý zapíše súbory, zavolá webhook alebo naplní medzitabuľku.
rulesunknown_references, compare_with a bezpečnostné limity ako max_change_share.

Každý flow sa kontroluje pri načítaní projektu, takže chyby zachytí už CI.

Záznamy

Záznam je jedna prenášaná položka, napríklad stav TENT-200 na skladovom mieste main. Typy záznamov sú definície v YAML s kľúčom a typovanými poľami.

TypKľúčHlavné polia
stock_levelsku + locationmnožstvo, na sklade, dátum naskladnenia
pricesku + channelzákladná cena, mena, množstevné a akciové ceny
productskutyp, sada atribútov, kanály, kategórie, viditeľnosť, varianty, súvisiace
categorynázovpreložené názvy
orderčíslo objednávkyzákazník, adresy, položky, súčty, platba, doprava, stav
order_updatečíslo objednávkystav, zásielka so sledovacím číslom, faktúra
  • Záznam nesie len polia, ktoré flow mapuje. Chýbajúce pole zostane bez zmeny, null ho vymaže a prázdna bunka v CSV dáta nikdy nezmaže.
  • Pole môže mať hodnotu pre každý predajný kanál alebo jazyk.
  • extends: pridá polia k typu z jadra.
  • references: product zabezpečí kontrolu, že SKU v cieli existuje.

Konektory

ČasťMožnosti
Prenossftp, ftp, local, http, soap, mssql a ďalšie databázy, email, s3, webhook
Formátcsv (akékoľvek kódovanie), xml, json, jsonl, pevná šírka
Konektor systémumagento (jadro); Pohoda, Helios, Fakturoid (doplnky); vlastné

Operácie pre Magento: stock.update, price.update, product.update, product.upsert, category.update. Zapisujú cez REST API Magenta podpísané OAuth 1.0a a cez tenký modul Deedspanu. Do databáz cudzích systémov sa zapisuje len do medzitabuliek alebo uložených procedúr.

Výrazy

Každé mapované pole má jeden výraz nad zdrojovým riadkom row. Výrazy sa nedostanú k súborom, sieti ani databáze a kontrolujú sa pri načítaní projektu.

SkupinaFunkcie
Texttrim, upper, lower, replace, split, extract, substr, pad, join, regex_match, regex_replace
Číslaint, decimal, round, abs, min, max
Dátumdate, format_date, add_days, now
Logikacoalesce, empty, in, ? :, ??
Prevodylookup, xref, setting
Kolekciemap, filter, first, count, sum

Keď výraz zlyhá, skončí chybou len jeho záznam:

záznam s chybou

map.quantity: int(): "many" is not a whole number. (expression: max(0, int(row['qty'])))

Behy a výsledky

Každé spustenie flow je beh. Delí sa na fázy v RabbitMQ a na dávky najviac po 1 000 záznamov.

  • Zápis je idempotentný podľa kľúča, napríklad TENT-200|main.
  • Posielajú sa len zmenené záznamy; compare_with: target porovnáva s aktuálnym stavom v cieli.
  • Z jedného flow beží vždy len jeden beh.
  • Beh, ktorý by toho zmenil alebo zmazal príliš veľa, počká na schválenie.
VýsledokVýznam
writtenOdoslané a prijaté
unchangedRovnaké ako minule, neodoslané
skippedVynechal ho hook alebo pravidlo
failedOdmietnuté; uloží sa pole a chyba
deletedZmazané, lebo v zdroji už nie je

Beh skončí ako completed, completed_with_failures alebo failed.

Ako začať

Stačí Docker, PHP beží v kontajneri.

  1. Založte projekt

    terminál

    $ composer create-project deedhold/deedspan-skeleton acme-integrations
    $ cd acme-integrations
    $ cp .env.example .env.local     # doplňte nastavenia a heslá
    $ docker compose up -d --build
  2. Napíšte flow

    Začnite s target: { transport: none }: záznamy sa namapujú a skontrolujú, ale nikam sa nepošlú.

    flows/stock-from-erp.yaml

    flow: stock-from-erp
    owner: erp
    record: stock_level
    source:
      transport: sftp
      connection: erp-ftp
      path: /export/stock_*.csv
      format: { type: csv, delimiter: ";", encoding: windows-1250 }
    map:
      sku:      "trim(row['sku'])"
      location: "lookup('warehouses', row['warehouse'])"
      quantity: "max(0, int(row['qty']))"
    target: { transport: none }
  3. Skontrolujte projekt

    terminál

    $ bin/console deedspan:flow:validate
    Record types: category, price, product, stock_level
    Connectors: magento
    Flows: stock-from-erp
  4. Vyskúšajte ho naprázdno

    Na cieľ ani databázu nesiahne. Keď niektorý záznam zlyhá, skončí s kódom 1, takže sa hodí do CI.

    terminál

    $ bin/console deedspan:flow:dry-run stock-from-erp samples/stock.csv
    TENT-200|main: sku=TENT-200, location=main, quantity=15
    BOOT-42|east: sku=BOOT-42, location=east, quantity=3
  5. Spustite ho dvakrát

    Druhý beh už nič nové nenájde.

    terminál

    $ bin/console deedspan:flow:run stock-from-erp --sync
    Run 01a1d5f1 completed: written 2, unchanged 0, skipped 0, failed 0.
    $ bin/console deedspan:flow:run stock-from-erp --sync
    Run 01a1d5f9 completed: written 0, unchanged 2, skipped 0, failed 0.
  6. Posielajte sklad do Magenta podľa plánu

    flows/stock-from-erp.yaml

    triggers: { schedule: "*/15 * * * *" }
    target:
      connector: magento
      operation: stock.update
      connection: shop
      locations: { main: default, east: east }   # zdroj MSI pre každé miesto

Príkazový riadok

Každý príkaz najprv načíta celý projekt. Logy idú na stderr ako JSON, výstup na stdout.

PríkazČo robí
deedspan:flow:validateSkontroluje typy záznamov, konektory a flow
deedspan:flow:dry-run <flow> <sample>Ukáže, čo by sa zapísalo; --json vypíše celý report
deedspan:flow:run <flow>Zaradí beh do frontu; --sync ho vykoná hneď, --force aj napriek bežiacemu behu
messenger:consume control bulkWorker
messenger:consume scheduler_flowsPlánovač (len jeden)
debug:schedulerČasy ďalších behov
messenger:statsPočet správ vo frontoch
messenger:failed:show / retrySprávy, ktoré vyčerpali pokusy
doctrine:migrations:migrateTabuľky v databáze; po každom upgrade

Tým istým nástrojom sa dajú spravovať pripojenia, nasadzovať a vracať verzie flow, znova odosielať chyby, schvaľovať pozastavené behy a exportovať celú inštaláciu. To isté vie API pre vaše nástroje aj portál Deedholdu.

Prevádzka

Nasadenie
Docker Compose na linuxovom serveri. CI skontroluje a vyskúša flow, zostaví image a nasadí ho. Prístup k plateným doplnkom ide ako build secret.
Prostredia
Najprv staging, produkcia dostane ten istý image po výslovnom schválení.
Verzie
Každé nasadenie uloží verziu flow, vrátiť ju ide jedným príkazom. Upgrade jadra: composer update deedhold/deedspan.
Heslá
Šifrovane v databáze inštalácie; nikdy v gite, hooky k nim nemajú prístup.
Logy
JSON na stderr s behom, flow, verziou, fázou, dávkou a kľúčom záznamu.
Upozornenia
Podpísané webhooky a e-mail: run.failed, record.failed, flow.silent, approval.needed a ďalšie.
Telemetria
OpenTelemetry cez OTLP, heartbeat URL pre každý flow, Sentry SDK.
Administrácia Magenta
Stĺpec Integrácia v prehľade objednávok; pri neúspešnej objednávke chyba a tlačidlo na opakovanie.
Chyby
Chybný riadok zhodí len svoj záznam. Neúspešné správy sa skúsia znova trikrát (po 1, 3 a 9 s). Neúspešné záznamy čakajú na zozname, odkiaľ ich pošlete znova.
Uchovávanie
Dáta a súbory 30 dní šifrovane, potom história a výsledky.
Odchod
Export pridá k repozitáru zálohu databázy a archív; ten istý Compose súbor spustíte kdekoľvek.

stderr

{"level_name":"WARNING","message":"Record failed.","context":{"event":"record.failed"},
 "extra":{"run_id":"01a1d668-1076-746d-9a19","flow":"stock-from-erp","stage":"send","chunk":3,"record_key":"STOVE-9|east"}}

Rozšírenia

Najprv YAML a výrazy, PHP len tam, kde nestačia. Inštalácia patrí jednému obchodníkovi, takže PHP z projektu beží bez sandboxu.

Vlastné konektory

Manifest connector.yaml nad všeobecnými prenosmi, PHP len v pomenovaných miestach. Konektor je v priečinku connectors/, alebo ako balík pre Composer.

connectors/trailhub/connector.yaml

schema_version: 1
connector: trailhub
version: 1.0.0
connection:
  url:   { type: string }
  token: { type: string, secret: true }
api:
  base_url: "{{ connection.url }}/api"
  auth: { type: bearer, token: "{{ connection.token }}" }
operations:
  claim.create:
    method: POST
    path: /claims
    class: App\Connector\CreateClaim
    record: warranty_claim

Trieda operácie dostane prihláseného klienta API, nikdy samotné heslo, a vracia výsledok pre každý záznam:

RecordOperation

interface RecordOperation
{
    public function checkOptions(array $options): void;

    /** @return list<RecordResult> one per record, in the same order */
    public function send(array $records, OperationContext $context): array;
}

Voliteľne: ComparesWithTarget pre compare_with: target, #[AsReferenceIndex] na kontrolu odkazov, #[AsTransport] a #[AsFormat] pre nové prenosy a formáty.

PHP hooky

Hook dostane jeden záznam a vráti ho, vráti niekoľko záznamov, alebo null, keď sa má preskočiť. K dispozícii má nastavenia, prevodové tabuľky, tabuľku krížových odkazov a logger, nikdy nie pripojenie.

src/Hook/SkipZeroStock.php

final class SkipZeroStock implements Hook
{
    public function __invoke(Record $record, HookContext $context): ?Record
    {
        return '0' === $record->get('quantity') ? null : $record;
    }
}

Vo flow ho zapíšete ako hooks: { before_send: App\Hook\SkipZeroStock }.

Vlastné typy záznamov

Behy, opakovania, porovnanie zmien aj skúšky naprázdno fungujú pre vlastné typy rovnako.

records/warranty_claim.yaml

schema_version: 1
record: warranty_claim
version: 1.0.0
key: [claim_no]
fields:
  claim_no:   { type: string, required: true }
  order_no:   { type: string, required: true }
  sku:        { type: string, references: product }
  created_at: { type: date }

Typy polí: string, decimal, int, bool, date, enum, object, list.

Správa od Deedholdu

Integrácie vám nastavíme a staging aj produkciu prevádzkujeme za pevný mesačný poplatok za každý napojený systém. Kód zostáva vám.