Skip to content

feat(integrations): Azure DevOps tagging service + state→label mappers (PR 4/N)#7627

Closed
asaphko wants to merge 7 commits into
feat/azure-devops-03-clientfrom
feat/azure-devops-04-tagging
Closed

feat(integrations): Azure DevOps tagging service + state→label mappers (PR 4/N)#7627
asaphko wants to merge 7 commits into
feat/azure-devops-03-clientfrom
feat/azure-devops-04-tagging

Conversation

@asaphko
Copy link
Copy Markdown
Contributor

@asaphko asaphko commented May 28, 2026

Summary

PR 4 of the stacked Azure DevOps integration rollout. Stands up the Flagsmith-side tagging library that will reflect ADO PR / work-item state on linked features.

  • Constants: AzureDevOpsTagLabel enum (6 members — PR Open, PR Merged, PR Abandoned, PR Draft, Work Item Open, Work Item Closed), AZURE_DEVOPS_TAG_COLOR = "#0078D4" (Microsoft Azure Blue), three lookup dicts (*_KIND_BY_LABEL, *_KIND_BY_RESOURCE_TYPE, *_DESCRIPTION_BY_LABEL).
  • Types: AzureDevOpsResourceMetadata TypedDict — the JSON snapshot the frontend supplies on link.
  • Mappers: case-insensitive state-to-label conversion. PR states (active/completed/abandoned + is_draft) and the common work-item states across Agile / Scrum / Basic / CMMI process templates. Unknown states return None.
  • Tagging service: four entry points — set_azure_devops_tag, apply_initial_tag, clear_tag_for_resource, refresh_tags_for_resource. All public functions are no-ops when tagging_enabled is False or no AzureDevOpsConfiguration exists for the project.
  • Spec doc updated: tag labels drop the "Azure" prefix to match GitLab's brevity convention (Issue Open, MR Merged). The TagType.AZURE_DEVOPS enum value scopes them at the type layer.

Naming decision

The original spec listed Azure PR Open / Azure PR Merged / .... Per the code review feedback on PR 1 and the GitLab precedent, this PR ships them as PR Open / PR Merged / ...TagType.AZURE_DEVOPS provides source disambiguation, "PR" / "Work Item" already disambiguate from GitLab's "MR" / "Issue", and shorter labels read better in tag chips. The spec is updated in the same PR to match.

Stack

Plan: docs/superpowers/plans/2026-05-28-azure-devops-04-tagging.md.

Out of scope

  • The dispatcher wiring that calls apply_initial_tag / clear_tag_for_resource from the FeatureExternalResource lifecycle — lands when vcs/services.py is extended.
  • The inbound webhook handler that calls refresh_tags_for_resource — lands in the webhook PR.
  • State-category enrichment via the REST client — for v1 the mapper uses a small lookup table; if users report mis-categorisations on custom process templates, we can revisit.
  • Retroactive tagging when a user flips tagging_enabled on (currently linked resources stay untagged until the next state change) — defensible; matches GitLab. A "backfill tags" admin command would be a follow-up.

Test plan

  • make lint clean (including the custom flagsmith-lint-tests FT003/FT004 hooks)
  • make typecheck clean
  • make test opts='-n0 tests/unit/integrations/azure_devops/' — 127 passed (PR 3 baseline 71 + 5 constants + 39 mappers + 12 tagging)
  • make test opts='tests/unit/integrations/gitlab tests/unit/integrations/github tests/unit/features/test_unit_feature_external_resources_views.py tests/unit/features/test_migrations.py' — passing (adjacent-integration regression guard)
  • make django-make-migrations opts='--check --dry-run' — no drift (PR 4 introduces no schema changes)

🤖 Generated with Claude Code

asaphko and others added 7 commits May 28, 2026 14:03
…gration

Fourth plan in the stacked-PRs rollout. Covers the Azure DevOps system-tag
library: constants (tag colour, label enum, lookup dicts), the resource
metadata TypedDict, the state-to-label mappers, and the tagging service
(apply_initial_tag, clear_tag_for_resource, refresh_tags_for_resource).
Service functions are no-ops when tagging_enabled is off or no
AzureDevOpsConfiguration exists for the project.

