Skip to content

feat(bitbucket): add Data Center review state writes - #1746

Open
KatalKavya96 wants to merge 1 commit into
apache:mainfrom
KatalKavya96:feat-bitbucket-datacenter-review-state
Open

KatalKavya96 wants to merge 1 commit into
apache:mainfrom
KatalKavya96:feat-bitbucket-datacenter-review-state

Conversation

@KatalKavya96

Copy link
Copy Markdown
Contributor

Summary

  • Add Bitbucket Data Center support for pull-request approval and change-request state writes.
  • Reuse the existing backend-neutral commands:
    • pr approve
    • pr unapprove
    • pr request-changes
    • pr remove-request-changes
  • Use the Data Center participant-status API for the authenticated reviewer.
  • Add direct API and CLI coverage for all four operations.
  • Update Bitbucket capability documentation to reflect Cloud + Data Center support.

Type of change

  • Skill change (.claude/skills/<name>/)
  • Tool / bridge contract (tools/<system>/*.md)
  • Python package (tools/*/ with pyproject.toml)
  • Groovy reference impl
  • Cross-cutting
  • Documentation
  • Project template
  • CI / dev loop
  • Other

Implementation

Bitbucket Data Center review state is updated through the pull-request participant resource for the authenticated user.

The command mappings are:

  • pr approve → APPROVED
  • pr unapprove → UNAPPROVED
  • pr request-changes → NEEDS_WORK
  • pr remove-request-changes → UNAPPROVED

The bridge first resolves the authenticated Data Center user's slug, then updates that user's participant state on the target pull request.

This avoids the deprecated approval-specific endpoints and keeps the existing normalized approval/change-request output contracts unchanged across Cloud and Data Center.

Test plan

  • Ruff checks pass
  • Ruff formatting passes
  • Mypy passes
  • Focused Data Center review-state tests pass
  • Full tools/bitbucket/tests/test_bitbucket.py suite passes
  • CLI coverage added for all four Data Center commands
  • git diff --check passes
  • Repository hooks pass

Scope

This PR adds standalone Data Center review-state mutations only.

The combined:

pr review <id> --verdict {comment,approve,request-changes} --body-file <path>

path remains Cloud-only because Data Center review-body + verdict semantics are a separate follow-up.

Data Center decline/reject and merge writes also remain separate follow-ups.

Refs #606

@KatalKavya96

Copy link
Copy Markdown
Contributor Author

Hi @potiuk,

This is the next focused #606 Data Center parity follow-up.

It enables the existing standalone review-state commands on Data Center through the participant-status API:

  • approve → APPROVED
  • unapprove → UNAPPROVED
  • request-changes → NEEDS_WORK
  • remove-request-changes → UNAPPROVED

The implementation keeps the existing backend-neutral CLI and normalizers, adds API + CLI coverage for all four operations, and avoids the deprecated approval-specific endpoints.

The combined pr review --body-file path remains Cloud-only and is intentionally left for a separate follow-up.

Refs #606

