Webhook fogadó JSON logolással: hookrelay

A werkstatt sorozat hetedik része – egy HTTP szerver ami webhook-okat fogad és strukturált JSON log fájlba ment.

Webhook fogadó JSON logolással: hookrelay

A Gitea-m tud webhook-ot küldeni minden push-nál. A kérdés az volt: hova küldje? Egy egyszerű szerver kell ami fogadja a payload-ot és elmenti – nem kell feldolgozni, nem kell továbbítani, csak logolni.

A hookrelay pontosan ennyi. Két végpont: /health az életjelhez, /webhook a payload fogadásához. Minden bejövő webhook egy JSON sor a log fájlban.

POST validáció

A stackctl-ben minden végpont GET volt. A hookrelay-nél a /webhook csak POST-ot fogad el – ez az első projekt ahol a HTTP metódust is ellenőrizni kell:

if r.Method != http.MethodPost {
    http.Error(w, "POST only", http.StatusMethodNotAllowed)
    return
}

A http.StatusMethodNotAllowed a 405-ös HTTP kód. A Go net/http csomagja minden standard kódhoz ad konstanst – nem kell fejből tudni a számokat.

JSON dekódolás

A bejövő payload-ot a json.NewDecoder olvassa be közvetlenül a request body-ból:

var hook Webhook
err := json.NewDecoder(r.Body).Decode(&hook)

Ez elegánsabb mint a teljes body beolvasása és utána az Unmarshal – a decoder stream-ből dolgozik, nem tölti be az egészet a memóriába. Kis payload-oknál nincs különbség, de a minta jó.

A production ready verzióban az event mező is ellenőrzésre kerül – ha üres, 400-as hibát kap a küldő. Az eredeti verzió ezt nem csinálta.

Fájlba írás append módban

A log fájlt os.OpenFile-lal nyitom append módban:

f, _ := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)

A három flag együtt: ha nem létezik a fájl, létrehozza. Ha létezik, a végére ír. Csak írásra nyitja meg. Ez pontosan az amit bash-ben a >> operátor csinál.

Minden webhook egy JSON sor – ez a JSON Lines formátum. Könnyű olvasni jq-val, könnyű grep-pelni, és könnyű feldolgozni.

A logger interface

A deployer-nél és az sshping-nél már tanultam az interface-alapú tesztelést. A hookrelay-nél a logger interface absztrahálja a fájlba írást:

type logger interface {
    save(hook Webhook) error
}

A valódi fileLogger fájlba ír. A teszt mockLogger egy slice-ba gyűjti a webhook-okat. Így a HTTP handler tesztelhető anélkül hogy fájlrendszert érintene – és a fileLogger-t külön teszteljük temp fájlokkal.

Tesztek

A hookrelay tesztjei kombináció: httptest a handler-ekhez, mock logger a webhook feldolgozáshoz, temp fájlok a fileLogger-hez. Ez lefedi a teljes működést: valid payload, hibás JSON, rossz metódus, hiányzó mező, fájlba írás, append viselkedés.

Mit tanultam?

Három új elem az eddigi projektekhez képest:

A POST metódus kezelés és validáció – nem minden request egyforma, és a szerver feladata elmondani mit fogad el.

A JSON Lines formátum – egyszerű, stream-elhető, grep-elhető logolás. Egy sor egy esemény.

Az os.OpenFile flag-ek – hogyan nyiss fájlt pontosan úgy ahogy kell: append, create, write-only. Ez a Go-ban explicit, bash-ben a >> mögé van rejtve.

Hogyan próbáld ki?

git clone https://github.com/brtkcs/werkstatt-tools.git
cd werkstatt-tools/hookrelay
go run main.go

Egy másik terminálból:

curl -X POST localhost:8080/webhook \
  -d '{"event":"push","repo":"werkstatt","branch":"main"}'

cat webhooks.log

A teljes forráskód a werkstatt-tools repóban.

Ez a werkstatt sorozat hetedik része. A stackctl rendszer infót szolgáltatott HTTP-n. A hookrelay a másik irány – nem adatot ad ki, hanem adatot fogad: webhook payload-okat, amiket JSON lines formátumban ment.