Skip to content

Add OCI Notifications: topics and subscriptions - #421

Open
arunesh-j wants to merge 1 commit into
developmentfrom
feat/oci-notifications
Open

Add OCI Notifications: topics and subscriptions#421
arunesh-j wants to merge 1 commit into
developmentfrom
feat/oci-notifications

Conversation

@arunesh-j

@arunesh-j arunesh-j commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Implements OCI Notifications (ONS) against the existing portable notification driver.
  • Topics, subscriptions with the real PENDING → ACTIVE confirmation flow, and message publishing.
  • Nothing added to services/notification/driver — OCI-only behaviour is a consumer-side Extras interface, per the rule set in Move OCI-only capabilities out of shared driver packages #393.

Closes #415. Part of #376.

Changes

  • providers/oci/notifications/Mock over memstore implementing driver.Notification, guarded by a sync.RWMutex.
  • server/oci/notifications/ — the /20181201/ surface. Topics: Create/Get/List/Update/Delete/ChangeCompartment. Subscriptions: Create/Get/List/Update/Delete/Confirm/Unsubscribe/ResendConfirmation/ChangeCompartment. Data plane: PublishMessage.
  • Wiring is one line each in providers/oci/oci.go and server/oci/oci.go.

The confirmation flow

CreateSubscription mints an OCID, sets lifecycleState: PENDING and a confirmation token. Real ONS mails the token to the endpoint; the emulator has no channel, so the token rides back in the response body and is dropped once the subscription is ACTIVE. GET /subscriptions/{id}/confirmation validates token and protocol, flips to ACTIVE, and returns an unsubscribeUrl pointing at this emulator's own origin.

PublishMessage skips anything not ACTIVE, so a publish to a PENDING subscription delivers to nobody — asserted at both driver and wire layers, and visible in the transcript below.

Two corrections to the issue as written

  1. The prefixes claim was wrong. OCI Notifications: topics and subscriptions #415 says the control and data planes sit under different API prefixes. They don't — real ONS splits them by host (the topic's apiEndpoint), both on /20181201. Implemented as POST /20181201/topics/{id}/messages, with every topic reporting the requesting origin as its apiEndpoint, so an SDK pointed at the returned endpoint lands back on the same listener.
  2. DeleteTopic returns 204, not 202. Real ONS answers 204 with an opc-work-request-id. docs/oci-conventions.md's generic 202 is the common case, not ONS's. The work request is still recorded and pollable.

Never accept-and-ignore

Refused with a named 400 rather than silently dropped: message attributes (ONS has no such field), definedTags, protocols outside the ONS set, unknown messageType, unsupported sortBy/sortOrder. Protocol aliases (email, httpsCUSTOM_HTTPS) are mapped, not dropped.

Provider Coverage

  • AWS
  • Azure
  • GCP
  • OCI

Checklist

  • All tests pass (go test ./...) — see note below
  • Linter passes (golangci-lint run --timeout=9m) — 0 issues
  • Every provider the change applies to implements the same behavior — OCI-only, additive
  • Integration tests added to cloudemu_test.go — driver + handler tests instead
  • Unit tests added to provider test files

Test Plan

go build ./...                                              clean
go test ./...                                               271 ok
go test -race ./providers/oci/... ./server/oci/...          11/11 ok
golangci-lint run --timeout=9m ./providers/oci/... ./server/oci/...   0 issues
go generate ./...                                           docs/coverage committed

Correction on a suite note. An earlier run of this branch showed cmd/cloudemu TestServeOutOfProcess failing, and I first attributed it to the sandbox. That was wrong. It is contention on the shared ~/.cloudemu daemon lock between the six Wave 2 worktrees running their suites in parallel — self-inflicted, not a repo bug. Re-run with nothing else running, the whole suite is exit 0 including that test.

