feat(integrations): Azure DevOps tagging service + state→label mappers (PR 4/N)#7627
feat(integrations): Azure DevOps tagging service + state→label mappers (PR 4/N)#7627asaphko wants to merge 7 commits into
Conversation
…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>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 3 Skipped Deployments
|
There was a problem hiding this comment.
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 Report❌ Patch coverage is
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. 🚀 New features to boost your workflow:
|
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.
AzureDevOpsTagLabelenum (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).AzureDevOpsResourceMetadataTypedDict — the JSON snapshot the frontend supplies on link.active/completed/abandoned+is_draft) and the common work-item states across Agile / Scrum / Basic / CMMI process templates. Unknown states returnNone.set_azure_devops_tag,apply_initial_tag,clear_tag_for_resource,refresh_tags_for_resource. All public functions are no-ops whentagging_enabledis False or noAzureDevOpsConfigurationexists for the project.Issue Open,MR Merged). TheTagType.AZURE_DEVOPSenum 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 asPR Open / PR Merged / ...—TagType.AZURE_DEVOPSprovides 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
apply_initial_tag/clear_tag_for_resourcefrom theFeatureExternalResourcelifecycle — lands whenvcs/services.pyis extended.refresh_tags_for_resource— lands in the webhook PR.tagging_enabledon (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 lintclean (including the customflagsmith-lint-testsFT003/FT004 hooks)make typecheckcleanmake 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