Skip to content

Add support for the Global Profile API - #164

Draft
tholovina-sift wants to merge 2 commits into
masterfrom
atlas-382-global-profile-api
Draft

Add support for the Global Profile API#164
tholovina-sift wants to merge 2 commits into
masterfrom
atlas-382-global-profile-api

Conversation

@tholovina-sift

Copy link
Copy Markdown

Add support for the Global Profile API

Summary

Sift is launching a new "Global Profile API" that returns cross-tenant identity,
decision, chargeback, order, transaction, and location signals for a user. This PR
adds client support for both of its endpoints, following the existing
FieldSet / SiftRequest / SiftResponse conventions used throughout this SDK
(e.g. GetDecisionsRequest, GetMerchantRequest, ApplyDecisionRequest).

What changed

New endpoints

  • GET /v3/accounts/{accountId}/global_profile/users/{userId}
    • GlobalProfileFieldSet (src/main/java/com/siftscience/model/GlobalProfileFieldSet.java) —
      carries userId (path) plus optional globalOnly / includeOwnData query params.
    • GlobalProfileRequest / GlobalProfileResponse — GET request following the
      GetDecisionsRequest query-parameter pattern (only sets global_only /
      include_own_data on the query string when explicitly provided, letting the
      server apply its own defaults otherwise).
  • POST /v3/accounts/{accountId}/global_profile/lookup
    • GlobalProfileLookupFieldSet (src/main/java/com/siftscience/model/GlobalProfileLookupFieldSet.java) —
      JSON body with email / phone. Overrides validate() (same pattern as
      ApplyDecisionFieldSet) to throw MissingFieldException client-side if
      both are missing, so callers get a clear error before any network call.
    • GlobalProfileLookupRequest / GlobalProfileLookupResponse.
  • SiftClient gains two new buildRequest(...) overloads for the field sets
    above, matching the existing dispatch-by-type convention.

Response model

Both endpoints share GlobalProfileResponseBody (extends BaseResponseBody,
adds error_code, lookback_months, and nested objects), plus new supporting
model classes: GlobalProfileSummary, GlobalProfileIdentityAge,
GlobalProfileUserDecisions, GlobalProfileChargebacks, GlobalProfileOrders,
GlobalProfileTransactions, GlobalProfileLocations, and
GlobalProfileLocationAccount. All use the repo's @Expose/@SerializedName
Gson convention. When profile_summary.identity_found is false, all other
fields are null, as documented in the Javadoc on GlobalProfileResponseBody.

Tests

  • GlobalProfileTest — GET happy path with both query params set, GET with no
    optional params (verifies they're omitted from the URL), and an
    identity_found: false case verifying the other fields deserialize to null.
  • GlobalProfileLookupTest — POST lookup by email, POST lookup by phone
    (asserts request body), and a client-side validation test asserting
    MissingFieldException is thrown when neither email nor phone is set.

All tests use the existing MockWebServer + OkHttpUtils.urlRewritingInterceptor

  • JSONAssert pattern used by the rest of the test suite.

Docs

  • CHANGES.MD: new 3.23.0 entry (minor bump for the new feature).
  • build.gradle: version bumped to 3.23.0.
  • README.md: new "Global Profile API" section with usage examples for both
    endpoints, following the style of the existing "Decision Status API" section.

Error handling

No changes were needed to SiftRequest's error-handling switch — the
documented error codes (52 UNAUTHORIZED, 53 PERMISSION_DENIED /
ENTITLEMENT_REQUIRED, -4 RATE_LIMIT_EXCEEDED, 51 INVALID_API_KEY, 55
INVALID_PARAMETER, -1 SERVER_ERROR) are already mapped to the appropriate
SiftException subclasses there.

Testing done

  • Manually reviewed all new/changed files against the existing patterns in
    GetDecisionsRequest/GetDecisionFieldSet, ApplyDecisionRequest/
    ApplyDecisionFieldSet, GetMerchantRequest, SiftRequest, SiftResponse,
    FieldSet, and BaseResponseBody to keep naming, serialization, and
    validation conventions consistent.
  • Added new unit tests (GlobalProfileTest, GlobalProfileLookupTest)
    covering both endpoints, optional query params, the identity_found: false
    response shape, and client-side validation of the lookup endpoint's
    required-field rule.
  • Could not run ./gradlew build/./gradlew test in this sandbox: the
    Gradle wrapper needs to download the Gradle 8.14 distribution and
    dependencies over the network, which is unavailable here. No compiler
    errors were spotted during manual review, but a real CI run (e.g. via
    /jenkins-test on the PR) is recommended before merge.

tholovina-sift and others added 2 commits August 18, 2026 21:34
Co-Authored-By: Claude <noreply@anthropic.com>
- Add validate() to GlobalProfileFieldSet throwing MissingFieldException when userId is null/empty (P1)
- Type GlobalProfileRequest constructor parameter as GlobalProfileFieldSet instead of raw FieldSet (P2)
- Add @Expose/@SerializedName to GlobalProfileFieldSet fields so fromJson() deserializes correctly (P3)
- Capture return value and add is_ok()/identity_found assertions to testGlobalProfileLookupByPhone (P4)
- Add testLookupIdentityNotFound covering the identity_found=false Lookup response path (P5)
- Add assertNull assertions for chargebacks/orders/transactions/locations/lookbackMonths in testIdentityNotFound (P6)
- Add Authorization header assertion to testGetGlobalProfileWithoutOptionalParams (P7)
- Fix pre-existing test failures: remove null-valued fields from JSONAssert comparison bodies so they match toJson() output (Gson omits nulls by default)

Co-Authored-By: Claude <noreply@anthropic.com>
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