- entity: (*Event).AttachGeoIP(geoip.Lookup) sets Geo* nullable
pointer fields with overwrite semantics (empty/zero collapses to
nil so DB column stays NULL). Single-arg signature matches the
call shape in design spec §9 (line 1437) and keeps the helper
symmetric with the geoip.Lookup value type.
- service: ServiceConfig.GeoIP geoip.Lookuper plumbed through to
the Service.geo field. Record now calls enrichGeo(evt) BEFORE
s.repo.Insert so geo_country/region/city/asn/asn_org persist in
the same row. enrichGeo is nil-safe on three axes (geo, evt,
evt.SourceIP) — geo enrichment is best-effort, never an error
path.
- service_test: fakeLookuper + newSvcWithGeo helper; five new
tests (enriches-before-insert, no-geo-leaves-nil, empty-IP-
skips-lookup, NopService-leaves-nil, best-effort-when-insert-
fails). All pre-existing tests pass unchanged because GeoIP
defaults to nil.
- entity_test: seven AttachGeoIP unit tests covering populate-all,
empty Lookup, partial fields, zero/negative ASN guards, overwrite
semantics, and ASN-pointer independence from the input value.
- integration_test: two real-Postgres tests (stub Lookuper writes
populated geo_* columns; NopService leaves them NULL) verify
the wiring round-trips through the Insert SQL.
Adds the two service primitives Phase 11's manage handler needs.
- event.Service.CountActiveDedup(ctx, tokenID): Redis SCAN over
dedup:trigger:{tokenID}:* in batches of 100, sums (value-1) for each
key (value=1 = first trigger fired notification, value=N>1 = N-1
silenced). Returns 0 when no rdb is configured.
- token.Service.DeleteByManageID: thin wrapper around
Repository.DeleteByManageID; ServiceRepository interface gains the
method so test fakes can implement it (fakeRepo updated). Returns
ErrNotFound on no-rows.
Tests cover: empty pattern returns 0, first-trigger-only returns 0
(notify fired, nothing silenced), aggregation across IPs sums correctly,
other tokens' keys are excluded by the prefix match, nil rdb returns 0.