Split inactive profile state
Replace the binary profile ACTIVE/INACTIVE model with PENDING, ACTIVE, and DEACTIVATED so invited-but-not-yet-activated members remain assignable to assets, data, and risks instead of being treated like deactivated users. Add activated_at/deactivated_at timestamps and Mark* lifecycle helpers, and update every transition (create, invite/re-invite, activation, archive, SCIM, SAML, sessions, compliance-portal grant) to the new states. Expose a multi-state states[] filter across coredata, GraphQL, MCP, and the console owner pickers, which now request ACTIVE and PENDING members. A migration renames the membership_state enum, classifies existing inactive profiles as PENDING from recent invitation activity, and backfills the new timestamp columns. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
@@ -30,9 +30,10 @@ type Profile implements Node {
|
||||
|
||||
enum ProfileState
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.ProfileState") {
|
||||
PENDING @goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStatePending")
|
||||
ACTIVE @goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStateActive")
|
||||
INACTIVE
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStateInactive")
|
||||
DEACTIVATED
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStateDeactivated")
|
||||
}
|
||||
|
||||
enum ProfileSource
|
||||
@@ -71,6 +72,7 @@ enum ProfileOrderField
|
||||
input ProfileFilter {
|
||||
contractEnded: Boolean
|
||||
state: ProfileState
|
||||
states: [ProfileState!]
|
||||
query: String
|
||||
role: MembershipRole
|
||||
kind: String
|
||||
|
||||
@@ -32,6 +32,10 @@ func (r *identityResolver) Profiles(ctx context.Context, obj *types.Identity, fi
|
||||
if filter != nil {
|
||||
filters = coredata.NewMembershipProfileFilter(filter.ContractEnded).WithMembership()
|
||||
|
||||
if len(filter.States) > 0 {
|
||||
filters.WithStates(filter.States...)
|
||||
}
|
||||
|
||||
if filter.State != nil {
|
||||
filters.WithState(*filter.State)
|
||||
}
|
||||
|
||||
@@ -188,6 +188,10 @@ func (r *organizationResolver) Profiles(ctx context.Context, obj *types.Organiza
|
||||
if filter != nil {
|
||||
filters = coredata.NewMembershipProfileFilter(filter.ContractEnded).WithMembership()
|
||||
|
||||
if len(filter.States) > 0 {
|
||||
filters.WithStates(filter.States...)
|
||||
}
|
||||
|
||||
if filter.State != nil {
|
||||
filters.WithState(*filter.State)
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func (r *mutationResolver) DeactivateUser(ctx context.Context, input types.Deact
|
||||
_, err := r.iam.OrganizationService.UpdateUserState(
|
||||
ctx,
|
||||
input.ProfileID,
|
||||
coredata.ProfileStateInactive,
|
||||
coredata.ProfileStateDeactivated,
|
||||
)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot deactivate profile", log.Error(err))
|
||||
|
||||
@@ -9,9 +9,10 @@ type OrganizationContext {
|
||||
|
||||
enum ProfileState
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.ProfileState") {
|
||||
PENDING @goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStatePending")
|
||||
ACTIVE @goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStateActive")
|
||||
INACTIVE
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStateInactive")
|
||||
DEACTIVATED
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStateDeactivated")
|
||||
}
|
||||
|
||||
enum MembershipRole
|
||||
@@ -69,6 +70,7 @@ input ProfileOrder
|
||||
input ProfileFilter {
|
||||
contractEnded: Boolean
|
||||
state: ProfileState
|
||||
states: [ProfileState!]
|
||||
query: String
|
||||
role: MembershipRole
|
||||
kind: String
|
||||
|
||||
@@ -119,6 +119,10 @@ func (r *organizationResolver) Profiles(ctx context.Context, obj *types.Organiza
|
||||
if filter != nil {
|
||||
filters = coredata.NewMembershipProfileFilter(filter.ContractEnded).WithMembership()
|
||||
|
||||
if len(filter.States) > 0 {
|
||||
filters.WithStates(filter.States...)
|
||||
}
|
||||
|
||||
if filter.State != nil {
|
||||
filters.WithState(*filter.State)
|
||||
}
|
||||
|
||||
@@ -2776,6 +2776,10 @@ func (r *Resolver) ListUsersTool(ctx context.Context, req *mcp.CallToolRequest,
|
||||
if input.Filter != nil {
|
||||
filter = coredata.NewMembershipProfileFilter(input.Filter.ContractEnded).WithMembership()
|
||||
|
||||
if len(input.Filter.States) > 0 {
|
||||
filter.WithStates(input.Filter.States...)
|
||||
}
|
||||
|
||||
if input.Filter.State != nil {
|
||||
filter.WithState(*input.Filter.State)
|
||||
}
|
||||
|
||||
@@ -184,8 +184,9 @@ components:
|
||||
ProfileState:
|
||||
type: string
|
||||
enum:
|
||||
- PENDING
|
||||
- ACTIVE
|
||||
- INACTIVE
|
||||
- DEACTIVATED
|
||||
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ProfileState
|
||||
|
||||
ProfileKind:
|
||||
@@ -526,7 +527,7 @@ components:
|
||||
description: Profile source (MANUAL, SCIM, or SAML)
|
||||
state:
|
||||
$ref: "#/components/schemas/ProfileState"
|
||||
description: Profile state (ACTIVE or INACTIVE)
|
||||
description: Profile state (PENDING, ACTIVE, or DEACTIVATED)
|
||||
position:
|
||||
type:
|
||||
- string
|
||||
@@ -578,7 +579,12 @@ components:
|
||||
description: Filter by contract status. True returns only users with ended contracts, false returns only users with active or no contract.
|
||||
state:
|
||||
$ref: "#/components/schemas/ProfileState"
|
||||
description: Filter by profile state (ACTIVE or INACTIVE)
|
||||
description: Filter by profile state (PENDING, ACTIVE, or DEACTIVATED)
|
||||
states:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/ProfileState"
|
||||
description: Filter by profile states (PENDING, ACTIVE, or DEACTIVATED)
|
||||
query:
|
||||
type: string
|
||||
description: Search by full name, email address, or position
|
||||
|
||||
Reference in New Issue
Block a user