Skip to content

fix(dataplane): reject events without a delivery target and honor app_id fanout#2746

Merged
mekilis merged 3 commits into
mainfrom
smart/pde-928-events-post-events-without-endpoint_id-silently-queues-and
Jul 20, 2026
Merged

fix(dataplane): reject events without a delivery target and honor app_id fanout#2746
mekilis merged 3 commits into
mainfrom
smart/pde-928-events-post-events-without-endpoint_id-silently-queues-and

Conversation

@mekilis

@mekilis mekilis commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • POST /v1/projects/{id}/events with neither endpoint_id nor the deprecated app_id alias previously returned 201 and queued an event the worker could never deliver; the request is now rejected with 400 at validation time.
  • app_id is resolved against project-scoped endpoints and rejected with 400 when it resolves to none, mirroring the existing endpoint_id existence check, and it is now forwarded to the worker task so accepted app_id requests actually resolve endpoints.
  • CreateSubscription is set unconditionally for this handler, so app_id addressing auto-provisions catch-all subscriptions the same way endpoint_id does.
  • The worker's findSubscriptions returned early after provisioning the first subscription-less endpoint, so multi-endpoint app_id fanout delivered to only one endpoint; it now continues so every resolved endpoint gets its catch-all subscription.

Test plan

  • go test ./api/models/ (validation: missing target rejected, endpoint_id-only and app_id-only accepted)
  • go test ./api/ integration tests (400 on missing target, 400 on app_id with no endpoints, app_id fanout delivers to all endpoints)
  • go test ./worker/task/ (multi-endpoint provisioning continues past the first subscription-less endpoint)
  • gofmt, go vet, golangci-lint clean on touched packages

Fixes PDE-928


Note

Medium Risk
Changes event intake validation (clients that omitted targets no longer get 201) and worker subscription resolution for multi-endpoint app_id fanout, both on the core outbound event path.

Overview
POST /v1/projects/{id}/events no longer accepts requests with neither endpoint_id nor deprecated app_id: validation returns 400 (please provide an endpoint ID) instead of queuing undeliverable events.

For app_id addressing, the handler now mirrors endpoint_id checks: it resolves endpoints in the project and returns 400 when none exist (app ID has no configured endpoints). Accepted requests pass AppID into the create-event worker task, always set CreateSubscription: true, and auto-provision catch-all subscriptions for subscription-less endpoints the same way as direct endpoint targeting.

In the worker, findSubscriptions no longer stops after the first subscription-less endpoint when auto-provisioning; it continues so multi-endpoint app_id fanout provisions catch-all subscriptions and delivery for every resolved endpoint, not just the first.

Reviewed by Cursor Bugbot for commit caa1730. Bugbot is set up for automated code reviews on this repo. Configure here.

@linear

linear Bot commented Jul 20, 2026

Copy link
Copy Markdown

PDE-928

@cursor

cursor Bot commented Jul 20, 2026

Copy link
Copy Markdown

Current version of PR was reviewed by /review-bugbot on Jul 20, 15:56 GMT+1. It flagged 0 findings.

Bugbot on commit 18d60f4 is skipped.

@mekilis
mekilis enabled auto-merge July 20, 2026 15:48
mekilis added 3 commits July 20, 2026 16:51
POST /v1/projects/{id}/events previously accepted a body with no
endpoint_id (and no deprecated app_id) with 201 and queued the event;
the worker then failed to resolve any endpoint and nothing was
delivered, with no signal to the caller.

the create endpoint event request must now name its delivery target at
validation time: endpoint_id, or the deprecated app_id alias. requests
naming neither are rejected with 400. an app_id that resolves to no
endpoints is also rejected with 400, mirroring the existing endpoint_id
existence check, and app_id is now forwarded to the worker task so an
accepted app_id request actually resolves endpoints instead of being
dropped silently.
CreateEndpointEvent forwarded app_id to the worker but still gated
CreateSubscription on endpoint_id, so a subscription-less endpoint
delivered when addressed by endpoint_id yet matched no subscriptions
when addressed via the deprecated app_id alias. both addressing modes
resolve to concrete endpoints and validation guarantees a resolvable
target before enqueue, so the flag is now always true for this handler.
fanout keeps its owner_id semantics unchanged.
findSubscriptions returned early after auto-provisioning a catch-all
subscription for the first subscription-less endpoint, skipping every
remaining endpoint on the event. single endpoint_id addressing never
hit this, but app_id addressing resolves multiple endpoints, so only
one endpoint received a delivery while the rest were silently dropped.
the loop now continues so each subscription-less endpoint gets its
catch-all subscription and endpoints with existing subscriptions are
still matched.
@mekilis
mekilis force-pushed the smart/pde-928-events-post-events-without-endpoint_id-silently-queues-and branch from 18d60f4 to caa1730 Compare July 20, 2026 15:52
@mekilis
mekilis merged commit 4b50de9 into main Jul 20, 2026
36 checks passed
@mekilis
mekilis deleted the smart/pde-928-events-post-events-without-endpoint_id-silently-queues-and branch July 20, 2026 16:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant