Skip to content

Commit e4ee4fb

Browse files
fix(account-status-lifecycle-test): read resource-level status for SDK compatibility (#102)
## Problem The `account-status-lifecycle-test` action reads user status **only** from the deprecated `UserTrait` annotation (`UserTrait_Status_Status`, e.g. `STATUS_ENABLED`). Recent `baton-sdk` versions moved status to a **resource-level** attribute (`Status_ResourceStatus`, e.g. `RESOURCE_STATUS_ENABLED`). When a connector migrates to `WithResourceStatus(...)` and sets only the resource-level status: - The SDK mirrors trait → resource, but **not** resource → trait. - `NewUserTrait` **defaults an unset trait status to `STATUS_ENABLED`**. So the test always reads `STATUS_ENABLED` from the trait, and disable verifications fail even when the account was correctly disabled: ``` User 2 current status: STATUS_ENABLED ✗ User is not disabled as expected ``` (Hit while migrating `baton-looker` off the deprecated trait options — the connector builds and disables correctly, but this shared test couldn't observe the resource-level status.) ## Fix `get_user_status` now reads **both** locations and: 1. Prefers the resource-level `resource.status.status`, normalizing `RESOURCE_STATUS_*` → the trait form `STATUS_*`. 2. Falls back to the deprecated trait `status.status` only when no resource-level status is present. 3. Treats `*_UNSPECIFIED` as absent, so the unreliable defaulted trait value never masks a real resource-level status. Downstream comparisons (`is_user_enabled`, summaries) are unchanged — they still compare against `STATUS_ENABLED` / `STATUS_DISABLED`. This keeps the action compatible with connectors built against **any** baton-sdk version (trait-only, resource-only, or both). ## Validation Verified the jq logic against all shapes: | Scenario | Result | |---|---| | New SDK: resource DISABLED, trait defaulted ENABLED | `STATUS_DISABLED` ✅ (was `STATUS_ENABLED` ❌) | | New SDK: resource ENABLED, trait defaulted ENABLED | `STATUS_ENABLED` ✅ | | Old connector: trait DISABLED, no resource status | `STATUS_DISABLED` ✅ | | Old connector: trait ENABLED, no resource status | `STATUS_ENABLED` ✅ | | Resource UNSPECIFIED + trait DISABLED | `STATUS_DISABLED` ✅ | | Nothing set | `unknown` ✅ | `bash -n` passes on the script. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent e8c2e04 commit e4ee4fb

2 files changed

Lines changed: 39 additions & 13 deletions

File tree

actions/account-status-lifecycle-test/README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,11 @@ The action performs account status tests based on the selected `test-flow`:
117117
## Status Detection
118118

119119
The action checks for account status using the following logic:
120-
- Extracts status from the UserTrait annotation
121-
- Checks if status equals `STATUS_ENABLED` (the only enabled status in baton-sdk)
120+
- Reads status from both places it can live, depending on the connector's baton-sdk version:
121+
- Resource-level `resource.status.status` (`Status_ResourceStatus` enum, e.g. `RESOURCE_STATUS_ENABLED`) — used by connectors built against baton-sdk **v0.19.0+**, which moved status from the trait to the resource ([ConductorOne/baton-sdk#996](https://github.com/ConductorOne/baton-sdk/pull/996)).
122+
- The deprecated UserTrait annotation's `status.status` (`UserTrait_Status_Status` enum, e.g. `STATUS_ENABLED`) — used by connectors built against baton-sdk **< v0.19.0**.
123+
- Prefers the resource-level value (normalized to the trait form) and falls back to the trait value. On v0.19.0+ the deprecated trait status defaults to `STATUS_ENABLED` when it isn't set explicitly, so the trait value alone is unreliable and is only used when no resource-level status is present.
124+
- Checks if the resulting status equals `STATUS_ENABLED` (the only enabled status in baton-sdk)
122125
- Any other status value is considered disabled:
123126
- `STATUS_DISABLED` - Account is disabled
124127
- `STATUS_DELETED` - Account is deleted

actions/account-status-lifecycle-test/account-status-lifecycle-test.sh

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,43 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
4141
source "${SCRIPT_DIR}/../_helpers/sleep.sh"
4242

4343
# Function to get user status (enabled/disabled)
44+
#
45+
# Status can live in one of two places depending on the baton-sdk version the
46+
# connector was built with:
47+
# * Resource-level: .resource.status.status, using the Status_ResourceStatus
48+
# enum (e.g. "RESOURCE_STATUS_ENABLED"). baton-sdk v0.19.0 moved status here
49+
# from the trait (ConductorOne/baton-sdk#996) and deprecated the trait field.
50+
# * Deprecated trait-level: the UserTrait annotation's .status.status, using
51+
# the UserTrait_Status_Status enum (e.g. "STATUS_ENABLED"). Used by
52+
# connectors built against baton-sdk < v0.19.0.
53+
#
54+
# We read both and prefer the resource-level value, normalizing it to the
55+
# trait-form ("RESOURCE_STATUS_ENABLED" -> "STATUS_ENABLED") so the rest of the
56+
# script keeps comparing against STATUS_ENABLED/STATUS_DISABLED. On v0.19.0+ the
57+
# deprecated trait status defaults to STATUS_ENABLED when it isn't set
58+
# explicitly, so the trait value alone is unreliable and is only used as a
59+
# fallback when no resource-level status is present.
4460
get_user_status() {
4561
local user_id="$1"
46-
local status=$(baton resources -t "user" --output-format=json \
62+
baton resources -t "user" --output-format=json \
4763
| jq -r --arg user_id "$user_id" \
48-
'(.resources // [])[] |
49-
select(.resource.id.resource == $user_id) |
50-
(.resource.annotations[]? | select(."@type" == "type.googleapis.com/c1.connector.v2.UserTrait")) as $trait |
51-
if $trait != null and $trait.status != null then
52-
$trait.status
53-
else
54-
"unknown"
55-
end')
56-
# Extract just the status value if it's JSON, otherwise return as-is
57-
echo "$status" | jq -r '.status // empty' 2>/dev/null || echo "$status"
64+
'(.resources // [])[]
65+
| select(.resource.id.resource == $user_id)
66+
| .resource as $r
67+
# Resource-level status (newer baton-sdk): Status_ResourceStatus enum.
68+
| ($r.status.status // null) as $resStatus
69+
# Deprecated trait-level status: UserTrait_Status_Status enum.
70+
| ((
71+
$r.annotations[]?
72+
| select(."@type" == "type.googleapis.com/c1.connector.v2.UserTrait")
73+
| .status.status
74+
) // null) as $traitStatus
75+
# Normalize "RESOURCE_STATUS_*" to the trait-form "STATUS_*".
76+
| (if $resStatus == null then null else ($resStatus | sub("^RESOURCE_"; "")) end) as $resNorm
77+
| if ($resNorm != null and $resNorm != "STATUS_UNSPECIFIED") then $resNorm
78+
elif ($traitStatus != null and $traitStatus != "STATUS_UNSPECIFIED") then $traitStatus
79+
else "unknown"
80+
end'
5881
}
5982

6083
# Function to check if user is enabled

0 commit comments

Comments
 (0)