Skip to content

Expose invitation expiry timestamp for SCIM org memberships#149

Open
c1-dev-bot[bot] wants to merge 1 commit into
mainfrom
epd-2278-invitation-expiry-timestamps
Open

Expose invitation expiry timestamp for SCIM org memberships#149
c1-dev-bot[bot] wants to merge 1 commit into
mainfrom
epd-2278-invitation-expiry-timestamps

Conversation

@c1-dev-bot
Copy link
Copy Markdown
Contributor

@c1-dev-bot c1-dev-bot Bot commented May 11, 2026

Summary

  • Adds created_at, expires_at (created_at + 7 days), role, failed_at, and failed_reason fields to the invitation resource user profile
  • Sets the CreatedAt user trait on invitation resources when the timestamp is available from the GitHub API
  • Enables customers to identify expired pending SCIM invitations and automate cleanup of stale seats

Background

GitHub expires org invitations after 7 days, but pending members continue to occupy seats. The baton-github connector already syncs pending invitations via the /orgs/{org}/invitations endpoint, but was not exposing the CreatedAt timestamp from the GitHub API response. Without this data, customers had no way to determine when an invitation was sent or whether it had expired.

The GitHub API's Invitation type provides CreatedAt and FailedAt timestamps. Since GitHub does not expose an explicit ExpiresAt field, we compute it as CreatedAt + 7 days (the documented GitHub expiry window).

Test plan

  • Verify invitation resources include created_at and expires_at in their profile after sync
  • Verify failed_at and failed_reason appear in profile when invitation has failed
  • Verify role field is populated in profile
  • Verify CreatedAt user trait is set on invitation resources
  • Verify existing invitation List/Create/Delete functionality is not affected

Fixes: CXH-1455


Automated PR Notice

This PR was automatically created by c1-dev-bot as a potential implementation.

This code requires:

  • Human review of the implementation approach
  • Manual testing to verify correctness
  • Approval from the appropriate team before merging

Add created_at, expires_at (created_at + 7 days), failed_at,
failed_reason, and role fields to the invitation user profile.
Also set the CreatedAt trait on the user resource when available.

This enables customers to identify expired pending SCIM invitations
and clean up seats occupied by stale invites.

Fixes: CXH-1455
@c1-dev-bot c1-dev-bot Bot requested a review from a team May 11, 2026 17:33
@linear-code
Copy link
Copy Markdown

linear-code Bot commented May 11, 2026

CXH-1455

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 11, 2026

Claude finished @c1-dev-bot[bot]'s task in 1m 4s —— View job


PR Review: Expose invitation expiry timestamp for SCIM org memberships

  • Gather context and read changed files
  • Review code changes against GitHub API types
  • Post review feedback

Overall: This PR looks good. The changes are clean, well-scoped, and correctly use the go-github library's Invitation struct fields. A few minor observations below.

What works well

  • Correct use of the GitHub API types. The Invitation struct in go-github v69 has CreatedAt, FailedAt (*Timestamp), FailedReason (*string), and Role (*string). The getters handle nil pointers safely, and the IsZero() guard before accessing .Time is the right pattern.

  • Computed expires_at is reasonable. Since GitHub doesn't expose an explicit expiry field, computing CreatedAt + 7 days matches the documented behavior. The constant githubInvitationExpiryDuration keeps this clear and maintainable.

  • Conditional WithCreatedAt trait. Only appending to userTraitOpts when createdAt is non-zero avoids passing a zero-value time.Time to the SDK, which is correct.

  • RFC3339 formatting. Using time.RFC3339 for all timestamp profile fields is consistent and parseable.

Minor observations

  1. createdAt.Add() vs createdAt.Time.Add() (line 33): createdAt is a github.Timestamp (which embeds time.Time), so createdAt.Add(...) works because it promotes time.Time.Add(). This is correct — just noting that expiresAt is a time.Time (not a Timestamp), which is why .Format() is called directly on it without .Time. Consistent and fine.

  2. failed_reason without failed_at: If the API ever returns a failed_reason with a zero/nil failed_at, the profile would have failed_reason without failed_at. This seems unlikely based on the GitHub API but worth being aware of. Not a blocker.

  3. No expired boolean convenience field: Consumers will need to parse expires_at and compare against the current time to determine if an invitation is expired. This is fine for a first pass — adding a computed expired boolean could be a follow-up if needed, though it would be a point-in-time snapshot from sync rather than live.

  4. 7-day assumption: The githubInvitationExpiryDuration constant correctly documents the current GitHub behavior. If GitHub ever changes this window, only this constant needs updating. Good.

Verdict

Looks good to merge after standard manual testing per the test plan. The implementation is minimal, correct, and doesn't affect existing sync/provisioning behavior.


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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants