feat(system_hooks): add seven fields GitLab sends to the Hook struct
What does this MR do?
API::Entities::Hook exposes twenty fields and the Hook struct carried thirteen, so seven were dropped on every system hook response. This adds them, each in the position the entity exposes it.
Every line below is read from gitlab-org/gitlab at commit e5d23f6d, the tip of master when this was written, rather than from a release pin.
| Field | Exposed at | Sent |
|---|---|---|
organization_id |
hook.rb:16 | when the record is a SystemHook, which is every record this service presents |
alert_status |
hook.rb:20 | always |
disabled_until |
hook.rb:21 | always |
push_events_branch_filter |
hook.rb:25 | always |
branch_filter_strategy |
hook.rb:26 | always |
custom_webhook_template |
hook.rb:28 | always |
custom_headers |
hook.rb:29 | unless the caller passes with_custom_headers: false, which no system hook route does |
One detail about custom_headers that the test records rather than assumes: the entity renders it through masked_custom_headers, which is custom_headers.keys.map { |k| { key: k } } in app/models/concerns/web_hooks/hook.rb, so each element carries the key alone and no value at all. The existing HookCustomHeader type is still the right one to decode into, since it is also the request type where the value does matter, and the example on the project webhooks documentation page shows the same key-only shape.
organization_id is the one that needs a sentence rather than a row. Its condition is ->(hook, _) { hook.is_a?(SystemHook) }, and lib/api/system_hooks.rb presents SystemHook records on every route that renders this entity, so for this struct it is effectively unconditional. The same entity is the parent of the project and group hook entities, where the field is absent, and those decode into ProjectHook and GroupHook, which correctly do not carry it.
Six of the seven are already modelled on ProjectHook and GroupHook with these exact names and types, so this closes a gap between the three hook structs rather than proposing a new shape. []*HookCustomHeader and HookURLVariable are reused as they stand.
The create and update routes both accept custom_webhook_template, declared in the hook_parameters block of lib/api/system_hooks.rb, so it is added to AddHookOptions and EditHookOptions as well. Without that half the struct could report a payload template it had no way to set.
I found this while developing an MCP server on top of this library, https://github.com/jmrplens/gitlab-mcp-server, which currently reads these seven keys out of the raw response beside the SDK decode and drops that workaround when this lands.
Is this a breaking change?
No. Seven additive fields on a response struct, and one additive optional field on each of two option structs. Nothing is renamed, retyped or removed.
It does change what a caller observes, which is the point: keys that previously decoded into nothing now decode into the value GitLab sent, so a caller that was reading the zero value of a field it did not have will start seeing real data.
How was this tested?
TestSystemHooksService_GetHook carries all seven keys in its fixture and asserts every decoded value, including the masked custom header and the parsed disabled_until timestamp.
TestSystemHooksService_AddHook asserts with testBodyJSON that custom_webhook_template reaches the request body, and that the same key decodes back off the response.
go build ./..., go test . and golangci-lint run ./... all pass locally, and gofumpt -l reports nothing on the changed files.
Related to #2300