@github-actions github-actions Bot added family:tools tools/* family:docs Docs, MISSION.md, READMEs contract:tracker Tool capability: issue / board / label backend contract:change-request Tool capability: proposed-change review + merge gate (PR / MR / Gerrit change) labels Oct 21, 2026

@Kaap10 Kaap10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thank you for implementing Bitbucket Data Center pull request approval writes for #606!

Review Summary

The participant status mapping (PUT .../participants/{userSlug} with APPROVED, UNAPPROVED, NEEDS_WORK) is the correct modern REST API approach for Bitbucket Data Center (Server 4.2+). Output normalization and capability documentation updates in README.md and docs/labels-and-capabilities.md are also well-structured.

However, there is a blocker regarding _current_user_slug that will cause review state writes to fail closed on live Data Center instances:


Major Blocker: Schema & Endpoint Mismatch on _current_user_slug

In tools/bitbucket/src/magpie_bitbucket/datacenter.py (lines 361-369):

def _current_user_slug(config: BitbucketConfig) -> str:
    """Return the authenticated Bitbucket Data Center user's URL-safe slug."""
    user = get_json(f"{_api_base(config)}/users", config)
    slug = user.get("slug")

    if not isinstance(slug, str) or not slug.strip():
        raise BitbucketError("Bitbucket Data Center current-user response did not include a user slug")

    return quote_path(slug)
  1. Atlassian Data Center REST API v1.0 Contract:

    • In Bitbucket Server / Data Center, GET /rest/api/1.0/users is the paged directory search endpoint (Page<User>).
    • The response schema is:
      {
        "size": 1,
        "limit": 25,
        "isLastPage": true,
        "values": [
          {
            "name": "alice",
            "displayName": "Alice",
            "slug": "alice"
          }
        ],
        "start": 0
      }
    • It contains no top-level slug key. user.get("slug") evaluates to None, which will always raise:
      BitbucketError: Bitbucket Data Center current-user response did not include a user slug
    • Furthermore, /rest/api/1.0/users returns server-wide users alphabetically rather than the caller.
  2. Why Unit Tests Passed:

    • In tools/bitbucket/tests/test_bitbucket.py:3050, the test mock configured a flat dictionary:
      mock_opener(
          mock_build_opener,
          {
              "slug": "alice",
              "displayName": "Alice",
              "name": "alice",
          },
          ...
      )
    • This masked the endpoint mismatch.
  3. Remediation Options:

    • Extract X-AUSERNAME header: In Bitbucket Server / Data Center, all HTTP responses return the X-AUSERNAME: <username> header identifying the authenticated caller.
    • Support config.username: Allow reading the slug from config.username / BITBUCKET_DATACENTER_USER, with fallback to X-AUSERNAME.


def _current_user_slug(config: BitbucketConfig) -> str:
"""Return the authenticated Bitbucket Data Center user's URL-safe slug."""
user = get_json(f"{_api_base(config)}/users", config)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In Bitbucket Data Center REST API v1.0, GET /rest/api/1.0/users is a paged user-directory search returning {"values": [...], "size": ...} rather than a single user object. Because there is no top-level slug field, user.get("slug") returns None on live instances and raises BitbucketError. Consider extracting the X-AUSERNAME response header or reading config.username.

match="Data Center pull request approval writes are not supported",
):
datacenter.approve_pull_request(load_config(), "9")
mock_opener(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The mock simulates a flat {"slug": "alice"} object, which masks the paged Page<User> schema returned by /rest/api/1.0/users. Updating the test to mock the real endpoint schema will prevent regressions.

@potiuk potiuk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The participant-status mapping and the docs look right, but the current-user lookup cannot work against a real Data Center instance, so the two open threads from Kaap10 need addressing before this can merge.

current-user lookup (tools/bitbucket/src/magpie_bitbucket/datacenter.py:363)

user = get_json(f"{_api_base(config)}/users", config)
slug = user.get("slug")

Agreeing with the open thread from Kaap10: GET /rest/api/1.0/users is the paged user-directory search ({"values": [...]}), not a current-user endpoint, so slug is always missing and all four new commands fail on a live Data Center instance. Read the authenticated username from the X-AUSERNAME response header (Bitbucket Server/DC sets it on every authenticated response), or from a configured username, and resolve the slug from that.

Smaller observations

  • tools/bitbucket/tests/test_bitbucket.py:3050 — the mock returns a flat user object, which is why the suite passes; mock the real response shape of whatever endpoint or header the fix uses

This review was drafted by an AI-assisted tool and
confirmed by an Apache Magpie maintainer. The findings
below are observations, not blockers; an Apache Magpie
maintainer — a real person — will take the next look at the
PR. If you think a finding is mis-applied, please reply on
the PR and a maintainer will weigh in.

More on how Apache Magpie handles maintainer review:
Contributing guide.

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

Labels

contract:change-request Tool capability: proposed-change review + merge gate (PR / MR / Gerrit change) contract:tracker Tool capability: issue / board / label backend family:docs Docs, MISSION.md, READMEs family:tools tools/*

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants