Add activeSession on membership

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2025-12-18 18:17:59 +01:00
parent 0a8a17934a
commit 7a26473e8f
8 changed files with 282 additions and 344 deletions

View File

@@ -300,3 +300,50 @@ WHERE
return result.RowsAffected(), nil return result.RowsAffected(), nil
} }
func (s *Session) LoadByRootSessionIDAndMembershipID(ctx context.Context, conn pg.Conn, rootSessionID gid.GID, membershipID gid.GID) error {
q := `
SELECT
id,
user_id,
tenant_id,
data,
parent_session_id,
expire_reason,
user_agent,
ip_address,
expired_at,
created_at,
updated_at
FROM
sessions
INNER JOIN
authz_memberships m ON m.organization_id = s.organization_id
WHERE
sessions.parent_session_id = @root_session_id
AND m.id = @membership_id
`
args := pgx.StrictNamedArgs{
"root_session_id": rootSessionID,
"membership_id": membershipID,
}
rows, err := conn.Query(ctx, q, args)
if err != nil {
if err == pgx.ErrNoRows {
return ErrResourceNotFound
}
return fmt.Errorf("cannot query session: %w", err)
}
session, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Session])
if err != nil {
return fmt.Errorf("cannot collect session: %w", err)
}
*s = session
return nil
}

View File

@@ -264,3 +264,49 @@ func (s SessionService) UpdateSessionData(ctx context.Context, sessionID gid.GID
}, },
) )
} }
func (s SessionService) GetActiveSessionForMembership(ctx context.Context, rootSessionID gid.GID, membershipID gid.GID) (*coredata.Session, error) {
childSession := &coredata.Session{}
err := s.pg.WithTx(
ctx,
func(tx pg.Conn) error {
rootSession := &coredata.Session{}
err := rootSession.LoadByID(ctx, tx, rootSessionID)
if err != nil {
if err == coredata.ErrResourceNotFound {
return NewSessionNotFoundError(rootSessionID)
}
return fmt.Errorf("cannot load root session: %w", err)
}
if !rootSession.IsRootSession() {
return fmt.Errorf("session %q is not a root session", rootSessionID)
}
membership := &coredata.Membership{}
err = membership.LoadByID(ctx, tx, coredata.NewScopeFromObjectID(membershipID), membershipID)
if err != nil {
return fmt.Errorf("cannot load membership: %w", err)
}
err = childSession.LoadByRootSessionIDAndMembershipID(ctx, tx, rootSessionID, membership.ID)
if err != nil {
if err == coredata.ErrResourceNotFound {
return NewSessionNotFoundError(rootSessionID)
}
return fmt.Errorf("cannot load child session: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return childSession, nil
}

View File

@@ -16,7 +16,6 @@ package connect_v1
import ( import (
"context" "context"
"fmt"
"net/http" "net/http"
"github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql"
@@ -72,14 +71,18 @@ func SessionDirective(ctx context.Context, obj any, next graphql.Resolver, requi
func IsViewerDirective(ctx context.Context, obj any, next graphql.Resolver) (any, error) { func IsViewerDirective(ctx context.Context, obj any, next graphql.Resolver) (any, error) {
identity := UserFromContext(ctx) identity := UserFromContext(ctx)
resolvedIdentity, ok := obj.(*types.Identity)
if !ok {
panic(fmt.Errorf("@isViewer called on non-identity object: %T", obj))
}
if identity.ID != resolvedIdentity.ID { switch node := obj.(type) {
case *types.Identity:
if identity.ID != node.ID {
return nil, ErrForbidden return nil, ErrForbidden
} }
case *types.Membership:
if identity.ID != node.IdentityID {
return nil, ErrForbidden
}
default:
}
return next(ctx) return next(ctx)
} }

View File

@@ -226,6 +226,7 @@ type Organization implements Node {
type Membership implements Node { type Membership implements Node {
id: ID! id: ID!
identityId: ID!
createdAt: Datetime! createdAt: Datetime!
profile: IdentityProfile! profile: IdentityProfile!
identity: Identity! @goField(forceResolver: true) identity: Identity! @goField(forceResolver: true)
@@ -234,6 +235,8 @@ type Membership implements Node {
provisionedBy: ProvisioningSource! provisionedBy: ProvisioningSource!
active: Boolean! active: Boolean!
lastSyncedAt: Datetime lastSyncedAt: Datetime
activeSession: Session @goField(forceResolver: true) @isViewer
} }
type Invitation implements Node { type Invitation implements Node {
@@ -245,14 +248,6 @@ type Invitation implements Node {
status: InvitationStatus! status: InvitationStatus!
} }
type InvitationProfile {
displayName: String!
firstName: String
lastName: String
jobTitle: String
department: String
}
type Session implements Node { type Session implements Node {
id: ID! id: ID!
ipAddress: String! ipAddress: String!
@@ -635,14 +630,6 @@ input RemoveMemberInput {
membershipId: ID! membershipId: ID!
} }
input InvitationProfileInput {
displayName: String!
firstName: String
lastName: String
jobTitle: String
department: String
}
input AcceptInvitationInput { input AcceptInvitationInput {
invitationId: ID! invitationId: ID!
} }

View File

@@ -180,23 +180,17 @@ type ComplexityRoot struct {
Node func(childComplexity int) int Node func(childComplexity int) int
} }
InvitationProfile struct {
Department func(childComplexity int) int
DisplayName func(childComplexity int) int
FirstName func(childComplexity int) int
JobTitle func(childComplexity int) int
LastName func(childComplexity int) int
}
InviteMemberPayload struct { InviteMemberPayload struct {
InvitationEdge func(childComplexity int) int InvitationEdge func(childComplexity int) int
} }
Membership struct { Membership struct {
Active func(childComplexity int) int Active func(childComplexity int) int
ActiveSession func(childComplexity int) int
CreatedAt func(childComplexity int) int CreatedAt func(childComplexity int) int
ID func(childComplexity int) int ID func(childComplexity int) int
Identity func(childComplexity int) int Identity func(childComplexity int) int
IdentityID func(childComplexity int) int
LastSyncedAt func(childComplexity int) int LastSyncedAt func(childComplexity int) int
Organization func(childComplexity int) int Organization func(childComplexity int) int
Permissions func(childComplexity int) int Permissions func(childComplexity int) int
@@ -445,6 +439,8 @@ type InvitationConnectionResolver interface {
type MembershipResolver interface { type MembershipResolver interface {
Identity(ctx context.Context, obj *types.Membership) (*types.Identity, error) Identity(ctx context.Context, obj *types.Membership) (*types.Identity, error)
Organization(ctx context.Context, obj *types.Membership) (*types.Organization, error) Organization(ctx context.Context, obj *types.Membership) (*types.Organization, error)
ActiveSession(ctx context.Context, obj *types.Membership) (*types.Session, error)
} }
type MembershipConnectionResolver interface { type MembershipConnectionResolver interface {
TotalCount(ctx context.Context, obj *types.MembershipConnection) (int, error) TotalCount(ctx context.Context, obj *types.MembershipConnection) (int, error)
@@ -916,37 +912,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return e.complexity.InvitationEdge.Node(childComplexity), true return e.complexity.InvitationEdge.Node(childComplexity), true
case "InvitationProfile.department":
if e.complexity.InvitationProfile.Department == nil {
break
}
return e.complexity.InvitationProfile.Department(childComplexity), true
case "InvitationProfile.displayName":
if e.complexity.InvitationProfile.DisplayName == nil {
break
}
return e.complexity.InvitationProfile.DisplayName(childComplexity), true
case "InvitationProfile.firstName":
if e.complexity.InvitationProfile.FirstName == nil {
break
}
return e.complexity.InvitationProfile.FirstName(childComplexity), true
case "InvitationProfile.jobTitle":
if e.complexity.InvitationProfile.JobTitle == nil {
break
}
return e.complexity.InvitationProfile.JobTitle(childComplexity), true
case "InvitationProfile.lastName":
if e.complexity.InvitationProfile.LastName == nil {
break
}
return e.complexity.InvitationProfile.LastName(childComplexity), true
case "InviteMemberPayload.invitationEdge": case "InviteMemberPayload.invitationEdge":
if e.complexity.InviteMemberPayload.InvitationEdge == nil { if e.complexity.InviteMemberPayload.InvitationEdge == nil {
break break
@@ -960,6 +925,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
} }
return e.complexity.Membership.Active(childComplexity), true return e.complexity.Membership.Active(childComplexity), true
case "Membership.activeSession":
if e.complexity.Membership.ActiveSession == nil {
break
}
return e.complexity.Membership.ActiveSession(childComplexity), true
case "Membership.createdAt": case "Membership.createdAt":
if e.complexity.Membership.CreatedAt == nil { if e.complexity.Membership.CreatedAt == nil {
break break
@@ -978,6 +949,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
} }
return e.complexity.Membership.Identity(childComplexity), true return e.complexity.Membership.Identity(childComplexity), true
case "Membership.identityId":
if e.complexity.Membership.IdentityID == nil {
break
}
return e.complexity.Membership.IdentityID(childComplexity), true
case "Membership.lastSyncedAt": case "Membership.lastSyncedAt":
if e.complexity.Membership.LastSyncedAt == nil { if e.complexity.Membership.LastSyncedAt == nil {
break break
@@ -1960,7 +1937,6 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
ec.unmarshalInputDeleteOrganizationInput, ec.unmarshalInputDeleteOrganizationInput,
ec.unmarshalInputDeleteSAMLConfigurationInput, ec.unmarshalInputDeleteSAMLConfigurationInput,
ec.unmarshalInputForgotPasswordInput, ec.unmarshalInputForgotPasswordInput,
ec.unmarshalInputInvitationProfileInput,
ec.unmarshalInputInviteMemberInput, ec.unmarshalInputInviteMemberInput,
ec.unmarshalInputMembershipOrder, ec.unmarshalInputMembershipOrder,
ec.unmarshalInputRemoveIPAllowlistEntryInput, ec.unmarshalInputRemoveIPAllowlistEntryInput,
@@ -2304,6 +2280,7 @@ type Organization implements Node {
type Membership implements Node { type Membership implements Node {
id: ID! id: ID!
identityId: ID!
createdAt: Datetime! createdAt: Datetime!
profile: IdentityProfile! profile: IdentityProfile!
identity: Identity! @goField(forceResolver: true) identity: Identity! @goField(forceResolver: true)
@@ -2312,6 +2289,8 @@ type Membership implements Node {
provisionedBy: ProvisioningSource! provisionedBy: ProvisioningSource!
active: Boolean! active: Boolean!
lastSyncedAt: Datetime lastSyncedAt: Datetime
activeSession: Session @goField(forceResolver: true) @isViewer
} }
type Invitation implements Node { type Invitation implements Node {
@@ -2323,14 +2302,6 @@ type Invitation implements Node {
status: InvitationStatus! status: InvitationStatus!
} }
type InvitationProfile {
displayName: String!
firstName: String
lastName: String
jobTitle: String
department: String
}
type Session implements Node { type Session implements Node {
id: ID! id: ID!
ipAddress: String! ipAddress: String!
@@ -2713,14 +2684,6 @@ input RemoveMemberInput {
membershipId: ID! membershipId: ID!
} }
input InvitationProfileInput {
displayName: String!
firstName: String
lastName: String
jobTitle: String
department: String
}
input AcceptInvitationInput { input AcceptInvitationInput {
invitationId: ID! invitationId: ID!
} }
@@ -5506,151 +5469,6 @@ func (ec *executionContext) fieldContext_InvitationEdge_cursor(_ context.Context
return fc, nil return fc, nil
} }
func (ec *executionContext) _InvitationProfile_displayName(ctx context.Context, field graphql.CollectedField, obj *types.InvitationProfile) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_InvitationProfile_displayName,
func(ctx context.Context) (any, error) {
return obj.DisplayName, nil
},
nil,
ec.marshalNString2string,
true,
true,
)
}
func (ec *executionContext) fieldContext_InvitationProfile_displayName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "InvitationProfile",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _InvitationProfile_firstName(ctx context.Context, field graphql.CollectedField, obj *types.InvitationProfile) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_InvitationProfile_firstName,
func(ctx context.Context) (any, error) {
return obj.FirstName, nil
},
nil,
ec.marshalOString2ᚖstring,
true,
false,
)
}
func (ec *executionContext) fieldContext_InvitationProfile_firstName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "InvitationProfile",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _InvitationProfile_lastName(ctx context.Context, field graphql.CollectedField, obj *types.InvitationProfile) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_InvitationProfile_lastName,
func(ctx context.Context) (any, error) {
return obj.LastName, nil
},
nil,
ec.marshalOString2ᚖstring,
true,
false,
)
}
func (ec *executionContext) fieldContext_InvitationProfile_lastName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "InvitationProfile",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _InvitationProfile_jobTitle(ctx context.Context, field graphql.CollectedField, obj *types.InvitationProfile) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_InvitationProfile_jobTitle,
func(ctx context.Context) (any, error) {
return obj.JobTitle, nil
},
nil,
ec.marshalOString2ᚖstring,
true,
false,
)
}
func (ec *executionContext) fieldContext_InvitationProfile_jobTitle(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "InvitationProfile",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _InvitationProfile_department(ctx context.Context, field graphql.CollectedField, obj *types.InvitationProfile) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_InvitationProfile_department,
func(ctx context.Context) (any, error) {
return obj.Department, nil
},
nil,
ec.marshalOString2ᚖstring,
true,
false,
)
}
func (ec *executionContext) fieldContext_InvitationProfile_department(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "InvitationProfile",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _InviteMemberPayload_invitationEdge(ctx context.Context, field graphql.CollectedField, obj *types.InviteMemberPayload) (ret graphql.Marshaler) { func (ec *executionContext) _InviteMemberPayload_invitationEdge(ctx context.Context, field graphql.CollectedField, obj *types.InviteMemberPayload) (ret graphql.Marshaler) {
return graphql.ResolveField( return graphql.ResolveField(
ctx, ctx,
@@ -5715,6 +5533,35 @@ func (ec *executionContext) fieldContext_Membership_id(_ context.Context, field
return fc, nil return fc, nil
} }
func (ec *executionContext) _Membership_identityId(ctx context.Context, field graphql.CollectedField, obj *types.Membership) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_Membership_identityId,
func(ctx context.Context) (any, error) {
return obj.IdentityID, nil
},
nil,
ec.marshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID,
true,
true,
)
}
func (ec *executionContext) fieldContext_Membership_identityId(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Membership",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type ID does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _Membership_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.Membership) (ret graphql.Marshaler) { func (ec *executionContext) _Membership_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.Membership) (ret graphql.Marshaler) {
return graphql.ResolveField( return graphql.ResolveField(
ctx, ctx,
@@ -6045,6 +5892,62 @@ func (ec *executionContext) fieldContext_Membership_lastSyncedAt(_ context.Conte
return fc, nil return fc, nil
} }
func (ec *executionContext) _Membership_activeSession(ctx context.Context, field graphql.CollectedField, obj *types.Membership) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_Membership_activeSession,
func(ctx context.Context) (any, error) {
return ec.resolvers.Membership().ActiveSession(ctx, obj)
},
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
directive0 := next
directive1 := func(ctx context.Context) (any, error) {
if ec.directives.IsViewer == nil {
var zeroVal *types.Session
return zeroVal, errors.New("directive isViewer is not implemented")
}
return ec.directives.IsViewer(ctx, obj, directive0)
}
next = directive1
return next
},
ec.marshalOSession2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐSession,
true,
false,
)
}
func (ec *executionContext) fieldContext_Membership_activeSession(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Membership",
Field: field,
IsMethod: true,
IsResolver: true,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "id":
return ec.fieldContext_Session_id(ctx, field)
case "ipAddress":
return ec.fieldContext_Session_ipAddress(ctx, field)
case "userAgent":
return ec.fieldContext_Session_userAgent(ctx, field)
case "updatedAt":
return ec.fieldContext_Session_updatedAt(ctx, field)
case "createdAt":
return ec.fieldContext_Session_createdAt(ctx, field)
case "expiresAt":
return ec.fieldContext_Session_expiresAt(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type Session", field.Name)
},
}
return fc, nil
}
func (ec *executionContext) _MembershipConnection_edges(ctx context.Context, field graphql.CollectedField, obj *types.MembershipConnection) (ret graphql.Marshaler) { func (ec *executionContext) _MembershipConnection_edges(ctx context.Context, field graphql.CollectedField, obj *types.MembershipConnection) (ret graphql.Marshaler) {
return graphql.ResolveField( return graphql.ResolveField(
ctx, ctx,
@@ -6174,6 +6077,8 @@ func (ec *executionContext) fieldContext_MembershipEdge_node(_ context.Context,
switch field.Name { switch field.Name {
case "id": case "id":
return ec.fieldContext_Membership_id(ctx, field) return ec.fieldContext_Membership_id(ctx, field)
case "identityId":
return ec.fieldContext_Membership_identityId(ctx, field)
case "createdAt": case "createdAt":
return ec.fieldContext_Membership_createdAt(ctx, field) return ec.fieldContext_Membership_createdAt(ctx, field)
case "profile": case "profile":
@@ -6190,6 +6095,8 @@ func (ec *executionContext) fieldContext_MembershipEdge_node(_ context.Context,
return ec.fieldContext_Membership_active(ctx, field) return ec.fieldContext_Membership_active(ctx, field)
case "lastSyncedAt": case "lastSyncedAt":
return ec.fieldContext_Membership_lastSyncedAt(ctx, field) return ec.fieldContext_Membership_lastSyncedAt(ctx, field)
case "activeSession":
return ec.fieldContext_Membership_activeSession(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type Membership", field.Name) return nil, fmt.Errorf("no field named %q was found under type Membership", field.Name)
}, },
@@ -13139,61 +13046,6 @@ func (ec *executionContext) unmarshalInputForgotPasswordInput(ctx context.Contex
return it, nil return it, nil
} }
func (ec *executionContext) unmarshalInputInvitationProfileInput(ctx context.Context, obj any) (types.InvitationProfileInput, error) {
var it types.InvitationProfileInput
asMap := map[string]any{}
for k, v := range obj.(map[string]any) {
asMap[k] = v
}
fieldsInOrder := [...]string{"displayName", "firstName", "lastName", "jobTitle", "department"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
continue
}
switch k {
case "displayName":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("displayName"))
data, err := ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
it.DisplayName = data
case "firstName":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("firstName"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
it.FirstName = data
case "lastName":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("lastName"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
it.LastName = data
case "jobTitle":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("jobTitle"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
it.JobTitle = data
case "department":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("department"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
it.Department = data
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputInviteMemberInput(ctx context.Context, obj any) (types.InviteMemberInput, error) { func (ec *executionContext) unmarshalInputInviteMemberInput(ctx context.Context, obj any) (types.InviteMemberInput, error) {
var it types.InviteMemberInput var it types.InviteMemberInput
asMap := map[string]any{} asMap := map[string]any{}
@@ -15069,53 +14921,6 @@ func (ec *executionContext) _InvitationEdge(ctx context.Context, sel ast.Selecti
return out return out
} }
var invitationProfileImplementors = []string{"InvitationProfile"}
func (ec *executionContext) _InvitationProfile(ctx context.Context, sel ast.SelectionSet, obj *types.InvitationProfile) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, invitationProfileImplementors)
out := graphql.NewFieldSet(fields)
deferred := make(map[string]*graphql.FieldSet)
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("InvitationProfile")
case "displayName":
out.Values[i] = ec._InvitationProfile_displayName(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "firstName":
out.Values[i] = ec._InvitationProfile_firstName(ctx, field, obj)
case "lastName":
out.Values[i] = ec._InvitationProfile_lastName(ctx, field, obj)
case "jobTitle":
out.Values[i] = ec._InvitationProfile_jobTitle(ctx, field, obj)
case "department":
out.Values[i] = ec._InvitationProfile_department(ctx, field, obj)
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch(ctx)
if out.Invalids > 0 {
return graphql.Null
}
atomic.AddInt32(&ec.deferred, int32(len(deferred)))
for label, dfs := range deferred {
ec.processDeferredGroup(graphql.DeferredGroup{
Label: label,
Path: graphql.GetPath(ctx),
FieldSet: dfs,
Context: ctx,
})
}
return out
}
var inviteMemberPayloadImplementors = []string{"InviteMemberPayload"} var inviteMemberPayloadImplementors = []string{"InviteMemberPayload"}
func (ec *executionContext) _InviteMemberPayload(ctx context.Context, sel ast.SelectionSet, obj *types.InviteMemberPayload) graphql.Marshaler { func (ec *executionContext) _InviteMemberPayload(ctx context.Context, sel ast.SelectionSet, obj *types.InviteMemberPayload) graphql.Marshaler {
@@ -15171,6 +14976,11 @@ func (ec *executionContext) _Membership(ctx context.Context, sel ast.SelectionSe
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1) atomic.AddUint32(&out.Invalids, 1)
} }
case "identityId":
out.Values[i] = ec._Membership_identityId(ctx, field, obj)
if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1)
}
case "createdAt": case "createdAt":
out.Values[i] = ec._Membership_createdAt(ctx, field, obj) out.Values[i] = ec._Membership_createdAt(ctx, field, obj)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
@@ -15270,6 +15080,39 @@ func (ec *executionContext) _Membership(ctx context.Context, sel ast.SelectionSe
} }
case "lastSyncedAt": case "lastSyncedAt":
out.Values[i] = ec._Membership_lastSyncedAt(ctx, field, obj) out.Values[i] = ec._Membership_lastSyncedAt(ctx, field, obj)
case "activeSession":
field := field
innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Membership_activeSession(ctx, field, obj)
return res
}
if field.Deferrable != nil {
dfs, ok := deferred[field.Deferrable.Label]
di := 0
if ok {
dfs.AddField(field)
di = len(dfs.Values) - 1
} else {
dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
deferred[field.Deferrable.Label] = dfs
}
dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
return innerFunc(ctx, dfs)
})
// don't run the out.Concurrently() call below
out.Values[i] = graphql.Null
continue
}
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
default: default:
panic("unknown field " + strconv.Quote(field.Name)) panic("unknown field " + strconv.Quote(field.Name))
} }
@@ -19889,6 +19732,13 @@ var (
} }
) )
func (ec *executionContext) marshalOSession2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐSession(ctx context.Context, sel ast.SelectionSet, v *types.Session) graphql.Marshaler {
if v == nil {
return graphql.Null
}
return ec._Session(ctx, sel, v)
}
func (ec *executionContext) unmarshalOSessionOrder2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐSessionOrder(ctx context.Context, v any) (*types.SessionOrder, error) { func (ec *executionContext) unmarshalOSessionOrder2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐSessionOrder(ctx context.Context, v any) (*types.SessionOrder, error) {
if v == nil { if v == nil {
return nil, nil return nil, nil

View File

@@ -62,6 +62,7 @@ func NewMembershipEdge(membership *coredata.Membership, orderField coredata.Memb
func NewMembership(membership *coredata.Membership) *Membership { func NewMembership(membership *coredata.Membership) *Membership {
return &Membership{ return &Membership{
ID: membership.ID, ID: membership.ID,
IdentityID: membership.UserID,
CreatedAt: membership.CreatedAt, CreatedAt: membership.CreatedAt,
// Permissions: membership.Permissions, // Permissions: membership.Permissions,
// ProvisionedBy: membership.ProvisionedBy, // ProvisionedBy: membership.ProvisionedBy,

View File

@@ -209,22 +209,6 @@ type InvitationEdge struct {
Cursor page.CursorKey `json:"cursor"` Cursor page.CursorKey `json:"cursor"`
} }
type InvitationProfile struct {
DisplayName string `json:"displayName"`
FirstName *string `json:"firstName,omitempty"`
LastName *string `json:"lastName,omitempty"`
JobTitle *string `json:"jobTitle,omitempty"`
Department *string `json:"department,omitempty"`
}
type InvitationProfileInput struct {
DisplayName string `json:"displayName"`
FirstName *string `json:"firstName,omitempty"`
LastName *string `json:"lastName,omitempty"`
JobTitle *string `json:"jobTitle,omitempty"`
Department *string `json:"department,omitempty"`
}
type InviteMemberInput struct { type InviteMemberInput struct {
OrganizationID gid.GID `json:"organizationId"` OrganizationID gid.GID `json:"organizationId"`
Email mail.Addr `json:"email"` Email mail.Addr `json:"email"`
@@ -237,6 +221,7 @@ type InviteMemberPayload struct {
type Membership struct { type Membership struct {
ID gid.GID `json:"id"` ID gid.GID `json:"id"`
IdentityID gid.GID `json:"identityId"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
Profile *IdentityProfile `json:"profile"` Profile *IdentityProfile `json:"profile"`
Identity *Identity `json:"identity"` Identity *Identity `json:"identity"`
@@ -245,6 +230,7 @@ type Membership struct {
ProvisionedBy ProvisioningSource `json:"provisionedBy"` ProvisionedBy ProvisioningSource `json:"provisionedBy"`
Active bool `json:"active"` Active bool `json:"active"`
LastSyncedAt *time.Time `json:"lastSyncedAt,omitempty"` LastSyncedAt *time.Time `json:"lastSyncedAt,omitempty"`
ActiveSession *Session `json:"activeSession,omitempty"`
} }
func (Membership) IsNode() {} func (Membership) IsNode() {}

View File

@@ -138,6 +138,24 @@ func (r *membershipResolver) Organization(ctx context.Context, obj *types.Member
return types.NewOrganization(organization), nil return types.NewOrganization(organization), nil
} }
// ActiveSession is the resolver for the activeSession field.
func (r *membershipResolver) ActiveSession(ctx context.Context, obj *types.Membership) (*types.Session, error) {
rootSession := SessionFromContext(ctx)
session, err := r.iam.SessionService.GetActiveSessionForMembership(ctx, rootSession.UserID, obj.ID)
if err != nil {
var errSessionNotFound *iam.ErrSessionNotFound
if errors.As(err, &errSessionNotFound) {
return nil, nil
}
panic(fmt.Errorf("cannot get active session: %w", err))
}
return types.NewSession(session), nil
}
// TotalCount is the resolver for the totalCount field. // TotalCount is the resolver for the totalCount field.
func (r *membershipConnectionResolver) TotalCount(ctx context.Context, obj *types.MembershipConnection) (int, error) { func (r *membershipConnectionResolver) TotalCount(ctx context.Context, obj *types.MembershipConnection) (int, error) {
switch obj.Resolver.(type) { switch obj.Resolver.(type) {