-
Notifications
You must be signed in to change notification settings - Fork 0
feat(platform): add Reddit Ads client (OAuth2 + token refresh) #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 39 commits
Commits
Show all changes
46 commits
Select commit
Hold shift + click to select a range
2624ca2
feat(platform): add Reddit Ads client (OAuth2 + token refresh)
mrautela365 c15f6f5
fix(review): address Copilot findings on Reddit client (PR #21)
mrautela365 d6ec469
fix(review): budget overflow cap, start-vs-end guard, normalized camp…
mrautela365 fcd7f27
fix(review): validate registration URL, surface body-read errors, pin…
mrautela365 1b42b21
refactor(reddit): extract magic literals to named constants (PR #21)
mrautela365 172c3b4
fix(review): harden Reddit URL/budget/id handling and tests (PR #21 r…
mrautela365 54fddfe
fix(review): surface read errors, validate account id, fix encoded-sl…
mrautela365 30be5c5
fix(review): validate account id charset, anchor post-id, make token-…
mrautela365 6ce20a5
fix(review): use canonical tlf slug, scope community warning, fix bud…
mrautela365 49981a7
fix(review): anchor post-path regex and validate ISO geo codes (PR #2…
mrautela365 5c403b2
fix(review): coalesce token refresh, trim ids, match escaped path (PR…
mrautela365 37e40b9
fix(review): re-check cache inside token single-flight; escape redd.i…
mrautela365 700c9db
fix(review): coalesce token refresh with stdlib only, drop x/sync (PR…
mrautela365 6665fd7
fix(review): share token-refresh result across waiters; leader honors…
mrautela365 435954e
fix(review): accept scheme-less Reddit post URLs (PR #21 round 11)
mrautela365 d73ae70
fix(review): ctx-cancel during /ads is fatal; make failure-shared tes…
mrautela365 d55f054
fix(review): surface partial state when ad-group creation fails after…
mrautela365 5ce79b7
feat(reddit): add 429 rate-limit retry with bounded backoff (PR #21)
mrautela365 51c5f08
fix(review): allocation-free truncate; race-safe test flags (PR #21)
mrautela365 4ae520c
fix(review): drain 429 body, bound token/Retry-After overflow, partia…
mrautela365 ace36cd
fix(review): allow at-cap Retry-After; fix past-start nudge wording (…
mrautela365 e0b5424
docs(okf): add recommended tags and timestamp frontmatter (PR: reddit…
mrautela365 bd9d643
fix(review): report raw Retry-After in over-cap abort; dated OKF log …
mrautela365 06b5e07
fix(review): reject empty EventName up front (PR #21)
mrautela365 ee03a08
fix(review): conversion pixel, video goal, retry-safe start (PR #21)
mrautela365 27e7dcc
fix(review): gate conversion_pixel_id on objective (PR #21)
mrautela365 c9b080a
fix(review): resolve subreddit names to IDs for targeting (PR #21)
mrautela365 675094a
fix(review): reject URL userinfo; align api-catalog on ID resolution
mrautela365 b492a09
fix(review): document partial-result contract; de-flake cancel test
mrautela365 8b4fb7a
fix(review): use /targeting/subreddits, resolve before campaign POST
mrautela365 443830c
fix(review): case-insensitive r/ strip, dedup, redact URL, de-flake
mrautela365 51e4e6b
fix(review): send subreddit names not IDs; require Project (PR #21)
mrautela365 4fc6ed7
fix(review): redact URLs in Steps and post-ID errors (PR #21)
mrautela365 cdc8434
fix(review): AdWarning field, redact validate errors, size start buff…
mrautela365 ba6f360
fix(review): allowlist UTM keys, redact all URL errors, horizon-nudge…
mrautela365 0a8b155
fix(review): gate community fallback on HTTP 400; status-only ad-fail…
mrautela365 36ad05f
fix(review): reject malformed query; never echo unparseable URL creds…
mrautela365 a411c1d
fix(review): partial on no campaign id; no OAuth body in errors; unco…
mrautela365 1a85ebd
fix(review): per-attempt timeout; UNCONFIRMED ambiguous creates; adgr…
mrautela365 b4c936b
fix(review): only UNCONFIRMED a campaign create that actually reached…
mrautela365 0cfae41
fix(review): ctx cancel during verify aborts, not UNCONFIRMED (PR #21)
mrautela365 770d893
fix(review): fix secretlint CI; validate EventName/Project length up …
mrautela365 0f5c218
fix(review): unify create-ambiguity classification; enforce composed …
mrautela365 c6dc0ea
fix(review): validate composed ad-group name before the campaign POST…
mrautela365 bc5f1ee
fix(review): preserve trace ctx on token refresh; precise budget/ad-f…
mrautela365 b72fffc
test(review): cover composed campaign-name limit + token-refresh canc…
mrautela365 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| --- | ||
| type: "Go Package" | ||
| title: "internal/platform/reddit" | ||
| description: "Reddit Ads API v3 client: OAuth2 token refresh and Campaign -> Ad Group -> Ad creation." | ||
| resource: "internal/platform/reddit" | ||
| tags: | ||
| - platform-client | ||
| - reddit-ads | ||
| - oauth2 | ||
| - go-package | ||
| timestamp: "2026-07-13T23:55:00Z" | ||
| --- | ||
|
|
||
| # internal/platform/reddit | ||
|
|
||
| Package reddit provides a Go client for the Reddit Ads API v3, porting the | ||
| upstream TypeScript `reddit-ads.service.ts` client. Credentials and account | ||
| configuration are injected via `NewClient`; the client never reads the process | ||
| environment. | ||
|
|
||
| Authentication uses OAuth 2.0 refresh-token exchange with a cached access token | ||
| and an expiry buffer (refresh shortly before the stated expiry). The token | ||
| endpoint and API base URL are overridable via functional options for `httptest`. | ||
|
|
||
| `CreateCampaign` drives the Campaign -> Ad Group -> Promoted Post (Ad) hierarchy, | ||
| creating everything PAUSED with a lifetime budget and objective-aware bid params. | ||
| It normalizes geo targets once (trimmed, uppercased) so the ad-group label, | ||
| targeting, and region derive from a single source of truth, and computes the | ||
|
mrautela365 marked this conversation as resolved.
|
||
| start time up front so a same-day (past midnight-UTC) start is nudged to | ||
| now+buffer before the campaign POST. Post URLs are validated by parsing the URL | ||
| authority (`reddit.com`/`redd.it` and subdomains only) to prevent host spoofing, | ||
| and UTM parameters are merged into the URL query while preserving any fragment. | ||
|
|
||
| Supplied subreddit names (`r/golang` or `golang`) are sent to the ad-group | ||
| `communities` targeting field as NAMES with the `r/` prefix stripped (and | ||
| case-insensitive duplicates removed) -- Reddit's Ads API targets communities by | ||
| name, not `t5_` ID, and rejects `t5_` values as "invalid communities". This | ||
| matches the reference TS implementation, which sends the stripped names | ||
| directly. If the ad-group create returns a 400 "invalid communities" the client | ||
| retries once WITHOUT communities (keyword/geo-only) and emits a | ||
| communities-skipped warning step, so an invalid subreddit never orphans the | ||
| PAUSED campaign. | ||
|
|
||
| See [internal/platform/reddit](../../../internal/platform/reddit). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.