Add state to MCP profile and refactor contract filter
Add the profile state attribute (ACTIVE/INACTIVE) to the MCP Profile schema so listUsers and getUser tools expose it, and add a state filter to listUsers. Rename excludeContractEnded to contractEnded across the entire stack (MCP, GraphQL, CLI, frontend). The new boolean is two-way: true returns only users with ended contracts, false returns only users with active or no contract, and null returns all. Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -65,7 +65,7 @@ function PeopleMultiSelectWithQuery<T extends FieldValues = FieldValues>(
|
|||||||
) {
|
) {
|
||||||
const { __ } = useTranslate();
|
const { __ } = useTranslate();
|
||||||
const { name, organizationId, control, selectedPeople = [], placeholder } = props;
|
const { name, organizationId, control, selectedPeople = [], placeholder } = props;
|
||||||
const people = usePeople(organizationId, { excludeContractEnded: true });
|
const people = usePeople(organizationId, { contractEnded: false });
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
const allPeople = [...people];
|
const allPeople = [...people];
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ function PeopleSelectWithQuery<TFieldValues extends FieldValues = FieldValues>(
|
|||||||
) {
|
) {
|
||||||
const { __ } = useTranslate();
|
const { __ } = useTranslate();
|
||||||
const { name, organizationId, control } = props;
|
const { name, organizationId, control } = props;
|
||||||
const people = usePeople(organizationId, { excludeContractEnded: true });
|
const people = usePeople(organizationId, { contractEnded: false });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Controller
|
<Controller
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function PeopleCell(props: Props) {
|
|||||||
query={peopleQuery}
|
query={peopleQuery}
|
||||||
variables={{
|
variables={{
|
||||||
organizationId: props.organizationId,
|
organizationId: props.organizationId,
|
||||||
filter: { excludeContractEnded: true },
|
filter: { contractEnded: false },
|
||||||
}}
|
}}
|
||||||
items={data =>
|
items={data =>
|
||||||
data.organization?.profiles?.edges.map(edge => edge.node) ?? []}
|
data.organization?.profiles?.edges.map(edge => edge.node) ?? []}
|
||||||
|
|||||||
@@ -49,13 +49,13 @@ export const peopleQuery = graphql`
|
|||||||
*/
|
*/
|
||||||
export function usePeople(
|
export function usePeople(
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
{ excludeContractEnded }: { excludeContractEnded?: boolean } = {},
|
{ contractEnded }: { contractEnded?: boolean } = {},
|
||||||
) {
|
) {
|
||||||
const data = useLazyLoadQuery<PeopleGraphQuery>(
|
const data = useLazyLoadQuery<PeopleGraphQuery>(
|
||||||
peopleQuery,
|
peopleQuery,
|
||||||
{
|
{
|
||||||
organizationId: organizationId,
|
organizationId: organizationId,
|
||||||
filter: excludeContractEnded ? { excludeContractEnded: true } : null,
|
filter: contractEnded !== undefined ? { contractEnded } : null,
|
||||||
},
|
},
|
||||||
{ fetchPolicy: "network-only" },
|
{ fetchPolicy: "network-only" },
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ function PeopleList({
|
|||||||
signatureDocumentsDialogPeopleQuery,
|
signatureDocumentsDialogPeopleQuery,
|
||||||
{
|
{
|
||||||
organizationId,
|
organizationId,
|
||||||
filter: { excludeContractEnded: true },
|
filter: { contractEnded: false },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const documentSignaturesPageQuery = graphql`
|
|||||||
query DocumentSignaturesPageQuery($documentId: ID! $organizationId: ID! $versionId: ID! $versionSpecified: Boolean!) {
|
query DocumentSignaturesPageQuery($documentId: ID! $organizationId: ID! $versionId: ID! $versionSpecified: Boolean!) {
|
||||||
organization: node(id: $organizationId) {
|
organization: node(id: $organizationId) {
|
||||||
__typename
|
__typename
|
||||||
...DocumentSignatureList_peopleFragment @arguments(filter: { excludeContractEnded: true })
|
...DocumentSignatureList_peopleFragment @arguments(filter: { contractEnded: false })
|
||||||
}
|
}
|
||||||
# We use this on /documents/:documentId
|
# We use this on /documents/:documentId
|
||||||
document: node(id: $documentId) @skip(if: $versionSpecified) {
|
document: node(id: $documentId) @skip(if: $versionSpecified) {
|
||||||
|
|||||||
@@ -550,7 +550,7 @@ function OwnerFilterSelect({
|
|||||||
onChange,
|
onChange,
|
||||||
}: OwnerFilterSelectProps) {
|
}: OwnerFilterSelectProps) {
|
||||||
const { __ } = useTranslate();
|
const { __ } = useTranslate();
|
||||||
const people = usePeople(organizationId, { excludeContractEnded: true });
|
const people = usePeople(organizationId, { contractEnded: false });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select value={value ?? "ALL"} onValueChange={onChange}>
|
<Select value={value ?? "ALL"} onValueChange={onChange}>
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
|
|||||||
|
|
||||||
if flagActive {
|
if flagActive {
|
||||||
variables["filter"] = map[string]any{
|
variables["filter"] = map[string]any{
|
||||||
"excludeContractEnded": true,
|
"contractEnded": false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +210,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
|
|||||||
cmd.Flags().IntVarP(&flagLimit, "limit", "L", 30, "Maximum number of users to list")
|
cmd.Flags().IntVarP(&flagLimit, "limit", "L", 30, "Maximum number of users to list")
|
||||||
cmd.Flags().StringVar(&flagOrder, "order-by", "", "Order by field (FULL_NAME, CREATED_AT, KIND)")
|
cmd.Flags().StringVar(&flagOrder, "order-by", "", "Order by field (FULL_NAME, CREATED_AT, KIND)")
|
||||||
cmd.Flags().StringVar(&flagOrderDir, "order-direction", "DESC", "Sort direction (ASC, DESC)")
|
cmd.Flags().StringVar(&flagOrderDir, "order-direction", "DESC", "Sort direction (ASC, DESC)")
|
||||||
cmd.Flags().BoolVar(&flagActive, "active", false, "Exclude users whose contract has ended")
|
cmd.Flags().BoolVar(&flagActive, "active", false, "Show only users with active or no contract")
|
||||||
flagOutput = cmdutil.AddOutputFlag(cmd)
|
flagOutput = cmdutil.AddOutputFlag(cmd)
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ type (
|
|||||||
MembershipProfileFilter struct {
|
MembershipProfileFilter struct {
|
||||||
withMembership *bool
|
withMembership *bool
|
||||||
withTrustCenterAccess *bool
|
withTrustCenterAccess *bool
|
||||||
excludeContractEnded *bool
|
contractEnded *bool
|
||||||
currentDate time.Time
|
currentDate time.Time
|
||||||
email *mail.Addr
|
email *mail.Addr
|
||||||
userName *string
|
userName *string
|
||||||
@@ -35,10 +35,10 @@ type (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewMembershipProfileFilter(excludeContractEnded *bool) *MembershipProfileFilter {
|
func NewMembershipProfileFilter(contractEnded *bool) *MembershipProfileFilter {
|
||||||
return &MembershipProfileFilter{
|
return &MembershipProfileFilter{
|
||||||
excludeContractEnded: excludeContractEnded,
|
contractEnded: contractEnded,
|
||||||
currentDate: time.Now(),
|
currentDate: time.Now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ func (f *MembershipProfileFilter) SQLArguments() pgx.StrictNamedArgs {
|
|||||||
"filter_external_id": f.externalID,
|
"filter_external_id": f.externalID,
|
||||||
"with_membership": f.withMembership,
|
"with_membership": f.withMembership,
|
||||||
"with_trust_center_access": f.withTrustCenterAccess,
|
"with_trust_center_access": f.withTrustCenterAccess,
|
||||||
"exclude_contract_ended": f.excludeContractEnded,
|
"contract_ended": f.contractEnded,
|
||||||
"current_date": f.currentDate,
|
"current_date": f.currentDate,
|
||||||
"filter_state": f.state,
|
"filter_state": f.state,
|
||||||
"filter_source": f.source,
|
"filter_source": f.source,
|
||||||
@@ -132,7 +132,9 @@ AND (
|
|||||||
)
|
)
|
||||||
AND (
|
AND (
|
||||||
CASE
|
CASE
|
||||||
WHEN @exclude_contract_ended::boolean IS NOT NULL AND @exclude_contract_ended::boolean = true THEN
|
WHEN @contract_ended::boolean IS NOT NULL AND @contract_ended::boolean = true THEN
|
||||||
|
(p.contract_end_date IS NOT NULL AND p.contract_end_date < @current_date::date)
|
||||||
|
WHEN @contract_ended::boolean IS NOT NULL AND @contract_ended::boolean = false THEN
|
||||||
(p.contract_end_date IS NULL OR p.contract_end_date >= @current_date::date)
|
(p.contract_end_date IS NULL OR p.contract_end_date >= @current_date::date)
|
||||||
ELSE TRUE
|
ELSE TRUE
|
||||||
END
|
END
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ enum ProfileOrderField
|
|||||||
}
|
}
|
||||||
|
|
||||||
input ProfileFilter {
|
input ProfileFilter {
|
||||||
excludeContractEnded: Boolean
|
contractEnded: Boolean
|
||||||
state: ProfileState
|
state: ProfileState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func (r *identityResolver) Profiles(ctx context.Context, obj *types.Identity, fi
|
|||||||
|
|
||||||
filters := coredata.NewMembershipProfileFilter(nil).WithMembership()
|
filters := coredata.NewMembershipProfileFilter(nil).WithMembership()
|
||||||
if filter != nil {
|
if filter != nil {
|
||||||
filters = coredata.NewMembershipProfileFilter(filter.ExcludeContractEnded).WithMembership()
|
filters = coredata.NewMembershipProfileFilter(filter.ContractEnded).WithMembership()
|
||||||
if filter.State != nil {
|
if filter.State != nil {
|
||||||
filters.WithState(*filter.State)
|
filters.WithState(*filter.State)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ input ProfileOrder
|
|||||||
}
|
}
|
||||||
|
|
||||||
input ProfileFilter {
|
input ProfileFilter {
|
||||||
excludeContractEnded: Boolean
|
contractEnded: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProfileConnection
|
type ProfileConnection
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ func (r *organizationResolver) Profiles(ctx context.Context, obj *types.Organiza
|
|||||||
|
|
||||||
filters := coredata.NewMembershipProfileFilter(nil).WithMembership()
|
filters := coredata.NewMembershipProfileFilter(nil).WithMembership()
|
||||||
if filter != nil {
|
if filter != nil {
|
||||||
filters = coredata.NewMembershipProfileFilter(filter.ExcludeContractEnded).WithMembership()
|
filters = coredata.NewMembershipProfileFilter(filter.ContractEnded).WithMembership()
|
||||||
}
|
}
|
||||||
|
|
||||||
pageOrderBy := page.OrderBy[coredata.MembershipProfileOrderField]{
|
pageOrderBy := page.OrderBy[coredata.MembershipProfileOrderField]{
|
||||||
|
|||||||
@@ -2491,7 +2491,10 @@ func (r *Resolver) ListUsersTool(ctx context.Context, req *mcp.CallToolRequest,
|
|||||||
|
|
||||||
filter := coredata.NewMembershipProfileFilter(nil).WithMembership()
|
filter := coredata.NewMembershipProfileFilter(nil).WithMembership()
|
||||||
if input.Filter != nil {
|
if input.Filter != nil {
|
||||||
filter = coredata.NewMembershipProfileFilter(input.Filter.ExcludeContractEnded).WithMembership()
|
filter = coredata.NewMembershipProfileFilter(input.Filter.ContractEnded).WithMembership()
|
||||||
|
if input.Filter.State != nil {
|
||||||
|
filter.WithState(*input.Filter.State)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pageResult, err := r.iamSvc.OrganizationService.ListProfiles(ctx, input.OrganizationID, cursor, filter)
|
pageResult, err := r.iamSvc.OrganizationService.ListProfiles(ctx, input.OrganizationID, cursor, filter)
|
||||||
|
|||||||
@@ -180,6 +180,13 @@ components:
|
|||||||
- AUDITOR
|
- AUDITOR
|
||||||
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.MembershipRole
|
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.MembershipRole
|
||||||
|
|
||||||
|
ProfileState:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- ACTIVE
|
||||||
|
- INACTIVE
|
||||||
|
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ProfileState
|
||||||
|
|
||||||
Profile:
|
Profile:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
@@ -189,6 +196,7 @@ components:
|
|||||||
- email_address
|
- email_address
|
||||||
- additional_email_addresses
|
- additional_email_addresses
|
||||||
- kind
|
- kind
|
||||||
|
- state
|
||||||
- created_at
|
- created_at
|
||||||
- updated_at
|
- updated_at
|
||||||
properties:
|
properties:
|
||||||
@@ -214,6 +222,9 @@ components:
|
|||||||
- string
|
- string
|
||||||
- "null"
|
- "null"
|
||||||
description: Profile kind
|
description: Profile kind
|
||||||
|
state:
|
||||||
|
$ref: "#/components/schemas/ProfileState"
|
||||||
|
description: Profile state (ACTIVE or INACTIVE)
|
||||||
position:
|
position:
|
||||||
type:
|
type:
|
||||||
- string
|
- string
|
||||||
@@ -260,9 +271,12 @@ components:
|
|||||||
filter:
|
filter:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
exclude_contract_ended:
|
contract_ended:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Exclude users with ended contracts
|
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)
|
||||||
|
|
||||||
ListUsersOutput:
|
ListUsersOutput:
|
||||||
type: object
|
type: object
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ func NewProfile(p *coredata.MembershipProfile) *Profile {
|
|||||||
EmailAddress: p.EmailAddress,
|
EmailAddress: p.EmailAddress,
|
||||||
AdditionalEmailAddresses: p.AdditionalEmailAddresses,
|
AdditionalEmailAddresses: p.AdditionalEmailAddresses,
|
||||||
Kind: p.Kind,
|
Kind: p.Kind,
|
||||||
|
State: p.State,
|
||||||
Position: p.Position,
|
Position: p.Position,
|
||||||
ContractStartDate: p.ContractStartDate,
|
ContractStartDate: p.ContractStartDate,
|
||||||
ContractEndDate: p.ContractEndDate,
|
ContractEndDate: p.ContractEndDate,
|
||||||
|
|||||||
Reference in New Issue
Block a user