Coverage leak check clean: no OCI operation appears in docs/coverage/{aws,azure,gcp}/*.md, and git diff development -- services/ is empty.

End-to-end on a running server (port 4616):

create topic                        -> 200, apiEndpoint = requesting origin
create subscription                 -> 200, lifecycleState PENDING + confirmationToken
publish BEFORE confirmation         -> 200 accepted, delivered to nobody
list                                -> still PENDING
confirm with token                  -> 200 + unsubscribeUrl
get subscription                    -> ACTIVE, token no longer echoed
publish AFTER confirmation          -> 200, delivered
list topics without compartmentId   -> 400 InvalidParameter
delete topic                        -> 204 + Opc-Work-Request-Id
list topics / subscriptions         -> [] , []  (cascade)
poll work request                   -> SUCCEEDED, DELETE_TOPIC, resources[0] DELETED

Left out

No oci-go-sdk compat test — the SDK splits ONS across two clients with a host override, needing more scaffolding than it would buy; wire shapes are asserted field-by-field instead. Publishing records deliveries rather than pushing to real HTTPS/email endpoints, matching how providers/aws/sns handles non-SQS protocols. definedTags is refused rather than modelled, consistent with the rest of the OCI surface.

Implements OCI Notifications against the portable notification driver.

providers/oci/notifications: a Mock over memstore.Store guarded by a single
RWMutex. Topics carry an ocid1.onstopic OCID, a compartment recorded at create
and filtered on every list, a short topic id, lifecycle state, etag and
creation time. Subscriptions carry an ocid1.onssubscription OCID and start
PENDING with a confirmation token; a publish reaches only ACTIVE ones, so an
unconfirmed endpoint receives nothing. Deleting a topic takes its subscriptions
with it, as ONS does.

server/oci/notifications: the /20181201 wire handler for topics,
subscriptions, the two token-authenticated confirmation endpoints, the
changeCompartment and resendConfirmation actions, and PublishMessage on the
topic's own endpoint. Real ONS splits control and data plane by host rather
than by prefix, so a topic reports the requesting origin as its apiEndpoint and
a publish lands back on the same listener. DeleteTopic is asynchronous: it
records a work request and answers 204 with opc-work-request-id.

The OCI-only surface — subscription compartments, tags, metadata, delivery
policy, the confirmation handshake and a topic's lifecycle state — is declared
consumer-side as an Extras interface in the handler, with its value types in
the provider. A driver that does not satisfy it is served 501. Nothing was
added to services/notification/driver.

Input the emulator cannot honour is refused rather than stored unused: message
attributes, defined tags, protocols outside the ONS set, unknown message types
and unsupported sort keys all answer 400 naming what is unsupported.
@arunesh-j arunesh-j added the oci Oracle Cloud Infrastructure label Aug 21, 2026

@NitinKumar004 NitinKumar004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review notes

Real data-plane engine (per #427): N/A.

Findings

Medium · docs — docs/services.md not updated with OCI Notifications — unmet Definition-of-done checkbox
docs/services.md:23
If a user consults docs/services.md (the human-facing service catalog) to learn what OCI surface CloudEmu emulates -> they find no ONS topics/subscriptions/publish entry and conclude the service is unimplemented, because the vertical slice's documentation step was skipped even though the code and generated coverage exist.

git diff merge-base..HEAD shows docs/services.md untouched; only auto-generated docs/coverage/* changed. oci-conventions.md 'Definition of done' requires 'Operations added to docs/services.md', and sibling OCI services each have a hand-written section (OCI Monitoring line 623, OCI identity line 749, OCI VCN note line 407). Notifications has none.

Low · structure — Per-feature filename not mirrored across provider/wire layers for topics and publish
providers/oci/notifications/publish.go:1
If a maintainer greps for the publish feature by filename (publish.go) -> they find only the provider side and miss that the wire implementation lives in topics.go, because the feature file name is not mirrored on the wire layer; a navigation cost, not a behavioral defect.

STRUCTURE.md §3: 'A feature uses the same filename across all three layers.' subscriptions.go matches (provider+wire), but topic CRUD is provider notifications.go vs wire topics.go, and publish is provider publish.go vs wire (folded into topics.go). provider notifications.go is defensible as the §4 .go core-CRUD file, but publish.go/topics.go breaks the one-grep goal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

oci Oracle Cloud Infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OCI Notifications: topics and subscriptions

2 participants