Tag labels drop the "Azure" prefix to match GitLab's brevity convention;
the spec gets updated to reflect this in the same PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Six AzureDevOpsTagLabel members covering PR + Work Item state, plus the
lookup tables the tagging service needs (kind-by-label,
kind-by-resource-type, description-by-label). Tag colour is Microsoft
"Azure Blue" (#0078D4) so the chips read distinctly from GitLab's
orange and the GitHub palette.

Tag labels deliberately omit the "Azure" prefix to match GitLab's
brevity convention — TagType.AZURE_DEVOPS scopes them at the type
layer, and "PR" / "Work Item" already disambiguate from GitLab's "MR"
/ "Issue".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Schema for the JSON metadata snapshot the frontend supplies at
resource-link time. The mapper in the next commit validates and
extracts fields from this shape. Mirrors GitLab's
GitLabResourceMetadata.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three mappers:

- map_pr_state_to_tag_label(state, *, is_draft) covers active /
  completed / abandoned states, with is_draft overriding "active" to
  PR_DRAFT.
- map_work_item_state_to_tag_label(state) covers the common states
  across ADO's Agile / Scrum / Basic process templates. Unknown states
  return None (fail-closed — the tagging service treats None as "no
  tag change").
- map_resource_to_tag_label(resource) is the high-level entry point
  that pydantic-validates the metadata snapshot stored on the
  FeatureExternalResource and dispatches to the right state mapper.

State lookups are case-insensitive. Mirrors the GitLab mapper shape.
Adds five resource fixtures to the local conftest for use by tagging
tests in the next commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four entry points:

- set_azure_devops_tag(feature, label) replaces any existing
  same-kind tag and applies the new one.
- apply_initial_tag(resource) tags the feature based on the
  metadata snapshot at link time. No-op when the project has no
  AzureDevOpsConfiguration or tagging_enabled is False.
- clear_tag_for_resource(resource) removes the kind-scoped tag when
  the unlinked resource was the last of its kind on the feature.
- refresh_tags_for_resource(resource) re-applies the right tag from
  current metadata. Called by the future inbound-webhook handler.

PR and Work Item tags coexist independently — clearing a PR resource
leaves Work Item tags intact and vice versa. Mirrors GitLab's
tagging.set_gitlab_tag / apply_initial_tag / clear_tag_for_resource.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The original spec listed labels as "Azure PR Open" etc. Implementation
matches GitLab's brevity convention (just "PR Open", "Work Item Open",
etc.) — the TagType.AZURE_DEVOPS enum value disambiguates the source
at the type layer. Update the spec so it tracks the code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…MI proposed state

Three small fixes surfaced by the final whole-PR review:

1. test_clear_tag_for_resource__non_ado_resource_... now pre-creates a
   GitLab system tag on the feature and asserts it survives the call.
   Without this, the test would pass even if ADO code accidentally
   cleared cross-vendor tags. Function renamed to make the intent
   explicit.
2. _make_pr_resource in conftest now includes is_draft in the URL so
   the open + draft fixtures don't share a URL (would otherwise hit
   the unique_feature_url_constraint if a future test uses both).
3. _WORK_ITEM_OPEN_STATES gains "proposed" so the ADO CMMI process
   template's initial state is correctly recognised. Adds one
   parametrise case to lock the mapping in.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

3 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs Ignored Ignored May 28, 2026 1:28pm
flagsmith-frontend-preview Ignored Ignored May 28, 2026 1:28pm
flagsmith-frontend-staging Ignored Ignored May 28, 2026 1:28pm

Request Review

@github-actions github-actions Bot added api Issue related to the REST API docs Documentation updates feature New feature or request and removed docs Documentation updates labels May 28, 2026
@asaphko asaphko self-assigned this May 28, 2026
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements the Flagsmith-side tagging service for the Azure DevOps integration. It introduces constants, a Pydantic-validated metadata TypedDict, state mappers to translate Azure DevOps pull request and work item states into Flagsmith tag labels, and the core tagging service containing entry points for applying, clearing, and refreshing tags. Comprehensive unit tests have been added to verify the constants, mappers, and tagging logic, and the integration design specification has been updated to reflect the brief, prefix-free tag labels. There are no review comments, and I have no additional feedback to provide.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

❌ Patch coverage is 99.33775% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.54%. Comparing base (b24ca9c) to head (6193e5e).

Files with missing lines Patch % Lines
api/integrations/azure_devops/services/tagging.py 94.28% 2 Missing ⚠️
Additional details and impacted files
@@                      Coverage Diff                      @@
##           feat/azure-devops-03-client    #7627    +/-   ##
=============================================================
  Coverage                        98.53%   98.54%            
=============================================================
  Files                             1464     1469     +5     
  Lines                            55312    55614   +302     
=============================================================
+ Hits                             54503    54803   +300     
- Misses                             809      811     +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Labels

api Issue related to the REST API feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants