Skip to content

feat(users): preserve handles and role assignments via v1#635

Open
michael-richey wants to merge 7 commits into
mainfrom
users-handle-mapping-key
Open

feat(users): preserve handles and role assignments via v1#635
michael-richey wants to merge 7 commits into
mainfrom
users-handle-mapping-key

Conversation

@michael-richey

@michael-richey michael-richey commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

What

Preserve distinct user handles during sync by mapping users by exact-case handle and optionally creating them through the v1 user API. Reconcile role assignments after creation and on later retries without reporting partial results as full success.

Why

Email is not necessarily unique, but the v2 create endpoint derives the destination handle from email. Distinct source users that share an email can therefore collide on one destination handle. The v1 endpoint accepts an explicit handle.

Role assignment is a separate API operation. A user can be created or updated successfully while one or more role assignments fail, so those partial outcomes must remain retryable and observable.

Changes

  • Map users by attributes.handle instead of email, preserving exact case.
  • Keep handle out of v2 create and update payloads and exclude it from update diffs.
  • Add the opt-in --use-v1-user-api flag and DD_USE_V1_USER_API environment variable; both default off.
  • Create flagged users through POST /api/v1/user, then resolve the v2 UUID by exact-case handle for destination state and downstream references.
  • Assign only missing mapped roles after v1 creation and during later updates.
  • Continue attempting remaining roles after an assignment fails.
  • Persist the reconciled user, successful role assignments, and successful attribute updates before raising a structured partial failure.
  • Route partial role failures through the existing failed-resource counter, failure event, and failure metric instead of emitting success.
  • Document the flag in the README.

Compatibility

With the flag disabled, user creation continues to use v2. Existing destination state produced by earlier handle collisions is not rewritten automatically; affected state must be cleaned before enabling the v1 path.

Tests

  • Exact-case handle mapping and shared-email users.
  • v1 and v2 create paths, payload filtering, UUID reconciliation, and update behavior.
  • Existing and missing role assignments.
  • Partial role failure during initial creation.
  • Second-run role retry with later roles still attempted, successful state persisted, and a structured failure raised.
  • Flag configuration and CLI parsing for sync and migrate.

@michael-richey
michael-richey marked this pull request as ready for review July 17, 2026 16:22
@michael-richey
michael-richey requested a review from a team as a code owner July 17, 2026 16:22
@michael-richey
michael-richey force-pushed the users-handle-mapping-key branch 3 times, most recently from 8523e02 to f54b846 Compare July 17, 2026 18:20
@michael-richey michael-richey changed the title feat(users): identify users by handle instead of email feat(users): key users by handle and add --use-v1-user-api Jul 17, 2026
Users were identified and linked by email, but the destination enforces
user uniqueness on the handle (email is not unique). Two source users
that share an email but have distinct handles collapsed onto one derived
handle — one user was mislabeled and the other could not be created.

- Identify/link users by their exact-case handle: switch
  resource_mapping_key to attributes.handle. Keep handle out of the
  read-only v2 create/update payload (pop before POST/PATCH) and out of
  update diffs (exclude_regex_paths), while retaining it for mapping and
  v1 creation.
- Add an opt-in --use-v1-user-api flag (default off; env
  DD_USE_V1_USER_API). When set, create users via POST /api/v1/user,
  which accepts an explicit handle, so every user keeps its own handle
  and no create collides. The v1 response is the legacy shape, so
  re-resolve the v2 UUID by exact-case handle (small sleep-free re-query
  loop for read-after-write visibility); failures re-raise so the apply
  loop continues (DR-safe). With the flag off, creation is unchanged (v2).
- Document the flag in the README; add pytest-cov to test extras.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@michael-richey
michael-richey force-pushed the users-handle-mapping-key branch from f54b846 to 803dcf2 Compare July 17, 2026 18:49
@michael-richey
michael-richey marked this pull request as draft July 17, 2026 19:16
@michael-richey
michael-richey removed the request for review from a team July 17, 2026 19:40
@michael-richey michael-richey changed the title feat(users): key users by handle and add --use-v1-user-api feat(users): preserve handles and role assignments via v1 Jul 17, 2026
@michael-richey
michael-richey marked this pull request as ready for review July 17, 2026 20:02
@michael-richey
michael-richey requested a review from Copilot July 17, 2026 20:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds an opt-in v1 user-creation path to preserve exact-case user handles (avoiding shared-email collisions), switches user mapping to be handle-based, and makes role reconciliation retryable/observable via structured partial failures.

Changes:

  • Map existing/users-by-handle (attributes.handle) and exclude handle from v2 create/update payloads and diffs.
  • Add --use-v1-user-api / DD_USE_V1_USER_API to create users via POST /api/v1/user, then reconcile the v2 UUID by exact-case handle.
  • Reconcile only missing role assignments, continue after per-role failures, persist partial state, and raise a structured UserRoleAssignmentError.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
datadog_sync/model/users.py Handle-based mapping, optional v1 create flow, role reconciliation + partial failure error type, v2 payload filtering/diff exclusion.
datadog_sync/utils/configuration.py Adds use_v1_user_api to configuration and wiring from CLI kwargs.
datadog_sync/commands/shared/options.py Introduces --use-v1-user-api CLI option + env var support.
datadog_sync/constants.py Adds DD_USE_V1_USER_API constant.
README.md Documents the v1 user creation flag and the shared-email handle-collision rationale.
tests/unit/test_users.py New unit tests covering handle mapping, v1/v2 create paths, UUID reconciliation, role assignment behavior, and flag wiring.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread datadog_sync/model/users.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants