Add webhooks and webhook events (logs) to GraphQL
What does this MR do and why?
This MR adds ProjectHook
, GroupHook
and WebhookEvent
to GraphQL to take advantage of keyset pagination. Users can now query for webhook information and webhook events within the last 7 days on project and groups (group hooks are EE-only). These new queries set us up to be able to rewrite the webhooks recent events UI, path /-/hooks/:id/edit
, to also use keyset pagination and filter by timestamp range.
Notable implementation decisions:
- For now, only single
projectHook
andgroupHook
fields were implemented onProjectType
andGroupType
respectively. In the future,projectHooks
andgroupHooks
fields to return a connection or array may be implemented without affecting the corresponding fields using single resolvers. -
webHookEvents
are limited to being resolved for no more than one webhook per query to prevent N+1 queries.web_hooks_logs_daily
is a daily partitioned db table without an index forweb_hook_id
, so there's no great existing way to preload webhook events. Because there no real need to load webhook events for multiple webhooks at once, implementingextension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
seemed the best approach -
WebHookLogsFinder
already implemented filtering by response status, but there's no DB index on it, so the status filter wasn't exposed as an argument inWebHooks::EventsResolver
. It shouldn't be a breaking change to add it as an optional filter in the future once an index is added or we determine how to safely use it. - The
WebHookLog
model in Rails are exposed asWebhookEvents
in GraphQL because we already use the term "events" in the UI to refer to these records, and the typeEvents
already exists for theEvent
model. - Naming in general around webhooks in the UI and in the rails backend is bit inconsistent. For consistency in these new resources, I tried to stick as close to the original model name as possible within the
WebHooks
namespace. - Added a new input type,
Types::TimestampRangeInputType
to take advantage ofRangeInputType
similar toTypes::TimeframeInputType
but usingTypes::TimeType
instead ofTypes::DateType
. This allows web hook events to be filtered down to the second. - GraphQL types for
SystemHook
andServiceHook
were not added in this MR
References
- Resolves #504101
- Project webhook REST API docs and group webhook REST API docs for comparison to new queries
How to set up and validate locally
- Create a webhook on a group and project. Tip: https://webhook.site may be helpful to generate a working webhook URL.
- Generate webhook events on the hooks using the "test" button or performing actions in GitLab according to the triggers checked on the webhooks. Make the hook execute more than 20 times or lower
max_page_count
to see pagination working - Open the GraphQL explorer and query for
projectHook
/groupHook
andwebhookEvents
/webhookEvent
on aproject
orgroup
resource. Tip: Global IDs can be found in the rails console usingGitlabSchema.id_from_object(group_or_project_hook_object)
and timestamps must be in in ISO 8601 format (e.g."2025-09-24T00:00:00Z"
) - Verify the output matches the webhook and webhook log attributes, max webhook event size is respected, and no N+1 queries occur.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Related to #504101
Edited by Sam Word