Extend Gitlab::Kas::Client to support events_platform Publish and Subscribe
## Summary
Extend the existing `Gitlab::Kas::Client` class
([`lib/gitlab/kas/client.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/kas/client.rb))
to support the `events_platform` gRPC service. This adds `Publish` and
`Subscribe` capabilities so Rails can produce and consume CloudEvents
through GitLab Relay (KAS).
## Why this approach (and not a separate gem)
The original plan was to build a new Ruby gem. After review extending `Gitlab::Kas::Client` is a better fit:
- **Pattern is established.** The class already wraps gRPC stubs for AutoFlow,
Notifications, ConfigurationProject, ManagedResources, etc. Adding
events_platform follows the same pattern.
- **Auth, TLS, JWT, and timeouts are already handled.** Reused as-is.
- **Lower risk and faster delivery** than packaging a new gem.
- **Doesn't preclude extraction.** If a non-Rails Ruby caller emerges,
the code can move to `vendor/gems/` or a separate repo.
## Scope
### 1. Register the events_platform stub
Add an entry to `STUB_CLASSES` in `Gitlab::Kas::Client`:
```ruby
events_platform: Gitlab::Agent::EventsPlatform::Rpc::EventsPlatform::Stub
```
(Verify the Ruby protobuf stubs are exported from gitlab-agent. AutoFlow's
stubs are vendored — same export mechanism should cover events_platform.)
### 2. `publish_event` method
Single method that wraps the unary `Publish` RPC. Follows the existing
pattern (see `send_autoflow_event` for reference).
```ruby
def publish_event(topic:, event:)
request = Gitlab::Agent::EventsPlatform::Rpc::PublishRequest.new(
topic: topic,
events: [event]
)
stub_for(:events_platform)
.publish(request, metadata: metadata)
.message_ids
end
```
### 3. `subscribe_events` method (bidi streaming)
Block-based API that hides the bidi protocol details:
```ruby
client.subscribe_events(
topic: 'gitlab.external',
consumer_group: 'gitlab-rails'
) do |event|
# If this block raises, no ack — message will be redelivered.
# If it returns successfully, ack is sent automatically.
Gitlab::EventStore.publish(translate(event))
end
```
Implementation handles:
- Sending `SubscribeConfig` as the first message
- Receiving `SubscribeResponse` events
- Sending `Ack` messages back on the same stream after successful block return
- Reconnecting on stream break (or failing fast — TBD during implementation)
### 4. Tests
- Unit tests for `publish_event` (mock the stub)
- Unit tests for `subscribe_events` block lifecycle (mock the bidi stream)
- Integration test if feasible (against a local KAS in CI)
## Out of scope
- The external producer endpoint and PAT/OAuth auth (out of scope until
the external API is designed).
## References
- [Subscribe Contract design doc (!19605)](https://gitlab.com/gitlab-com/content-sites/handbook/-/merge_requests/19605) — defines the gRPC contract this implements
- [Existing `Gitlab::Kas::Client`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/kas/client.rb) — pattern to follow
- Parent epic: https://gitlab.com/groups/gitlab-org/-/work_items/21346
issue