From 925c53d689baaa2a0c68d029bd8ee39d8b59f7cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Tue, 14 Apr 2026 18:27:34 +0400 Subject: [PATCH] Slim base.graphql to directives, scalars, Query, Node, and Mutation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move Identity, Organization, Viewer, PageInfo, OrderDirection, CountryCode, OIDCProviderInfo, File, and ReauthenticationReason out of base.graphql into their own dedicated files across all three APIs. base.graphql now only contains directives, scalars, Node interface, Query type, and an empty Mutation type (required by Relay schemaExtensions). Entity files use extend type Mutation for their mutations. Signed-off-by: Émile Ré --- pkg/server/api/connect/v1/base.resolvers.go | 615 ------------------ .../api/connect/v1/graphql/base.graphql | 91 +-- .../api/connect/v1/graphql/identity.graphql | 40 ++ .../api/connect/v1/graphql/oidc.graphql | 4 + .../api/connect/v1/graphql/pagination.graphql | 12 + .../api/connect/v1/graphql/session.graphql | 32 + .../api/connect/v1/identity.resolvers.go | 185 ++++++ .../api/connect/v1/session.resolvers.go | 454 +++++++++++++ pkg/server/api/console/v1/base.resolvers.go | 185 ------ pkg/server/api/console/v1/file.resolvers.go | 39 ++ .../api/console/v1/graphql/base.graphql | 308 --------- .../console/v1/graphql/country_code.graphql | 253 +++++++ .../api/console/v1/graphql/file.graphql | 9 + .../api/console/v1/graphql/pagination.graphql | 12 + .../api/console/v1/graphql/viewer.graphql | 25 + pkg/server/api/console/v1/viewer.resolvers.go | 184 ++++++ pkg/server/api/trust/v1/auth.resolvers.go | 179 +++++ pkg/server/api/trust/v1/base.resolvers.go | 173 ----- pkg/server/api/trust/v1/graphql/auth.graphql | 9 + pkg/server/api/trust/v1/graphql/base.graphql | 294 +-------- .../api/trust/v1/graphql/country_code.graphql | 253 +++++++ .../api/trust/v1/graphql/identity.graphql | 8 + pkg/server/api/trust/v1/graphql/oidc.graphql | 4 + .../api/trust/v1/graphql/organization.graphql | 10 + .../api/trust/v1/graphql/pagination.graphql | 6 + .../api/trust/v1/organization.resolvers.go | 26 + 26 files changed, 1746 insertions(+), 1664 deletions(-) create mode 100644 pkg/server/api/connect/v1/graphql/identity.graphql create mode 100644 pkg/server/api/connect/v1/graphql/oidc.graphql create mode 100644 pkg/server/api/connect/v1/graphql/pagination.graphql create mode 100644 pkg/server/api/connect/v1/identity.resolvers.go create mode 100644 pkg/server/api/console/v1/file.resolvers.go create mode 100644 pkg/server/api/console/v1/graphql/country_code.graphql create mode 100644 pkg/server/api/console/v1/graphql/file.graphql create mode 100644 pkg/server/api/console/v1/graphql/pagination.graphql create mode 100644 pkg/server/api/console/v1/graphql/viewer.graphql create mode 100644 pkg/server/api/console/v1/viewer.resolvers.go create mode 100644 pkg/server/api/trust/v1/auth.resolvers.go create mode 100644 pkg/server/api/trust/v1/graphql/country_code.graphql create mode 100644 pkg/server/api/trust/v1/graphql/identity.graphql create mode 100644 pkg/server/api/trust/v1/graphql/oidc.graphql create mode 100644 pkg/server/api/trust/v1/graphql/organization.graphql create mode 100644 pkg/server/api/trust/v1/graphql/pagination.graphql create mode 100644 pkg/server/api/trust/v1/organization.resolvers.go diff --git a/pkg/server/api/connect/v1/base.resolvers.go b/pkg/server/api/connect/v1/base.resolvers.go index a9629e840..834914317 100644 --- a/pkg/server/api/connect/v1/base.resolvers.go +++ b/pkg/server/api/connect/v1/base.resolvers.go @@ -12,628 +12,17 @@ import ( "strings" "github.com/99designs/gqlgen/graphql" - "github.com/vektah/gqlparser/v2/gqlerror" "go.gearno.de/kit/log" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/iam" "go.probo.inc/probo/pkg/mail" - "go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/server/api/authn" - "go.probo.inc/probo/pkg/server/api/authz" "go.probo.inc/probo/pkg/server/api/connect/v1/schema" "go.probo.inc/probo/pkg/server/api/connect/v1/types" "go.probo.inc/probo/pkg/server/gqlutils" - "go.probo.inc/probo/pkg/server/gqlutils/types/cursor" ) -// Profiles is the resolver for the profiles field. -func (r *identityResolver) Profiles(ctx context.Context, obj *types.Identity, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ProfileOrderBy, filter *types.ProfileFilter) (*types.ProfileConnection, error) { - if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileList, authz.WithSkipAssumptionCheck()); err != nil { - return nil, err - } - - filters := coredata.NewMembershipProfileFilter(nil).WithMembership() - if filter != nil { - filters = coredata.NewMembershipProfileFilter(filter.ExcludeContractEnded).WithMembership() - if filter.State != nil { - filters.WithState(*filter.State) - } - } - - if gqlutils.OnlyTotalCountSelected(ctx) { - return &types.ProfileConnection{ - Resolver: r, - ParentID: obj.ID, - Filters: filters, - }, nil - } - - pageOrderBy := page.OrderBy[coredata.MembershipProfileOrderField]{ - Field: coredata.MembershipProfileOrderFieldFullName, - Direction: page.OrderDirectionAsc, - } - if orderBy != nil { - pageOrderBy = page.OrderBy[coredata.MembershipProfileOrderField]{ - Field: orderBy.Field, - Direction: orderBy.Direction, - } - } - - cursor := cursor.NewCursor(first, after, last, before, pageOrderBy) - - page, err := r.iam.AccountService.ListProfilesForIdentity(ctx, obj.ID, cursor, filters) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot list profiles", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewProfileConnection(page, r, obj.ID, filters), nil -} - -// Sessions is the resolver for the sessions field. -func (r *identityResolver) Sessions(ctx context.Context, obj *types.Identity, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.SessionOrder) (*types.SessionConnection, error) { - if err := r.authorize(ctx, obj.ID, iam.ActionSessionList); err != nil { - return nil, err - } - - if gqlutils.OnlyTotalCountSelected(ctx) { - return &types.SessionConnection{ - Resolver: r, - ParentID: obj.ID, - }, nil - } - - pageOrderBy := page.OrderBy[coredata.SessionOrderField]{ - Field: coredata.SessionOrderFieldCreatedAt, - Direction: page.OrderDirectionDesc, - } - if orderBy != nil { - pageOrderBy = page.OrderBy[coredata.SessionOrderField]{ - Field: orderBy.Field, - Direction: orderBy.Direction, - } - } - - cursor := cursor.NewCursor(first, after, last, before, pageOrderBy) - - page, err := r.iam.AccountService.ListSessions(ctx, obj.ID, cursor) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot list sessions", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewSessionConnection(page, r, obj.ID), nil -} - -// PersonalAPIKeys is the resolver for the personalAPIKeys field. -func (r *identityResolver) PersonalAPIKeys(ctx context.Context, obj *types.Identity, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.PersonalAPIKeyConnection, error) { - if err := r.authorize(ctx, obj.ID, iam.ActionPersonalAPIKeyList); err != nil { - return nil, err - } - - if gqlutils.OnlyTotalCountSelected(ctx) { - return &types.PersonalAPIKeyConnection{ - Resolver: r, - ParentID: obj.ID, - }, nil - } - - pageOrderBy := page.OrderBy[coredata.PersonalAPIKeyOrderField]{ - Field: coredata.PersonalAPIKeyOrderFieldCreatedAt, - Direction: page.OrderDirectionDesc, - } - - cursor := cursor.NewCursor(first, after, last, before, pageOrderBy) - - page, err := r.iam.AccountService.ListPersonalAPIKeys(ctx, obj.ID, cursor) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot list personal api keys", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return types.NewPersonalAPIKeyConnection(page, r, obj.ID), nil -} - -// SsoLoginURL is the resolver for the ssoLoginURL field. -func (r *identityResolver) SsoLoginURL(ctx context.Context, obj *types.Identity) (*string, error) { - if err := r.authorize(ctx, obj.ID, iam.ActionIdentityGet); err != nil { - return nil, err - } - - identity := authn.IdentityFromContext(ctx) - - count, err := r.iam.AccountService.CountSAMLConfigurationsForEmail(ctx, identity.EmailAddress) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot count SAML configurations for email", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - if count != 1 { - if count == 0 { - return nil, graphql.ErrorOnPath( - ctx, - fmt.Errorf("no SAML configuration for email"), - ) - } - - return nil, graphql.ErrorOnPath( - ctx, - fmt.Errorf("multiple SSO configurations found for this domain. Please use your organization-specific SSO login URL"), - ) - } - - samlConfigs, err := r.iam.AccountService.ListSAMLConfigurationsForEmail(ctx, identity.EmailAddress) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot list SAML configurations for email", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - if len(samlConfigs) == 0 { - r.logger.ErrorCtx(ctx, "cannot find SAML config") - return nil, gqlutils.NotFoundf(ctx, "cannot find SAML config") - } - samlConfig := samlConfigs[0] - - loginURL := r.SSOLoginURL(samlConfig.ID) - return &loginURL, nil -} - -// Permission is the resolver for the permission field. -func (r *identityResolver) Permission(ctx context.Context, obj *types.Identity, action string) (bool, error) { - return r.Resolver.Permission(ctx, obj, action) -} - -// SignIn is the resolver for the signIn field. -func (r *mutationResolver) SignIn(ctx context.Context, input types.SignInInput) (*types.SignInPayload, error) { - identity, err := r.iam.AuthService.CheckCredentials(ctx, input.Email, input.Password) - if err != nil { - var errInvalidPassword *iam.ErrInvalidPassword - if errors.As(err, &errInvalidPassword) { - return nil, gqlutils.Invalid(ctx, err) - } - - var errInvalidCredentials *iam.ErrInvalidCredentials - if errors.As(err, &errInvalidCredentials) { - return nil, &gqlerror.Error{ - Message: err.Error(), - Extensions: map[string]any{ - "code": "INVALID_CREDENTIALS", - }, - } - } - - r.logger.ErrorCtx(ctx, "cannot check credentials", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - session := authn.SessionFromContext(ctx) - - switch { - case session == nil: - var err error - session, err = r.iam.AuthService.OpenSessionWithPassword( - ctx, - identity.ID, - ) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot create session", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - case session.IdentityID != identity.ID: - if err := r.iam.SessionService.CloseSession(ctx, session.ID); err != nil { - r.logger.ErrorCtx(ctx, "cannot close session", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - session, err = r.iam.AuthService.OpenSessionWithPassword( - ctx, - identity.ID, - ) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot create session", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - } - - w := gqlutils.HTTPResponseWriterFromContext(ctx) - r.sessionCookie.Set(w, session) - - if input.OrganizationID != nil { - var err error - _, _, err = r.iam.SessionService.OpenPasswordChildSessionForOrganization(ctx, session.ID, *input.OrganizationID) - if err != nil { - // Here session middleware already took care of expired/nil root session so we only handle membership related errors - var errMembershipNotFound *iam.ErrMembershipNotFound - var errUserInactive *iam.ErrUserInactive - - if errors.As(err, &errMembershipNotFound) || errors.As(err, &errUserInactive) { - return nil, gqlutils.Forbiddenf(ctx, "forbidden") - } - - r.logger.ErrorCtx(ctx, "cannot assume organization", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - } - - return &types.SignInPayload{ - Identity: types.NewIdentity(identity), - Session: types.NewSession(session), - }, nil -} - -// SignUp is the resolver for the signUp field. -func (r *mutationResolver) SignUp(ctx context.Context, input types.SignUpInput) (*types.SignUpPayload, error) { - identity, session, err := r.iam.AuthService.CreateIdentityWithPassword( - ctx, - &iam.CreateIdentityWithPasswordRequest{ - Email: input.Email, - Password: input.Password, - FullName: input.FullName, - }, - ) - if err != nil { - var errIdentityAlreadyExists *iam.ErrIdentityAlreadyExists - if errors.As(err, &errIdentityAlreadyExists) { - return nil, gqlutils.Invalid(ctx, err) - } - - var errSignupDisabled *iam.ErrSignupDisabled - if errors.As(err, &errSignupDisabled) { - return nil, gqlutils.Forbidden(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot create identity with password", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - w := gqlutils.HTTPResponseWriterFromContext(ctx) - r.sessionCookie.Set(w, session) - - return &types.SignUpPayload{ - Identity: types.NewIdentity(identity), - }, nil -} - -// SignOut is the resolver for the signOut field. -func (r *mutationResolver) SignOut(ctx context.Context) (*types.SignOutPayload, error) { - session := authn.SessionFromContext(ctx) - - err := r.iam.SessionService.CloseSession(ctx, session.ID) - if err != nil { - var ErrSessionNotFound *iam.ErrSessionNotFound - if errors.As(err, &ErrSessionNotFound) { - return &types.SignOutPayload{}, nil - } - - r.logger.ErrorCtx(ctx, "cannot close session", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - w := gqlutils.HTTPResponseWriterFromContext(ctx) - r.sessionCookie.Clear(w) - - return &types.SignOutPayload{Success: true}, nil -} - -// ActivateAccount is the resolver for the activateAccount field. -func (r *mutationResolver) ActivateAccount(ctx context.Context, input types.ActivateAccountInput) (*types.ActivateAccountPayload, error) { - session := authn.SessionFromContext(ctx) - - if session != nil { - // Sign out any other account before activating a new one - err := r.iam.SessionService.CloseSession(ctx, session.ID) - if err != nil { - var ErrSessionNotFound *iam.ErrSessionNotFound - if !errors.As(err, &ErrSessionNotFound) { - r.logger.ErrorCtx(ctx, "cannot close session", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - } - - w := gqlutils.HTTPResponseWriterFromContext(ctx) - r.sessionCookie.Clear(w) - } - - identity, user, err := r.iam.AuthService.ActivateAccount( - ctx, - &iam.ActivateAccountRequest{ - InvitationToken: input.Token, - }, - ) - if err != nil { - var ( - errInvalidToken *iam.ErrInvalidToken - errInvitationNotFound *iam.ErrInvitationNotFound - errInvitationExpired *iam.ErrInvitationExpired - - isInvalidErr = errors.As(err, &errInvalidToken) || - errors.As(err, &errInvitationNotFound) || - errors.As(err, &errInvitationExpired) - ) - - if isInvalidErr { - return nil, gqlutils.Invalid(ctx, err) - } - - if _, ok := errors.AsType[*iam.ErrInvitationAlreadyAccepted](err); ok { - return nil, gqlutils.AccountAlreadyActivated(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot activate account from invitation", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - var ssoLoginURL *string - samlConfigs, err := r.iam.AccountService.ListSAMLConfigurationsForEmail(ctx, user.EmailAddress) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot list saml configurations", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - for _, samlConfig := range samlConfigs { - if samlConfig.OrganizationID != user.OrganizationID { - continue - } - - ssoLoginURL = new(r.SSOLoginURL(samlConfig.ID)) - } - - if ssoLoginURL != nil { - return &types.ActivateAccountPayload{ - CreatePasswordToken: nil, - SsoLoginURL: ssoLoginURL, - Profile: types.NewProfile(user), - }, nil - } - - var createPasswordToken *string - if identity.HashedPassword == nil { - token, err := r.iam.AuthService.GetResetPasswordToken(ctx, identity.EmailAddress) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot generate password create token", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - createPasswordToken = &token - } - - return &types.ActivateAccountPayload{ - CreatePasswordToken: createPasswordToken, - SsoLoginURL: nil, - Profile: types.NewProfile(user), - }, nil -} - -// ForgotPassword is the resolver for the forgotPassword field. -func (r *mutationResolver) ForgotPassword(ctx context.Context, input types.ForgotPasswordInput) (*types.ForgotPasswordPayload, error) { - err := r.iam.AuthService.SendPasswordResetInstructionByEmail( - ctx, - input.Email, - ) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot send password reset instruction by email", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.ForgotPasswordPayload{ - Success: true, - }, nil -} - -// ResetPassword is the resolver for the resetPassword field. -func (r *mutationResolver) ResetPassword(ctx context.Context, input types.ResetPasswordInput) (*types.ResetPasswordPayload, error) { - err := r.iam.AuthService.ResetPassword( - ctx, - &iam.ResetPasswordRequest{ - Token: input.Token, - Password: input.Password, - }, - ) - if err != nil { - var errInvalidToken *iam.ErrInvalidToken - if errors.As(err, &errInvalidToken) { - return nil, gqlutils.Invalid(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot reset password", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.ResetPasswordPayload{ - Success: true, - }, nil -} - -// VerifyEmail is the resolver for the verifyEmail field. -func (r *mutationResolver) VerifyEmail(ctx context.Context, input types.VerifyEmailInput) (*types.VerifyEmailPayload, error) { - err := r.iam.AccountService.VerifyEmail(ctx, input.Token) - if err != nil { - var ( - errInvalidToken *iam.ErrInvalidToken - errIdentityNotFound *iam.ErrIdentityNotFound - errEmailAlreadyVerified *iam.ErrEmailAlreadyVerified - errEmailVerificationMismatch *iam.ErrEmailVerificationMismatch - - isInvalidErr = errors.As(err, &errInvalidToken) || - errors.As(err, &errEmailVerificationMismatch) - ) - - if isInvalidErr { - return nil, gqlutils.Invalid(ctx, err) - } - - if errors.As(err, &errEmailAlreadyVerified) { - return nil, gqlutils.Conflict(ctx, err) - } - - if errors.As(err, &errIdentityNotFound) { - return nil, gqlutils.NotFound(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot verify email", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.VerifyEmailPayload{ - Success: true, - }, nil -} - -// ChangePassword is the resolver for the changePassword field. -func (r *mutationResolver) ChangePassword(ctx context.Context, input types.ChangePasswordInput) (*types.ChangePasswordPayload, error) { - identity := authn.IdentityFromContext(ctx) - - err := r.iam.AccountService.ChangePassword( - ctx, - identity.ID, - &iam.ChangePasswordRequest{ - CurrentPassword: input.CurrentPassword, - NewPassword: input.NewPassword, - }, - ) - if err != nil { - var ( - errInvalidPassword *iam.ErrInvalidPassword - errIdentityNotFound *iam.ErrIdentityNotFound - ) - - if errors.As(err, &errInvalidPassword) { - return nil, gqlutils.Invalid(ctx, err) - } - - if errors.As(err, &errIdentityNotFound) { - return nil, gqlutils.NotFound(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot change password", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.ChangePasswordPayload{ - Success: true, - }, nil -} - -// ChangeEmail is the resolver for the changeEmail field. -func (r *mutationResolver) ChangeEmail(ctx context.Context, input types.ChangeEmailInput) (*types.ChangeEmailPayload, error) { - identity := authn.IdentityFromContext(ctx) - - err := r.iam.AccountService.ChangeEmail( - ctx, - identity.ID, - &iam.ChangeEmailRequest{ - NewEmail: input.NewEmail, - Password: input.Password, - }, - ) - if err != nil { - var ( - errInvalidPassword *iam.ErrInvalidPassword - errIdentityNotFound *iam.ErrIdentityNotFound - ) - - if errors.As(err, &errInvalidPassword) { - return nil, gqlutils.Invalid(ctx, err) - } - - if errors.As(err, &errIdentityNotFound) { - return nil, gqlutils.NotFound(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot change email", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.ChangeEmailPayload{ - Success: true, - }, nil -} - -// AssumeOrganizationSession is the resolver for the assumeOrganizationSession field. -func (r *mutationResolver) AssumeOrganizationSession(ctx context.Context, input types.AssumeOrganizationSessionInput) (*types.AssumeOrganizationSessionPayload, error) { - rootSession := authn.SessionFromContext(ctx) - - childSession, membership, err := r.iam.SessionService.AssumeOrganizationSession(ctx, rootSession.ID, input.OrganizationID, input.Continue) - if err != nil { - var ( - errMembershipNotFound *iam.ErrMembershipNotFound - errPasswordAuthenticationRequired *iam.ErrPasswordAuthenticationRequired - errSAMLAuthenticationRequired *iam.ErrSAMLAuthenticationRequired - ) - - switch { - case errors.As(err, &errMembershipNotFound): - return nil, gqlutils.NotFound(ctx, err) - - case errors.As(err, &errPasswordAuthenticationRequired): - return &types.AssumeOrganizationSessionPayload{ - Result: types.PasswordRequired{ - Reason: types.ReauthenticationReason(errPasswordAuthenticationRequired.Reason), - }, - }, nil - - case errors.As(err, &errSAMLAuthenticationRequired): - return &types.AssumeOrganizationSessionPayload{ - Result: types.SAMLAuthenticationRequired{ - Reason: types.ReauthenticationReason(errSAMLAuthenticationRequired.Reason), - }, - }, nil - - default: - r.logger.ErrorCtx(ctx, "cannot assume organization session", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - } - - return &types.AssumeOrganizationSessionPayload{ - Result: types.OrganizationSessionCreated{ - Session: types.NewSession(childSession), - Membership: types.NewMembership(membership), - }, - }, nil -} - -// RevokeSession is the resolver for the revokeSession field. -func (r *mutationResolver) RevokeSession(ctx context.Context, input types.RevokeSessionInput) (*types.RevokeSessionPayload, error) { - if err := r.authorize(ctx, input.SessionID, iam.ActionSessionRevoke); err != nil { - return nil, err - } - - identity := authn.IdentityFromContext(ctx) - - err := r.iam.SessionService.RevokeSession(ctx, identity.ID, input.SessionID) - if err != nil { - var ErrSessionExpired *iam.ErrSessionExpired - if errors.As(err, &ErrSessionExpired) { - return &types.RevokeSessionPayload{Success: true}, nil - } - - r.logger.ErrorCtx(ctx, "cannot revoke session", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.RevokeSessionPayload{Success: true}, nil -} - -// RevokeAllSessions is the resolver for the revokeAllSessions field. -func (r *mutationResolver) RevokeAllSessions(ctx context.Context) (*types.RevokeAllSessionsPayload, error) { - if err := r.authorize(ctx, authn.SessionFromContext(ctx).ID, iam.ActionSessionRevokeAll); err != nil { - return nil, err - } - - session := authn.SessionFromContext(ctx) - - revokedCount, err := r.iam.SessionService.RevokeAllSessions(ctx, session.ID) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot revoke all sessions", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.RevokeAllSessionsPayload{RevokedCount: int(revokedCount)}, nil -} - // Node is the resolver for the node field. func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) { var ( @@ -843,15 +232,11 @@ func (r *queryResolver) SignUpEnabled(ctx context.Context) (bool, error) { return r.iam.IsSignUpEnabled(), nil } -// Identity returns schema.IdentityResolver implementation. -func (r *Resolver) Identity() schema.IdentityResolver { return &identityResolver{r} } - // Mutation returns schema.MutationResolver implementation. func (r *Resolver) Mutation() schema.MutationResolver { return &mutationResolver{r} } // Query returns schema.QueryResolver implementation. func (r *Resolver) Query() schema.QueryResolver { return &queryResolver{r} } -type identityResolver struct{ *Resolver } type mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver } diff --git a/pkg/server/api/connect/v1/graphql/base.graphql b/pkg/server/api/connect/v1/graphql/base.graphql index 1f90ecab3..4f27b4294 100644 --- a/pkg/server/api/connect/v1/graphql/base.graphql +++ b/pkg/server/api/connect/v1/graphql/base.graphql @@ -16,34 +16,10 @@ scalar Datetime scalar Upload scalar EmailAddr -enum OrderDirection - @goModel(model: "go.probo.inc/probo/pkg/page.OrderDirection") { - ASC @goEnum(value: "go.probo.inc/probo/pkg/page.OrderDirectionAsc") - DESC @goEnum(value: "go.probo.inc/probo/pkg/page.OrderDirectionDesc") -} - interface Node { id: ID! } -type PageInfo { - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: CursorKey - endCursor: CursorKey -} - -type OIDCProviderInfo { - name: String! - loginURL: String! -} - -enum ReauthenticationReason { - SESSION_EXPIRED - SENSITIVE_ACTION - POLICY_REQUIREMENT -} - type Query { node(id: ID!): Node @session(required: PRESENT) viewer: Identity @session(required: PRESENT) @@ -58,69 +34,4 @@ type Query { @session(required: OPTIONAL) } -type Identity implements Node { - id: ID! - email: EmailAddr! - fullName: String! - emailVerified: Boolean! - createdAt: Datetime! - updatedAt: Datetime! - - profiles( - first: Int - after: CursorKey - last: Int - before: CursorKey - orderBy: ProfileOrder - filter: ProfileFilter - ): ProfileConnection @goField(forceResolver: true) - - sessions( - first: Int - after: CursorKey - last: Int - before: CursorKey - orderBy: SessionOrder - ): SessionConnection @goField(forceResolver: true) - - personalAPIKeys( - first: Int - after: CursorKey - last: Int - before: CursorKey - ): PersonalAPIKeyConnection @goField(forceResolver: true) - - ssoLoginURL: String - @goField(forceResolver: true) - @session(required: PRESENT) - - permission(action: String!): Boolean! - @goField(forceResolver: true) - @session(required: PRESENT) -} - -type Mutation { - signIn(input: SignInInput!): SignInPayload @session(required: OPTIONAL) - signUp(input: SignUpInput!): SignUpPayload @session(required: NONE) - signOut: SignOutPayload @session(required: PRESENT) - activateAccount( - input: ActivateAccountInput! - ): ActivateAccountPayload @session(required: OPTIONAL) - forgotPassword(input: ForgotPasswordInput!): ForgotPasswordPayload - @session(required: NONE) - resetPassword(input: ResetPasswordInput!): ResetPasswordPayload - @session(required: NONE) - verifyEmail(input: VerifyEmailInput!): VerifyEmailPayload - @session(required: OPTIONAL) - changePassword(input: ChangePasswordInput!): ChangePasswordPayload - @session(required: PRESENT) - changeEmail(input: ChangeEmailInput!): ChangeEmailPayload - @session(required: PRESENT) - assumeOrganizationSession( - input: AssumeOrganizationSessionInput! - ): AssumeOrganizationSessionPayload @session(required: PRESENT) - - revokeSession(input: RevokeSessionInput!): RevokeSessionPayload! - @session(required: PRESENT) - revokeAllSessions: RevokeAllSessionsPayload @session(required: PRESENT) -} +type Mutation diff --git a/pkg/server/api/connect/v1/graphql/identity.graphql b/pkg/server/api/connect/v1/graphql/identity.graphql new file mode 100644 index 000000000..e8a0bcf94 --- /dev/null +++ b/pkg/server/api/connect/v1/graphql/identity.graphql @@ -0,0 +1,40 @@ +type Identity implements Node { + id: ID! + email: EmailAddr! + fullName: String! + emailVerified: Boolean! + createdAt: Datetime! + updatedAt: Datetime! + + profiles( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: ProfileOrder + filter: ProfileFilter + ): ProfileConnection @goField(forceResolver: true) + + sessions( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: SessionOrder + ): SessionConnection @goField(forceResolver: true) + + personalAPIKeys( + first: Int + after: CursorKey + last: Int + before: CursorKey + ): PersonalAPIKeyConnection @goField(forceResolver: true) + + ssoLoginURL: String + @goField(forceResolver: true) + @session(required: PRESENT) + + permission(action: String!): Boolean! + @goField(forceResolver: true) + @session(required: PRESENT) +} diff --git a/pkg/server/api/connect/v1/graphql/oidc.graphql b/pkg/server/api/connect/v1/graphql/oidc.graphql new file mode 100644 index 000000000..016b85698 --- /dev/null +++ b/pkg/server/api/connect/v1/graphql/oidc.graphql @@ -0,0 +1,4 @@ +type OIDCProviderInfo { + name: String! + loginURL: String! +} diff --git a/pkg/server/api/connect/v1/graphql/pagination.graphql b/pkg/server/api/connect/v1/graphql/pagination.graphql new file mode 100644 index 000000000..dc748d8ad --- /dev/null +++ b/pkg/server/api/connect/v1/graphql/pagination.graphql @@ -0,0 +1,12 @@ +type PageInfo { + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: CursorKey + endCursor: CursorKey +} + +enum OrderDirection + @goModel(model: "go.probo.inc/probo/pkg/page.OrderDirection") { + ASC @goEnum(value: "go.probo.inc/probo/pkg/page.OrderDirectionAsc") + DESC @goEnum(value: "go.probo.inc/probo/pkg/page.OrderDirectionDesc") +} diff --git a/pkg/server/api/connect/v1/graphql/session.graphql b/pkg/server/api/connect/v1/graphql/session.graphql index 54b4c3020..5b829a5b3 100644 --- a/pkg/server/api/connect/v1/graphql/session.graphql +++ b/pkg/server/api/connect/v1/graphql/session.graphql @@ -1,3 +1,35 @@ +enum ReauthenticationReason { + SESSION_EXPIRED + SENSITIVE_ACTION + POLICY_REQUIREMENT +} + +extend type Mutation { + signIn(input: SignInInput!): SignInPayload @session(required: OPTIONAL) + signUp(input: SignUpInput!): SignUpPayload @session(required: NONE) + signOut: SignOutPayload @session(required: PRESENT) + activateAccount( + input: ActivateAccountInput! + ): ActivateAccountPayload @session(required: OPTIONAL) + forgotPassword(input: ForgotPasswordInput!): ForgotPasswordPayload + @session(required: NONE) + resetPassword(input: ResetPasswordInput!): ResetPasswordPayload + @session(required: NONE) + verifyEmail(input: VerifyEmailInput!): VerifyEmailPayload + @session(required: OPTIONAL) + changePassword(input: ChangePasswordInput!): ChangePasswordPayload + @session(required: PRESENT) + changeEmail(input: ChangeEmailInput!): ChangeEmailPayload + @session(required: PRESENT) + assumeOrganizationSession( + input: AssumeOrganizationSessionInput! + ): AssumeOrganizationSessionPayload @session(required: PRESENT) + + revokeSession(input: RevokeSessionInput!): RevokeSessionPayload! + @session(required: PRESENT) + revokeAllSessions: RevokeAllSessionsPayload @session(required: PRESENT) +} + enum SessionOrderField @goModel(model: "go.probo.inc/probo/pkg/coredata.SessionOrderField") { CREATED_AT diff --git a/pkg/server/api/connect/v1/identity.resolvers.go b/pkg/server/api/connect/v1/identity.resolvers.go new file mode 100644 index 000000000..febdfcca0 --- /dev/null +++ b/pkg/server/api/connect/v1/identity.resolvers.go @@ -0,0 +1,185 @@ +package connect_v1 + +// This file will be automatically regenerated based on the schema, any resolver +// implementations +// will be copied through when generating and any unknown code will be moved to the end. +// Code generated by github.com/99designs/gqlgen version v0.17.87 + +import ( + "context" + "fmt" + + "github.com/99designs/gqlgen/graphql" + "go.gearno.de/kit/log" + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/iam" + "go.probo.inc/probo/pkg/page" + "go.probo.inc/probo/pkg/server/api/authn" + "go.probo.inc/probo/pkg/server/api/authz" + "go.probo.inc/probo/pkg/server/api/connect/v1/schema" + "go.probo.inc/probo/pkg/server/api/connect/v1/types" + "go.probo.inc/probo/pkg/server/gqlutils" + "go.probo.inc/probo/pkg/server/gqlutils/types/cursor" +) + +// Profiles is the resolver for the profiles field. +func (r *identityResolver) Profiles(ctx context.Context, obj *types.Identity, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ProfileOrderBy, filter *types.ProfileFilter) (*types.ProfileConnection, error) { + if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileList, authz.WithSkipAssumptionCheck()); err != nil { + return nil, err + } + + filters := coredata.NewMembershipProfileFilter(nil).WithMembership() + if filter != nil { + filters = coredata.NewMembershipProfileFilter(filter.ExcludeContractEnded).WithMembership() + if filter.State != nil { + filters.WithState(*filter.State) + } + } + + if gqlutils.OnlyTotalCountSelected(ctx) { + return &types.ProfileConnection{ + Resolver: r, + ParentID: obj.ID, + Filters: filters, + }, nil + } + + pageOrderBy := page.OrderBy[coredata.MembershipProfileOrderField]{ + Field: coredata.MembershipProfileOrderFieldFullName, + Direction: page.OrderDirectionAsc, + } + if orderBy != nil { + pageOrderBy = page.OrderBy[coredata.MembershipProfileOrderField]{ + Field: orderBy.Field, + Direction: orderBy.Direction, + } + } + + cursor := cursor.NewCursor(first, after, last, before, pageOrderBy) + + page, err := r.iam.AccountService.ListProfilesForIdentity(ctx, obj.ID, cursor, filters) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot list profiles", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewProfileConnection(page, r, obj.ID, filters), nil +} + +// Sessions is the resolver for the sessions field. +func (r *identityResolver) Sessions(ctx context.Context, obj *types.Identity, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.SessionOrder) (*types.SessionConnection, error) { + if err := r.authorize(ctx, obj.ID, iam.ActionSessionList); err != nil { + return nil, err + } + + if gqlutils.OnlyTotalCountSelected(ctx) { + return &types.SessionConnection{ + Resolver: r, + ParentID: obj.ID, + }, nil + } + + pageOrderBy := page.OrderBy[coredata.SessionOrderField]{ + Field: coredata.SessionOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if orderBy != nil { + pageOrderBy = page.OrderBy[coredata.SessionOrderField]{ + Field: orderBy.Field, + Direction: orderBy.Direction, + } + } + + cursor := cursor.NewCursor(first, after, last, before, pageOrderBy) + + page, err := r.iam.AccountService.ListSessions(ctx, obj.ID, cursor) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot list sessions", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewSessionConnection(page, r, obj.ID), nil +} + +// PersonalAPIKeys is the resolver for the personalAPIKeys field. +func (r *identityResolver) PersonalAPIKeys(ctx context.Context, obj *types.Identity, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.PersonalAPIKeyConnection, error) { + if err := r.authorize(ctx, obj.ID, iam.ActionPersonalAPIKeyList); err != nil { + return nil, err + } + + if gqlutils.OnlyTotalCountSelected(ctx) { + return &types.PersonalAPIKeyConnection{ + Resolver: r, + ParentID: obj.ID, + }, nil + } + + pageOrderBy := page.OrderBy[coredata.PersonalAPIKeyOrderField]{ + Field: coredata.PersonalAPIKeyOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + + cursor := cursor.NewCursor(first, after, last, before, pageOrderBy) + + page, err := r.iam.AccountService.ListPersonalAPIKeys(ctx, obj.ID, cursor) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot list personal api keys", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return types.NewPersonalAPIKeyConnection(page, r, obj.ID), nil +} + +// SsoLoginURL is the resolver for the ssoLoginURL field. +func (r *identityResolver) SsoLoginURL(ctx context.Context, obj *types.Identity) (*string, error) { + if err := r.authorize(ctx, obj.ID, iam.ActionIdentityGet); err != nil { + return nil, err + } + + identity := authn.IdentityFromContext(ctx) + + count, err := r.iam.AccountService.CountSAMLConfigurationsForEmail(ctx, identity.EmailAddress) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot count SAML configurations for email", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + if count != 1 { + if count == 0 { + return nil, graphql.ErrorOnPath( + ctx, + fmt.Errorf("no SAML configuration for email"), + ) + } + + return nil, graphql.ErrorOnPath( + ctx, + fmt.Errorf("multiple SSO configurations found for this domain. Please use your organization-specific SSO login URL"), + ) + } + + samlConfigs, err := r.iam.AccountService.ListSAMLConfigurationsForEmail(ctx, identity.EmailAddress) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot list SAML configurations for email", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + if len(samlConfigs) == 0 { + r.logger.ErrorCtx(ctx, "cannot find SAML config") + return nil, gqlutils.NotFoundf(ctx, "cannot find SAML config") + } + samlConfig := samlConfigs[0] + + loginURL := r.SSOLoginURL(samlConfig.ID) + return &loginURL, nil +} + +// Permission is the resolver for the permission field. +func (r *identityResolver) Permission(ctx context.Context, obj *types.Identity, action string) (bool, error) { + return r.Resolver.Permission(ctx, obj, action) +} + +// Identity returns schema.IdentityResolver implementation. +func (r *Resolver) Identity() schema.IdentityResolver { return &identityResolver{r} } + +type identityResolver struct{ *Resolver } diff --git a/pkg/server/api/connect/v1/session.resolvers.go b/pkg/server/api/connect/v1/session.resolvers.go index aa7a65707..7f5b05a79 100644 --- a/pkg/server/api/connect/v1/session.resolvers.go +++ b/pkg/server/api/connect/v1/session.resolvers.go @@ -7,13 +7,467 @@ package connect_v1 import ( "context" + "errors" + "github.com/vektah/gqlparser/v2/gqlerror" "go.gearno.de/kit/log" + "go.probo.inc/probo/pkg/iam" + "go.probo.inc/probo/pkg/server/api/authn" "go.probo.inc/probo/pkg/server/api/connect/v1/schema" "go.probo.inc/probo/pkg/server/api/connect/v1/types" "go.probo.inc/probo/pkg/server/gqlutils" ) +// SignIn is the resolver for the signIn field. +func (r *mutationResolver) SignIn(ctx context.Context, input types.SignInInput) (*types.SignInPayload, error) { + identity, err := r.iam.AuthService.CheckCredentials(ctx, input.Email, input.Password) + if err != nil { + var errInvalidPassword *iam.ErrInvalidPassword + if errors.As(err, &errInvalidPassword) { + return nil, gqlutils.Invalid(ctx, err) + } + + var errInvalidCredentials *iam.ErrInvalidCredentials + if errors.As(err, &errInvalidCredentials) { + return nil, &gqlerror.Error{ + Message: err.Error(), + Extensions: map[string]any{ + "code": "INVALID_CREDENTIALS", + }, + } + } + + r.logger.ErrorCtx(ctx, "cannot check credentials", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + session := authn.SessionFromContext(ctx) + + switch { + case session == nil: + var err error + session, err = r.iam.AuthService.OpenSessionWithPassword( + ctx, + identity.ID, + ) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot create session", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + case session.IdentityID != identity.ID: + if err := r.iam.SessionService.CloseSession(ctx, session.ID); err != nil { + r.logger.ErrorCtx(ctx, "cannot close session", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + session, err = r.iam.AuthService.OpenSessionWithPassword( + ctx, + identity.ID, + ) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot create session", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + } + + w := gqlutils.HTTPResponseWriterFromContext(ctx) + r.sessionCookie.Set(w, session) + + if input.OrganizationID != nil { + var err error + _, _, err = r.iam.SessionService.OpenPasswordChildSessionForOrganization(ctx, session.ID, *input.OrganizationID) + if err != nil { + // Here session middleware already took care of expired/nil root session so we only handle membership related errors + var errMembershipNotFound *iam.ErrMembershipNotFound + var errUserInactive *iam.ErrUserInactive + + if errors.As(err, &errMembershipNotFound) || errors.As(err, &errUserInactive) { + return nil, gqlutils.Forbiddenf(ctx, "forbidden") + } + + r.logger.ErrorCtx(ctx, "cannot assume organization", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + } + + return &types.SignInPayload{ + Identity: types.NewIdentity(identity), + Session: types.NewSession(session), + }, nil +} + +// SignUp is the resolver for the signUp field. +func (r *mutationResolver) SignUp(ctx context.Context, input types.SignUpInput) (*types.SignUpPayload, error) { + identity, session, err := r.iam.AuthService.CreateIdentityWithPassword( + ctx, + &iam.CreateIdentityWithPasswordRequest{ + Email: input.Email, + Password: input.Password, + FullName: input.FullName, + }, + ) + if err != nil { + var errIdentityAlreadyExists *iam.ErrIdentityAlreadyExists + if errors.As(err, &errIdentityAlreadyExists) { + return nil, gqlutils.Invalid(ctx, err) + } + + var errSignupDisabled *iam.ErrSignupDisabled + if errors.As(err, &errSignupDisabled) { + return nil, gqlutils.Forbidden(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot create identity with password", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + w := gqlutils.HTTPResponseWriterFromContext(ctx) + r.sessionCookie.Set(w, session) + + return &types.SignUpPayload{ + Identity: types.NewIdentity(identity), + }, nil +} + +// SignOut is the resolver for the signOut field. +func (r *mutationResolver) SignOut(ctx context.Context) (*types.SignOutPayload, error) { + session := authn.SessionFromContext(ctx) + + err := r.iam.SessionService.CloseSession(ctx, session.ID) + if err != nil { + var ErrSessionNotFound *iam.ErrSessionNotFound + if errors.As(err, &ErrSessionNotFound) { + return &types.SignOutPayload{}, nil + } + + r.logger.ErrorCtx(ctx, "cannot close session", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + w := gqlutils.HTTPResponseWriterFromContext(ctx) + r.sessionCookie.Clear(w) + + return &types.SignOutPayload{Success: true}, nil +} + +// ActivateAccount is the resolver for the activateAccount field. +func (r *mutationResolver) ActivateAccount(ctx context.Context, input types.ActivateAccountInput) (*types.ActivateAccountPayload, error) { + session := authn.SessionFromContext(ctx) + + if session != nil { + // Sign out any other account before activating a new one + err := r.iam.SessionService.CloseSession(ctx, session.ID) + if err != nil { + var ErrSessionNotFound *iam.ErrSessionNotFound + if !errors.As(err, &ErrSessionNotFound) { + r.logger.ErrorCtx(ctx, "cannot close session", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + } + + w := gqlutils.HTTPResponseWriterFromContext(ctx) + r.sessionCookie.Clear(w) + } + + identity, user, err := r.iam.AuthService.ActivateAccount( + ctx, + &iam.ActivateAccountRequest{ + InvitationToken: input.Token, + }, + ) + if err != nil { + var ( + errInvalidToken *iam.ErrInvalidToken + errInvitationNotFound *iam.ErrInvitationNotFound + errInvitationExpired *iam.ErrInvitationExpired + + isInvalidErr = errors.As(err, &errInvalidToken) || + errors.As(err, &errInvitationNotFound) || + errors.As(err, &errInvitationExpired) + ) + + if isInvalidErr { + return nil, gqlutils.Invalid(ctx, err) + } + + if _, ok := errors.AsType[*iam.ErrInvitationAlreadyAccepted](err); ok { + return nil, gqlutils.AccountAlreadyActivated(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot activate account from invitation", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + var ssoLoginURL *string + samlConfigs, err := r.iam.AccountService.ListSAMLConfigurationsForEmail(ctx, user.EmailAddress) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot list saml configurations", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + for _, samlConfig := range samlConfigs { + if samlConfig.OrganizationID != user.OrganizationID { + continue + } + + ssoLoginURL = new(r.SSOLoginURL(samlConfig.ID)) + } + + if ssoLoginURL != nil { + return &types.ActivateAccountPayload{ + CreatePasswordToken: nil, + SsoLoginURL: ssoLoginURL, + Profile: types.NewProfile(user), + }, nil + } + + var createPasswordToken *string + if identity.HashedPassword == nil { + token, err := r.iam.AuthService.GetResetPasswordToken(ctx, identity.EmailAddress) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot generate password create token", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + createPasswordToken = &token + } + + return &types.ActivateAccountPayload{ + CreatePasswordToken: createPasswordToken, + SsoLoginURL: nil, + Profile: types.NewProfile(user), + }, nil +} + +// ForgotPassword is the resolver for the forgotPassword field. +func (r *mutationResolver) ForgotPassword(ctx context.Context, input types.ForgotPasswordInput) (*types.ForgotPasswordPayload, error) { + err := r.iam.AuthService.SendPasswordResetInstructionByEmail( + ctx, + input.Email, + ) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot send password reset instruction by email", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.ForgotPasswordPayload{ + Success: true, + }, nil +} + +// ResetPassword is the resolver for the resetPassword field. +func (r *mutationResolver) ResetPassword(ctx context.Context, input types.ResetPasswordInput) (*types.ResetPasswordPayload, error) { + err := r.iam.AuthService.ResetPassword( + ctx, + &iam.ResetPasswordRequest{ + Token: input.Token, + Password: input.Password, + }, + ) + if err != nil { + var errInvalidToken *iam.ErrInvalidToken + if errors.As(err, &errInvalidToken) { + return nil, gqlutils.Invalid(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot reset password", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.ResetPasswordPayload{ + Success: true, + }, nil +} + +// VerifyEmail is the resolver for the verifyEmail field. +func (r *mutationResolver) VerifyEmail(ctx context.Context, input types.VerifyEmailInput) (*types.VerifyEmailPayload, error) { + err := r.iam.AccountService.VerifyEmail(ctx, input.Token) + if err != nil { + var ( + errInvalidToken *iam.ErrInvalidToken + errIdentityNotFound *iam.ErrIdentityNotFound + errEmailAlreadyVerified *iam.ErrEmailAlreadyVerified + errEmailVerificationMismatch *iam.ErrEmailVerificationMismatch + + isInvalidErr = errors.As(err, &errInvalidToken) || + errors.As(err, &errEmailVerificationMismatch) + ) + + if isInvalidErr { + return nil, gqlutils.Invalid(ctx, err) + } + + if errors.As(err, &errEmailAlreadyVerified) { + return nil, gqlutils.Conflict(ctx, err) + } + + if errors.As(err, &errIdentityNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot verify email", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.VerifyEmailPayload{ + Success: true, + }, nil +} + +// ChangePassword is the resolver for the changePassword field. +func (r *mutationResolver) ChangePassword(ctx context.Context, input types.ChangePasswordInput) (*types.ChangePasswordPayload, error) { + identity := authn.IdentityFromContext(ctx) + + err := r.iam.AccountService.ChangePassword( + ctx, + identity.ID, + &iam.ChangePasswordRequest{ + CurrentPassword: input.CurrentPassword, + NewPassword: input.NewPassword, + }, + ) + if err != nil { + var ( + errInvalidPassword *iam.ErrInvalidPassword + errIdentityNotFound *iam.ErrIdentityNotFound + ) + + if errors.As(err, &errInvalidPassword) { + return nil, gqlutils.Invalid(ctx, err) + } + + if errors.As(err, &errIdentityNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot change password", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.ChangePasswordPayload{ + Success: true, + }, nil +} + +// ChangeEmail is the resolver for the changeEmail field. +func (r *mutationResolver) ChangeEmail(ctx context.Context, input types.ChangeEmailInput) (*types.ChangeEmailPayload, error) { + identity := authn.IdentityFromContext(ctx) + + err := r.iam.AccountService.ChangeEmail( + ctx, + identity.ID, + &iam.ChangeEmailRequest{ + NewEmail: input.NewEmail, + Password: input.Password, + }, + ) + if err != nil { + var ( + errInvalidPassword *iam.ErrInvalidPassword + errIdentityNotFound *iam.ErrIdentityNotFound + ) + + if errors.As(err, &errInvalidPassword) { + return nil, gqlutils.Invalid(ctx, err) + } + + if errors.As(err, &errIdentityNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot change email", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.ChangeEmailPayload{ + Success: true, + }, nil +} + +// AssumeOrganizationSession is the resolver for the assumeOrganizationSession field. +func (r *mutationResolver) AssumeOrganizationSession(ctx context.Context, input types.AssumeOrganizationSessionInput) (*types.AssumeOrganizationSessionPayload, error) { + rootSession := authn.SessionFromContext(ctx) + + childSession, membership, err := r.iam.SessionService.AssumeOrganizationSession(ctx, rootSession.ID, input.OrganizationID, input.Continue) + if err != nil { + var ( + errMembershipNotFound *iam.ErrMembershipNotFound + errPasswordAuthenticationRequired *iam.ErrPasswordAuthenticationRequired + errSAMLAuthenticationRequired *iam.ErrSAMLAuthenticationRequired + ) + + switch { + case errors.As(err, &errMembershipNotFound): + return nil, gqlutils.NotFound(ctx, err) + + case errors.As(err, &errPasswordAuthenticationRequired): + return &types.AssumeOrganizationSessionPayload{ + Result: types.PasswordRequired{ + Reason: types.ReauthenticationReason(errPasswordAuthenticationRequired.Reason), + }, + }, nil + + case errors.As(err, &errSAMLAuthenticationRequired): + return &types.AssumeOrganizationSessionPayload{ + Result: types.SAMLAuthenticationRequired{ + Reason: types.ReauthenticationReason(errSAMLAuthenticationRequired.Reason), + }, + }, nil + + default: + r.logger.ErrorCtx(ctx, "cannot assume organization session", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + } + + return &types.AssumeOrganizationSessionPayload{ + Result: types.OrganizationSessionCreated{ + Session: types.NewSession(childSession), + Membership: types.NewMembership(membership), + }, + }, nil +} + +// RevokeSession is the resolver for the revokeSession field. +func (r *mutationResolver) RevokeSession(ctx context.Context, input types.RevokeSessionInput) (*types.RevokeSessionPayload, error) { + if err := r.authorize(ctx, input.SessionID, iam.ActionSessionRevoke); err != nil { + return nil, err + } + + identity := authn.IdentityFromContext(ctx) + + err := r.iam.SessionService.RevokeSession(ctx, identity.ID, input.SessionID) + if err != nil { + var ErrSessionExpired *iam.ErrSessionExpired + if errors.As(err, &ErrSessionExpired) { + return &types.RevokeSessionPayload{Success: true}, nil + } + + r.logger.ErrorCtx(ctx, "cannot revoke session", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.RevokeSessionPayload{Success: true}, nil +} + +// RevokeAllSessions is the resolver for the revokeAllSessions field. +func (r *mutationResolver) RevokeAllSessions(ctx context.Context) (*types.RevokeAllSessionsPayload, error) { + if err := r.authorize(ctx, authn.SessionFromContext(ctx).ID, iam.ActionSessionRevokeAll); err != nil { + return nil, err + } + + session := authn.SessionFromContext(ctx) + + revokedCount, err := r.iam.SessionService.RevokeAllSessions(ctx, session.ID) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot revoke all sessions", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.RevokeAllSessionsPayload{RevokedCount: int(revokedCount)}, nil +} + // Identity is the resolver for the identity field. func (r *sessionResolver) Identity(ctx context.Context, obj *types.Session) (*types.Identity, error) { if gqlutils.OnlyIDSelected(ctx) { diff --git a/pkg/server/api/console/v1/base.resolvers.go b/pkg/server/api/console/v1/base.resolvers.go index 2bd0e9874..d32d4efef 100644 --- a/pkg/server/api/console/v1/base.resolvers.go +++ b/pkg/server/api/console/v1/base.resolvers.go @@ -9,13 +9,11 @@ import ( "context" "errors" "fmt" - "time" "go.gearno.de/kit/log" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/iam" - "go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/probo" "go.probo.inc/probo/pkg/server/api/authn" "go.probo.inc/probo/pkg/server/api/console/v1/schema" @@ -24,23 +22,6 @@ import ( "go.probo.inc/probo/pkg/validator" ) -// DownloadURL is the resolver for the downloadUrl field. -func (r *fileResolver) DownloadURL(ctx context.Context, obj *types.File) (string, error) { - if err := r.authorize(ctx, obj.ID, probo.ActionFileDownloadUrl); err != nil { - return "", err - } - - prb := r.ProboService(ctx, obj.ID.TenantID()) - - downloadUrl, err := prb.Files.GenerateFileTempURL(ctx, obj.ID, 60*time.Second) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot generate download URL", log.Error(err)) - return "", gqlutils.Internal(ctx) - } - - return downloadUrl, nil -} - // UpdateOrganizationContext is the resolver for the updateOrganizationContext field. func (r *mutationResolver) UpdateOrganizationContext(ctx context.Context, input types.UpdateOrganizationContextInput) (*types.UpdateOrganizationContextPayload, error) { if err := r.authorize(ctx, input.OrganizationID, probo.ActionOrganizationContextUpdate); err != nil { @@ -431,177 +412,11 @@ func (r *queryResolver) Viewer(ctx context.Context) (*types.Viewer, error) { return &types.Viewer{ID: viewerID}, nil } -// SignableDocuments is the resolver for the signableDocuments field. -func (r *viewerResolver) SignableDocuments(ctx context.Context, obj *types.Viewer, organizationID gid.GID, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.EmployeeDocumentConnection, error) { - if err := r.authorize(ctx, organizationID, probo.ActionEmployeeDocumentList); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, organizationID.TenantID()) - - pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{ - Field: coredata.DocumentOrderFieldCreatedAt, - Direction: page.OrderDirectionDesc, - } - if orderBy != nil { - pageOrderBy = page.OrderBy[coredata.DocumentOrderField]{ - Field: orderBy.Field, - Direction: orderBy.Direction, - } - } - - cursor := types.NewCursor(first, after, last, before, pageOrderBy) - - identity := authn.IdentityFromContext(ctx) - - documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeSignature) - - documentsPage, err := prb.Documents.ListByOrganizationID(ctx, organizationID, cursor, documentFilter) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot list organization signable documents", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - employeeDocuments := make([]*types.EmployeeDocument, len(documentsPage.Data)) - for i, doc := range documentsPage.Data { - employeeDocuments[i] = &types.EmployeeDocument{ - ID: doc.ID, - Title: doc.Title, - DocumentType: doc.DocumentType, - CreatedAt: doc.CreatedAt, - UpdatedAt: doc.UpdatedAt, - FilterMode: types.EmployeeDocumentFilterModeSignature, - } - } - - page := page.NewPage(employeeDocuments, documentsPage.Cursor) - - return types.NewEmployeeDocumentConnection(page), nil -} - -// SignableDocument is the resolver for the signableDocument field. -func (r *viewerResolver) SignableDocument(ctx context.Context, obj *types.Viewer, id gid.GID) (*types.EmployeeDocument, error) { - if err := r.authorize(ctx, id, probo.ActionEmployeeDocumentGet); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, id.TenantID()) - - identity := authn.IdentityFromContext(ctx) - - documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeSignature) - document, err := prb.Documents.GetWithFilter(ctx, id, documentFilter) - if err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) { - return nil, gqlutils.NotFound(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot get signable document", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.EmployeeDocument{ - ID: document.ID, - Title: document.Title, - DocumentType: document.DocumentType, - CreatedAt: document.CreatedAt, - UpdatedAt: document.UpdatedAt, - FilterMode: types.EmployeeDocumentFilterModeSignature, - }, nil -} - -// ApprovableDocuments is the resolver for the approvableDocuments field. -func (r *viewerResolver) ApprovableDocuments(ctx context.Context, obj *types.Viewer, organizationID gid.GID, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.EmployeeDocumentConnection, error) { - if err := r.authorize(ctx, organizationID, probo.ActionEmployeeDocumentList); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, organizationID.TenantID()) - - pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{ - Field: coredata.DocumentOrderFieldCreatedAt, - Direction: page.OrderDirectionDesc, - } - if orderBy != nil { - pageOrderBy = page.OrderBy[coredata.DocumentOrderField]{ - Field: orderBy.Field, - Direction: orderBy.Direction, - } - } - - cursor := types.NewCursor(first, after, last, before, pageOrderBy) - - identity := authn.IdentityFromContext(ctx) - - documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeApproval) - - documentsPage, err := prb.Documents.ListByOrganizationID(ctx, organizationID, cursor, documentFilter) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot list organization approvable documents", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - employeeDocuments := make([]*types.EmployeeDocument, len(documentsPage.Data)) - for i, doc := range documentsPage.Data { - employeeDocuments[i] = &types.EmployeeDocument{ - ID: doc.ID, - Title: doc.Title, - DocumentType: doc.DocumentType, - CreatedAt: doc.CreatedAt, - UpdatedAt: doc.UpdatedAt, - FilterMode: types.EmployeeDocumentFilterModeApproval, - } - } - - page := page.NewPage(employeeDocuments, documentsPage.Cursor) - - return types.NewEmployeeDocumentConnection(page), nil -} - -// ApprovableDocument is the resolver for the approvableDocument field. -func (r *viewerResolver) ApprovableDocument(ctx context.Context, obj *types.Viewer, id gid.GID) (*types.EmployeeDocument, error) { - if err := r.authorize(ctx, id, probo.ActionEmployeeDocumentGet); err != nil { - return nil, err - } - - prb := r.ProboService(ctx, id.TenantID()) - - identity := authn.IdentityFromContext(ctx) - - documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeApproval) - document, err := prb.Documents.GetWithFilter(ctx, id, documentFilter) - if err != nil { - if errors.Is(err, coredata.ErrResourceNotFound) { - return nil, gqlutils.NotFound(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot get approvable document", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return &types.EmployeeDocument{ - ID: document.ID, - Title: document.Title, - DocumentType: document.DocumentType, - CreatedAt: document.CreatedAt, - UpdatedAt: document.UpdatedAt, - FilterMode: types.EmployeeDocumentFilterModeApproval, - }, nil -} - -// File returns schema.FileResolver implementation. -func (r *Resolver) File() schema.FileResolver { return &fileResolver{r} } - // Mutation returns schema.MutationResolver implementation. func (r *Resolver) Mutation() schema.MutationResolver { return &mutationResolver{r} } // Query returns schema.QueryResolver implementation. func (r *Resolver) Query() schema.QueryResolver { return &queryResolver{r} } -// Viewer returns schema.ViewerResolver implementation. -func (r *Resolver) Viewer() schema.ViewerResolver { return &viewerResolver{r} } - -type fileResolver struct{ *Resolver } type mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver } -type viewerResolver struct{ *Resolver } diff --git a/pkg/server/api/console/v1/file.resolvers.go b/pkg/server/api/console/v1/file.resolvers.go new file mode 100644 index 000000000..ea9aae0fe --- /dev/null +++ b/pkg/server/api/console/v1/file.resolvers.go @@ -0,0 +1,39 @@ +package console_v1 + +// This file will be automatically regenerated based on the schema, any resolver +// implementations +// will be copied through when generating and any unknown code will be moved to the end. +// Code generated by github.com/99designs/gqlgen version v0.17.87 + +import ( + "context" + "time" + + "go.gearno.de/kit/log" + "go.probo.inc/probo/pkg/probo" + "go.probo.inc/probo/pkg/server/api/console/v1/schema" + "go.probo.inc/probo/pkg/server/api/console/v1/types" + "go.probo.inc/probo/pkg/server/gqlutils" +) + +// DownloadURL is the resolver for the downloadUrl field. +func (r *fileResolver) DownloadURL(ctx context.Context, obj *types.File) (string, error) { + if err := r.authorize(ctx, obj.ID, probo.ActionFileDownloadUrl); err != nil { + return "", err + } + + prb := r.ProboService(ctx, obj.ID.TenantID()) + + downloadUrl, err := prb.Files.GenerateFileTempURL(ctx, obj.ID, 60*time.Second) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot generate download URL", log.Error(err)) + return "", gqlutils.Internal(ctx) + } + + return downloadUrl, nil +} + +// File returns schema.FileResolver implementation. +func (r *Resolver) File() schema.FileResolver { return &fileResolver{r} } + +type fileResolver struct{ *Resolver } diff --git a/pkg/server/api/console/v1/graphql/base.graphql b/pkg/server/api/console/v1/graphql/base.graphql index 680fcfd29..1a46793a2 100644 --- a/pkg/server/api/console/v1/graphql/base.graphql +++ b/pkg/server/api/console/v1/graphql/base.graphql @@ -1,4 +1,3 @@ -# Directives directive @goField( forceResolver: Boolean name: String @@ -12,7 +11,6 @@ directive @goModel( directive @goEnum(value: String) on ENUM_VALUE -# Scalars scalar CursorKey scalar Datetime scalar Upload @@ -20,321 +18,15 @@ scalar Duration scalar BigInt scalar EmailAddr -# Interfaces interface Node { id: ID! } -# Pagination -type PageInfo { - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: CursorKey - endCursor: CursorKey -} - -# Enums -enum OrderDirection - @goModel(model: "go.probo.inc/probo/pkg/page.OrderDirection") { - ASC @goEnum(value: "go.probo.inc/probo/pkg/page.OrderDirectionAsc") - DESC @goEnum(value: "go.probo.inc/probo/pkg/page.OrderDirectionDesc") -} - -enum CountryCode - @goModel(model: "go.probo.inc/probo/pkg/coredata.CountryCode") { - AD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAD") - AE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAE") - AF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAF") - AG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAG") - AI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAI") - AL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAL") - AM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAM") - AO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAO") - AQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAQ") - AR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAR") - AS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAS") - AT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAT") - AU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAU") - AW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAW") - AX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAX") - AZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAZ") - BA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBA") - BB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBB") - BD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBD") - BE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBE") - BF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBF") - BG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBG") - BH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBH") - BI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBI") - BJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBJ") - BL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBL") - BM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBM") - BN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBN") - BO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBO") - BQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBQ") - BR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBR") - BS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBS") - BT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBT") - BV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBV") - BW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBW") - BY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBY") - BZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBZ") - CA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCA") - CC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCC") - CD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCD") - CF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCF") - CG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCG") - CH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCH") - CI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCI") - CK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCK") - CL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCL") - CM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCM") - CN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCN") - CO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCO") - CR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCR") - CU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCU") - CV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCV") - CW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCW") - CX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCX") - CY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCY") - CZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCZ") - DE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDE") - DJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDJ") - DK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDK") - DM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDM") - DO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDO") - DZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDZ") - EC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEC") - EE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEE") - EG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEG") - EH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEH") - ER @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeER") - ES @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeES") - ET @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeET") - EU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEU") - FI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFI") - FJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFJ") - FK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFK") - FM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFM") - FO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFO") - FR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFR") - GA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGA") - GB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGB") - GD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGD") - GE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGE") - GF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGF") - GG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGG") - GH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGH") - GI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGI") - GL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGL") - GM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGM") - GN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGN") - GP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGP") - GQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGQ") - GR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGR") - GT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGT") - GU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGU") - GW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGW") - GY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGY") - HK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHK") - HM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHM") - HN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHN") - HR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHR") - HT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHT") - HU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHU") - ID @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeID") - IE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIE") - IL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIL") - IM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIM") - IN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIN") - IO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIO") - IQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIQ") - IR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIR") - IS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIS") - IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIT") - JE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJE") - JM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJM") - JO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJO") - JP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJP") - KE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKE") - KG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKG") - KH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKH") - KI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKI") - KM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKM") - KN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKN") - KP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKP") - KR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKR") - KW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKW") - KY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKY") - KZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKZ") - LA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLA") - LB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLB") - LC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLC") - LI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLI") - LK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLK") - LR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLR") - LS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLS") - LT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLT") - LU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLU") - LV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLV") - LY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLY") - MA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMA") - MC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMC") - MD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMD") - ME @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeME") - MF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMF") - MG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMG") - MH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMH") - MK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMK") - ML @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeML") - MM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMM") - MN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMN") - MO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMO") - MP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMP") - MQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMQ") - MR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMR") - MS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMS") - MT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMT") - MU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMU") - MV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMV") - MW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMW") - MX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMX") - MY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMY") - MZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMZ") - NA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNA") - NC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNC") - NE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNE") - NF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNF") - NG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNG") - NI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNI") - NL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNL") - NO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNO") - NP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNP") - NR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNR") - NU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNU") - NZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNZ") - OM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeOM") - PA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePA") - PE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePE") - PF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePF") - PG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePG") - PH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePH") - PK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePK") - PL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePL") - PM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePM") - PN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePN") - PR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePR") - PS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePS") - PT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePT") - PW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePW") - PY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePY") - QA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeQA") - RE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRE") - RO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRO") - RS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRS") - RU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRU") - RW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRW") - SA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSA") - SB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSB") - SC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSC") - SD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSD") - SE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSE") - SG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSG") - SH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSH") - SI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSI") - SJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSJ") - SK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSK") - SL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSL") - SM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSM") - SN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSN") - SO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSO") - SR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSR") - SS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSS") - ST @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeST") - SV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSV") - SX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSX") - SY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSY") - SZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSZ") - TC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTC") - TD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTD") - TF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTF") - TG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTG") - TH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTH") - TJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTJ") - TK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTK") - TL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTL") - TM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTM") - TN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTN") - TO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTO") - TR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTR") - TT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTT") - TV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTV") - TW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTW") - TZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTZ") - UA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUA") - UG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUG") - UM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUM") - US @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUS") - UY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUY") - UZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUZ") - VA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVA") - VC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVC") - VE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVE") - VG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVG") - VI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVI") - VN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVN") - VU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVU") - WF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeWF") - WS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeWS") - YE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeYE") - YT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeYT") - ZA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZA") - ZM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZM") - ZW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZW") -} - -type File { - id: ID! - mimeType: String! - fileName: String! - size: BigInt! - downloadUrl: String! @goField(forceResolver: true) - createdAt: Datetime! - updatedAt: Datetime! -} - -# Root Types type Query { node(id: ID!): Node! viewer: Viewer! } -type Viewer { - id: ID! - - signableDocuments( - organizationId: ID! - first: Int - after: CursorKey - last: Int - before: CursorKey - orderBy: DocumentOrder - ): EmployeeDocumentConnection! @goField(forceResolver: true) - - signableDocument(id: ID!): EmployeeDocument @goField(forceResolver: true) - - approvableDocuments( - organizationId: ID! - first: Int - after: CursorKey - last: Int - before: CursorKey - orderBy: DocumentOrder - ): EmployeeDocumentConnection! @goField(forceResolver: true) - - approvableDocument(id: ID!): EmployeeDocument @goField(forceResolver: true) -} - type Mutation { updateOrganizationContext( input: UpdateOrganizationContextInput! diff --git a/pkg/server/api/console/v1/graphql/country_code.graphql b/pkg/server/api/console/v1/graphql/country_code.graphql new file mode 100644 index 000000000..5582496ae --- /dev/null +++ b/pkg/server/api/console/v1/graphql/country_code.graphql @@ -0,0 +1,253 @@ +enum CountryCode + @goModel(model: "go.probo.inc/probo/pkg/coredata.CountryCode") { + AD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAD") + AE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAE") + AF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAF") + AG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAG") + AI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAI") + AL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAL") + AM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAM") + AO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAO") + AQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAQ") + AR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAR") + AS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAS") + AT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAT") + AU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAU") + AW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAW") + AX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAX") + AZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAZ") + BA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBA") + BB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBB") + BD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBD") + BE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBE") + BF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBF") + BG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBG") + BH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBH") + BI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBI") + BJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBJ") + BL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBL") + BM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBM") + BN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBN") + BO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBO") + BQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBQ") + BR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBR") + BS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBS") + BT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBT") + BV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBV") + BW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBW") + BY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBY") + BZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBZ") + CA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCA") + CC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCC") + CD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCD") + CF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCF") + CG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCG") + CH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCH") + CI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCI") + CK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCK") + CL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCL") + CM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCM") + CN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCN") + CO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCO") + CR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCR") + CU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCU") + CV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCV") + CW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCW") + CX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCX") + CY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCY") + CZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCZ") + DE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDE") + DJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDJ") + DK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDK") + DM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDM") + DO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDO") + DZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDZ") + EC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEC") + EE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEE") + EG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEG") + EH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEH") + ER @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeER") + ES @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeES") + ET @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeET") + EU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEU") + FI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFI") + FJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFJ") + FK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFK") + FM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFM") + FO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFO") + FR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFR") + GA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGA") + GB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGB") + GD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGD") + GE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGE") + GF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGF") + GG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGG") + GH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGH") + GI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGI") + GL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGL") + GM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGM") + GN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGN") + GP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGP") + GQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGQ") + GR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGR") + GT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGT") + GU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGU") + GW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGW") + GY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGY") + HK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHK") + HM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHM") + HN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHN") + HR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHR") + HT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHT") + HU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHU") + ID @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeID") + IE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIE") + IL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIL") + IM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIM") + IN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIN") + IO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIO") + IQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIQ") + IR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIR") + IS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIS") + IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIT") + JE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJE") + JM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJM") + JO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJO") + JP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJP") + KE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKE") + KG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKG") + KH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKH") + KI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKI") + KM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKM") + KN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKN") + KP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKP") + KR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKR") + KW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKW") + KY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKY") + KZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKZ") + LA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLA") + LB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLB") + LC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLC") + LI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLI") + LK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLK") + LR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLR") + LS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLS") + LT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLT") + LU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLU") + LV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLV") + LY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLY") + MA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMA") + MC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMC") + MD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMD") + ME @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeME") + MF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMF") + MG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMG") + MH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMH") + MK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMK") + ML @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeML") + MM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMM") + MN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMN") + MO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMO") + MP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMP") + MQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMQ") + MR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMR") + MS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMS") + MT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMT") + MU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMU") + MV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMV") + MW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMW") + MX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMX") + MY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMY") + MZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMZ") + NA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNA") + NC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNC") + NE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNE") + NF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNF") + NG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNG") + NI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNI") + NL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNL") + NO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNO") + NP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNP") + NR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNR") + NU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNU") + NZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNZ") + OM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeOM") + PA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePA") + PE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePE") + PF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePF") + PG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePG") + PH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePH") + PK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePK") + PL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePL") + PM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePM") + PN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePN") + PR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePR") + PS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePS") + PT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePT") + PW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePW") + PY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePY") + QA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeQA") + RE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRE") + RO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRO") + RS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRS") + RU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRU") + RW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRW") + SA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSA") + SB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSB") + SC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSC") + SD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSD") + SE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSE") + SG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSG") + SH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSH") + SI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSI") + SJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSJ") + SK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSK") + SL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSL") + SM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSM") + SN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSN") + SO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSO") + SR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSR") + SS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSS") + ST @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeST") + SV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSV") + SX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSX") + SY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSY") + SZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSZ") + TC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTC") + TD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTD") + TF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTF") + TG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTG") + TH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTH") + TJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTJ") + TK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTK") + TL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTL") + TM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTM") + TN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTN") + TO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTO") + TR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTR") + TT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTT") + TV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTV") + TW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTW") + TZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTZ") + UA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUA") + UG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUG") + UM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUM") + US @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUS") + UY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUY") + UZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUZ") + VA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVA") + VC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVC") + VE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVE") + VG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVG") + VI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVI") + VN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVN") + VU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVU") + WF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeWF") + WS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeWS") + YE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeYE") + YT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeYT") + ZA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZA") + ZM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZM") + ZW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZW") +} + diff --git a/pkg/server/api/console/v1/graphql/file.graphql b/pkg/server/api/console/v1/graphql/file.graphql new file mode 100644 index 000000000..ef51af51c --- /dev/null +++ b/pkg/server/api/console/v1/graphql/file.graphql @@ -0,0 +1,9 @@ +type File { + id: ID! + mimeType: String! + fileName: String! + size: BigInt! + downloadUrl: String! @goField(forceResolver: true) + createdAt: Datetime! + updatedAt: Datetime! +} diff --git a/pkg/server/api/console/v1/graphql/pagination.graphql b/pkg/server/api/console/v1/graphql/pagination.graphql new file mode 100644 index 000000000..308b8b531 --- /dev/null +++ b/pkg/server/api/console/v1/graphql/pagination.graphql @@ -0,0 +1,12 @@ +type PageInfo { + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: CursorKey + endCursor: CursorKey +} + +enum OrderDirection + @goModel(model: "go.probo.inc/probo/pkg/page.OrderDirection") { + ASC @goEnum(value: "go.probo.inc/probo/pkg/page.OrderDirectionAsc") + DESC @goEnum(value: "go.probo.inc/probo/pkg/page.OrderDirectionDesc") +} diff --git a/pkg/server/api/console/v1/graphql/viewer.graphql b/pkg/server/api/console/v1/graphql/viewer.graphql new file mode 100644 index 000000000..f77412224 --- /dev/null +++ b/pkg/server/api/console/v1/graphql/viewer.graphql @@ -0,0 +1,25 @@ +type Viewer { + id: ID! + + signableDocuments( + organizationId: ID! + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: DocumentOrder + ): EmployeeDocumentConnection! @goField(forceResolver: true) + + signableDocument(id: ID!): EmployeeDocument @goField(forceResolver: true) + + approvableDocuments( + organizationId: ID! + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: DocumentOrder + ): EmployeeDocumentConnection! @goField(forceResolver: true) + + approvableDocument(id: ID!): EmployeeDocument @goField(forceResolver: true) +} diff --git a/pkg/server/api/console/v1/viewer.resolvers.go b/pkg/server/api/console/v1/viewer.resolvers.go new file mode 100644 index 000000000..872706eee --- /dev/null +++ b/pkg/server/api/console/v1/viewer.resolvers.go @@ -0,0 +1,184 @@ +package console_v1 + +// This file will be automatically regenerated based on the schema, any resolver +// implementations +// will be copied through when generating and any unknown code will be moved to the end. +// Code generated by github.com/99designs/gqlgen version v0.17.87 + +import ( + "context" + "errors" + + "go.gearno.de/kit/log" + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/gid" + "go.probo.inc/probo/pkg/page" + "go.probo.inc/probo/pkg/probo" + "go.probo.inc/probo/pkg/server/api/authn" + "go.probo.inc/probo/pkg/server/api/console/v1/schema" + "go.probo.inc/probo/pkg/server/api/console/v1/types" + "go.probo.inc/probo/pkg/server/gqlutils" +) + +// SignableDocuments is the resolver for the signableDocuments field. +func (r *viewerResolver) SignableDocuments(ctx context.Context, obj *types.Viewer, organizationID gid.GID, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.EmployeeDocumentConnection, error) { + if err := r.authorize(ctx, organizationID, probo.ActionEmployeeDocumentList); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, organizationID.TenantID()) + + pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{ + Field: coredata.DocumentOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if orderBy != nil { + pageOrderBy = page.OrderBy[coredata.DocumentOrderField]{ + Field: orderBy.Field, + Direction: orderBy.Direction, + } + } + + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + + identity := authn.IdentityFromContext(ctx) + + documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeSignature) + + documentsPage, err := prb.Documents.ListByOrganizationID(ctx, organizationID, cursor, documentFilter) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot list organization signable documents", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + employeeDocuments := make([]*types.EmployeeDocument, len(documentsPage.Data)) + for i, doc := range documentsPage.Data { + employeeDocuments[i] = &types.EmployeeDocument{ + ID: doc.ID, + Title: doc.Title, + DocumentType: doc.DocumentType, + CreatedAt: doc.CreatedAt, + UpdatedAt: doc.UpdatedAt, + FilterMode: types.EmployeeDocumentFilterModeSignature, + } + } + + page := page.NewPage(employeeDocuments, documentsPage.Cursor) + + return types.NewEmployeeDocumentConnection(page), nil +} + +// SignableDocument is the resolver for the signableDocument field. +func (r *viewerResolver) SignableDocument(ctx context.Context, obj *types.Viewer, id gid.GID) (*types.EmployeeDocument, error) { + if err := r.authorize(ctx, id, probo.ActionEmployeeDocumentGet); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, id.TenantID()) + + identity := authn.IdentityFromContext(ctx) + + documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeSignature) + document, err := prb.Documents.GetWithFilter(ctx, id, documentFilter) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot get signable document", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.EmployeeDocument{ + ID: document.ID, + Title: document.Title, + DocumentType: document.DocumentType, + CreatedAt: document.CreatedAt, + UpdatedAt: document.UpdatedAt, + FilterMode: types.EmployeeDocumentFilterModeSignature, + }, nil +} + +// ApprovableDocuments is the resolver for the approvableDocuments field. +func (r *viewerResolver) ApprovableDocuments(ctx context.Context, obj *types.Viewer, organizationID gid.GID, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.EmployeeDocumentConnection, error) { + if err := r.authorize(ctx, organizationID, probo.ActionEmployeeDocumentList); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, organizationID.TenantID()) + + pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{ + Field: coredata.DocumentOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if orderBy != nil { + pageOrderBy = page.OrderBy[coredata.DocumentOrderField]{ + Field: orderBy.Field, + Direction: orderBy.Direction, + } + } + + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + + identity := authn.IdentityFromContext(ctx) + + documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeApproval) + + documentsPage, err := prb.Documents.ListByOrganizationID(ctx, organizationID, cursor, documentFilter) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot list organization approvable documents", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + employeeDocuments := make([]*types.EmployeeDocument, len(documentsPage.Data)) + for i, doc := range documentsPage.Data { + employeeDocuments[i] = &types.EmployeeDocument{ + ID: doc.ID, + Title: doc.Title, + DocumentType: doc.DocumentType, + CreatedAt: doc.CreatedAt, + UpdatedAt: doc.UpdatedAt, + FilterMode: types.EmployeeDocumentFilterModeApproval, + } + } + + page := page.NewPage(employeeDocuments, documentsPage.Cursor) + + return types.NewEmployeeDocumentConnection(page), nil +} + +// ApprovableDocument is the resolver for the approvableDocument field. +func (r *viewerResolver) ApprovableDocument(ctx context.Context, obj *types.Viewer, id gid.GID) (*types.EmployeeDocument, error) { + if err := r.authorize(ctx, id, probo.ActionEmployeeDocumentGet); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, id.TenantID()) + + identity := authn.IdentityFromContext(ctx) + + documentFilter := coredata.NewDocumentFilter(nil).WithEmployeeIdentityID(&identity.ID, coredata.EmployeeFilterModeApproval) + document, err := prb.Documents.GetWithFilter(ctx, id, documentFilter) + if err != nil { + if errors.Is(err, coredata.ErrResourceNotFound) { + return nil, gqlutils.NotFound(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot get approvable document", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.EmployeeDocument{ + ID: document.ID, + Title: document.Title, + DocumentType: document.DocumentType, + CreatedAt: document.CreatedAt, + UpdatedAt: document.UpdatedAt, + FilterMode: types.EmployeeDocumentFilterModeApproval, + }, nil +} + +// Viewer returns schema.ViewerResolver implementation. +func (r *Resolver) Viewer() schema.ViewerResolver { return &viewerResolver{r} } + +type viewerResolver struct{ *Resolver } diff --git a/pkg/server/api/trust/v1/auth.resolvers.go b/pkg/server/api/trust/v1/auth.resolvers.go new file mode 100644 index 000000000..12972cb9c --- /dev/null +++ b/pkg/server/api/trust/v1/auth.resolvers.go @@ -0,0 +1,179 @@ +package trust_v1 + +// This file will be automatically regenerated based on the schema, any resolver +// implementations +// will be copied through when generating and any unknown code will be moved to the end. +// Code generated by github.com/99designs/gqlgen version v0.17.87 + +import ( + "context" + "errors" + + "go.gearno.de/kit/log" + "go.probo.inc/probo/pkg/baseurl" + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/iam" + "go.probo.inc/probo/pkg/saferedirect" + "go.probo.inc/probo/pkg/server/api/authn" + "go.probo.inc/probo/pkg/server/api/compliancepage" + "go.probo.inc/probo/pkg/server/api/trust/v1/types" + "go.probo.inc/probo/pkg/server/gqlutils" +) + +// SendMagicLink is the resolver for the sendMagicLink field. +func (r *mutationResolver) SendMagicLink(ctx context.Context, input types.SendMagicLinkInput) (*types.SendMagicLinkPayload, error) { + trustCenter := compliancepage.CompliancePageFromContext(ctx) + + baseURL := compliancepage.CompliancePageBaseURLFromContext(ctx) + + safeRedirect := saferedirect.New(saferedirect.StaticHosts(baseurl.MustParse(*baseURL).Host())) + + if input.Continue != nil { + _, ok := safeRedirect.Validate(ctx, *input.Continue) + if !ok { + return nil, gqlutils.Invalidf(ctx, "invalid continue URL") + } + } + + req := &iam.SendMagicLinkRequest{ + Email: input.Email, + CompliancePageID: &trustCenter.ID, + OrganizationID: trustCenter.OrganizationID, + URLPath: "verify-magic-link", + Continue: input.Continue, + } + + if err := r.iam.AuthService.SendMagicLink(ctx, req); err != nil { + r.logger.ErrorCtx(ctx, "cannot send magic link", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return nil, nil +} + +// VerifyMagicLink is the resolver for the verifyMagicLink field. +func (r *mutationResolver) VerifyMagicLink(ctx context.Context, input types.VerifyMagicLinkInput) (*types.VerifyMagicLinkPayload, error) { + session := authn.SessionFromContext(ctx) + identity := authn.IdentityFromContext(ctx) + + email, err := r.iam.AuthService.GetMagicLinkEmail(ctx, input.Token) + if err != nil { + var errExpiredToken *iam.ErrExpiredToken + if errors.As(err, &errExpiredToken) { + return nil, gqlutils.Invalid(ctx, err) + } + + var errInvalidToken *iam.ErrInvalidToken + if errors.As(err, &errInvalidToken) { + return nil, gqlutils.Invalid(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot get magic link email", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + var continueURL *string + + switch { + case session == nil: + var err error + identity, session, continueURL, err = r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token) + if err != nil { + var errExpiredToken *iam.ErrExpiredToken + if errors.As(err, &errExpiredToken) { + return nil, gqlutils.Invalid(ctx, err) + } + + var errInvalidToken *iam.ErrInvalidToken + if errors.As(err, &errInvalidToken) { + return nil, gqlutils.Invalid(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot open session with magic link", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + case identity.EmailAddress != email: + if err := r.iam.SessionService.CloseSession(ctx, session.ID); err != nil { + r.logger.ErrorCtx(ctx, "cannot close session", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + var err error + identity, session, continueURL, err = r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token) + if err != nil { + var errExpiredToken *iam.ErrExpiredToken + if errors.As(err, &errExpiredToken) { + return nil, gqlutils.Invalid(ctx, err) + } + + var errInvalidToken *iam.ErrInvalidToken + if errors.As(err, &errInvalidToken) { + return nil, gqlutils.Invalid(ctx, err) + } + + r.logger.ErrorCtx(ctx, "cannot open session with magic link", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + } + + trustCenter := compliancepage.CompliancePageFromContext(ctx) + + if _, err := r.trust.ProvisionMember(ctx, trustCenter.ID, identity.ID); err != nil { + r.logger.ErrorCtx(ctx, "cannot provision member", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + w := gqlutils.HTTPResponseWriterFromContext(ctx) + r.sessionCookie.Set(w, session) + + return &types.VerifyMagicLinkPayload{ + Continue: continueURL, + }, nil +} + +// UpdateFullName is the resolver for the updateFullName field. +func (r *mutationResolver) UpdateFullName(ctx context.Context, input types.UpdateFullNameInput) (*types.UpdateFullNamePayload, error) { + identity := authn.IdentityFromContext(ctx) + if identity == nil { + return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access") + } + + identity, err := r.iam.AccountService.UpdateIdentity( + ctx, + identity.ID, + &iam.UpdateIdentityRequest{ + FullName: input.FullName, + }, + ) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot update identity", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + compliancePage := compliancepage.CompliancePageFromContext(ctx) + + profile, err := r.iam.OrganizationService.GetProfileForIdentityAndOrganization(ctx, identity.ID, compliancePage.OrganizationID) + if err != nil { + if _, ok := errors.AsType[*iam.ErrProfileNotFound](err); !ok { + r.logger.ErrorCtx(ctx, "cannot get profile", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + } + + if profile.Source == coredata.ProfileSourceManual { + if _, err := r.iam.OrganizationService.UpdateUser(ctx, &iam.UpdateUserRequest{ + ID: profile.ID, + FullName: identity.FullName, + AdditionalEmailAddresses: profile.AdditionalEmailAddresses, + Kind: profile.Kind, + Position: profile.Position, + ContractStartDate: &profile.ContractStartDate, + ContractEndDate: &profile.ContractEndDate, + }); err != nil { + r.logger.ErrorCtx(ctx, "cannot update profile", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + } + + return &types.UpdateFullNamePayload{Success: true}, nil +} diff --git a/pkg/server/api/trust/v1/base.resolvers.go b/pkg/server/api/trust/v1/base.resolvers.go index 58053abb6..4472b0728 100644 --- a/pkg/server/api/trust/v1/base.resolvers.go +++ b/pkg/server/api/trust/v1/base.resolvers.go @@ -9,14 +9,10 @@ import ( "context" "errors" "strings" - "time" "go.gearno.de/kit/log" - "go.probo.inc/probo/pkg/baseurl" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/gid" - "go.probo.inc/probo/pkg/iam" - "go.probo.inc/probo/pkg/saferedirect" "go.probo.inc/probo/pkg/server/api/authn" "go.probo.inc/probo/pkg/server/api/compliancepage" "go.probo.inc/probo/pkg/server/api/trust/v1/schema" @@ -25,171 +21,6 @@ import ( "go.probo.inc/probo/pkg/trust" ) -// SendMagicLink is the resolver for the sendMagicLink field. -func (r *mutationResolver) SendMagicLink(ctx context.Context, input types.SendMagicLinkInput) (*types.SendMagicLinkPayload, error) { - trustCenter := compliancepage.CompliancePageFromContext(ctx) - - baseURL := compliancepage.CompliancePageBaseURLFromContext(ctx) - - safeRedirect := saferedirect.New(saferedirect.StaticHosts(baseurl.MustParse(*baseURL).Host())) - - if input.Continue != nil { - _, ok := safeRedirect.Validate(ctx, *input.Continue) - if !ok { - return nil, gqlutils.Invalidf(ctx, "invalid continue URL") - } - } - - req := &iam.SendMagicLinkRequest{ - Email: input.Email, - CompliancePageID: &trustCenter.ID, - OrganizationID: trustCenter.OrganizationID, - URLPath: "verify-magic-link", - Continue: input.Continue, - } - - if err := r.iam.AuthService.SendMagicLink(ctx, req); err != nil { - r.logger.ErrorCtx(ctx, "cannot send magic link", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - return nil, nil -} - -// VerifyMagicLink is the resolver for the verifyMagicLink field. -func (r *mutationResolver) VerifyMagicLink(ctx context.Context, input types.VerifyMagicLinkInput) (*types.VerifyMagicLinkPayload, error) { - session := authn.SessionFromContext(ctx) - identity := authn.IdentityFromContext(ctx) - - email, err := r.iam.AuthService.GetMagicLinkEmail(ctx, input.Token) - if err != nil { - var errExpiredToken *iam.ErrExpiredToken - if errors.As(err, &errExpiredToken) { - return nil, gqlutils.Invalid(ctx, err) - } - - var errInvalidToken *iam.ErrInvalidToken - if errors.As(err, &errInvalidToken) { - return nil, gqlutils.Invalid(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot get magic link email", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - var continueURL *string - - switch { - case session == nil: - var err error - identity, session, continueURL, err = r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token) - if err != nil { - var errExpiredToken *iam.ErrExpiredToken - if errors.As(err, &errExpiredToken) { - return nil, gqlutils.Invalid(ctx, err) - } - - var errInvalidToken *iam.ErrInvalidToken - if errors.As(err, &errInvalidToken) { - return nil, gqlutils.Invalid(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot open session with magic link", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - case identity.EmailAddress != email: - if err := r.iam.SessionService.CloseSession(ctx, session.ID); err != nil { - r.logger.ErrorCtx(ctx, "cannot close session", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - var err error - identity, session, continueURL, err = r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token) - if err != nil { - var errExpiredToken *iam.ErrExpiredToken - if errors.As(err, &errExpiredToken) { - return nil, gqlutils.Invalid(ctx, err) - } - - var errInvalidToken *iam.ErrInvalidToken - if errors.As(err, &errInvalidToken) { - return nil, gqlutils.Invalid(ctx, err) - } - - r.logger.ErrorCtx(ctx, "cannot open session with magic link", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - } - - trustCenter := compliancepage.CompliancePageFromContext(ctx) - - if _, err := r.trust.ProvisionMember(ctx, trustCenter.ID, identity.ID); err != nil { - r.logger.ErrorCtx(ctx, "cannot provision member", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - w := gqlutils.HTTPResponseWriterFromContext(ctx) - r.sessionCookie.Set(w, session) - - return &types.VerifyMagicLinkPayload{ - Continue: continueURL, - }, nil -} - -// UpdateFullName is the resolver for the updateFullName field. -func (r *mutationResolver) UpdateFullName(ctx context.Context, input types.UpdateFullNameInput) (*types.UpdateFullNamePayload, error) { - identity := authn.IdentityFromContext(ctx) - if identity == nil { - return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access") - } - - identity, err := r.iam.AccountService.UpdateIdentity( - ctx, - identity.ID, - &iam.UpdateIdentityRequest{ - FullName: input.FullName, - }, - ) - if err != nil { - r.logger.ErrorCtx(ctx, "cannot update identity", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - - compliancePage := compliancepage.CompliancePageFromContext(ctx) - - profile, err := r.iam.OrganizationService.GetProfileForIdentityAndOrganization(ctx, identity.ID, compliancePage.OrganizationID) - if err != nil { - if _, ok := errors.AsType[*iam.ErrProfileNotFound](err); !ok { - r.logger.ErrorCtx(ctx, "cannot get profile", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - } - - if profile.Source == coredata.ProfileSourceManual { - if _, err := r.iam.OrganizationService.UpdateUser(ctx, &iam.UpdateUserRequest{ - ID: profile.ID, - FullName: identity.FullName, - AdditionalEmailAddresses: profile.AdditionalEmailAddresses, - Kind: profile.Kind, - Position: profile.Position, - ContractStartDate: &profile.ContractStartDate, - ContractEndDate: &profile.ContractEndDate, - }); err != nil { - r.logger.ErrorCtx(ctx, "cannot update profile", log.Error(err)) - return nil, gqlutils.Internal(ctx) - } - } - - return &types.UpdateFullNamePayload{Success: true}, nil -} - -// LogoURL is the resolver for the logoUrl field. -func (r *organizationResolver) LogoURL(ctx context.Context, obj *types.Organization) (*string, error) { - trustService := r.TrustService(ctx, obj.ID.TenantID()) - - return trustService.Organizations.GenerateLogoURL(ctx, obj.ID, 1*time.Hour) -} - // Viewer is the resolver for the viewer field. func (r *queryResolver) Viewer(ctx context.Context) (*types.Identity, error) { identity := authn.IdentityFromContext(ctx) @@ -352,12 +183,8 @@ func (r *queryResolver) OidcProviders(ctx context.Context) ([]*types.OIDCProvide // Mutation returns schema.MutationResolver implementation. func (r *Resolver) Mutation() schema.MutationResolver { return &mutationResolver{r} } -// Organization returns schema.OrganizationResolver implementation. -func (r *Resolver) Organization() schema.OrganizationResolver { return &organizationResolver{r} } - // Query returns schema.QueryResolver implementation. func (r *Resolver) Query() schema.QueryResolver { return &queryResolver{r} } type mutationResolver struct{ *Resolver } -type organizationResolver struct{ *Resolver } type queryResolver struct{ *Resolver } diff --git a/pkg/server/api/trust/v1/graphql/auth.graphql b/pkg/server/api/trust/v1/graphql/auth.graphql index d0f1d2f9f..ee56f8764 100644 --- a/pkg/server/api/trust/v1/graphql/auth.graphql +++ b/pkg/server/api/trust/v1/graphql/auth.graphql @@ -1,3 +1,12 @@ +extend type Mutation { + sendMagicLink(input: SendMagicLinkInput!): SendMagicLinkPayload + @session(required: OPTIONAL) + verifyMagicLink(input: VerifyMagicLinkInput!): VerifyMagicLinkPayload + @session(required: OPTIONAL) + updateFullName(input: UpdateFullNameInput!): UpdateFullNamePayload + @session(required: PRESENT) +} + input SendMagicLinkInput { email: EmailAddr! continue: String diff --git a/pkg/server/api/trust/v1/graphql/base.graphql b/pkg/server/api/trust/v1/graphql/base.graphql index 18294645e..d7cdc2e91 100644 --- a/pkg/server/api/trust/v1/graphql/base.graphql +++ b/pkg/server/api/trust/v1/graphql/base.graphql @@ -21,291 +21,6 @@ interface Node { id: ID! } -type PageInfo { - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: CursorKey - endCursor: CursorKey -} - -type Identity implements Node { - id: ID! - email: EmailAddr! - fullName: String! - emailVerified: Boolean! - createdAt: Datetime! - updatedAt: Datetime! -} - -type Organization implements Node { - id: ID! - name: String! - logoUrl: String @goField(forceResolver: true) - - description: String - websiteUrl: String - email: String - headquarterAddress: String -} - -type OIDCProviderInfo { - name: String! - loginURL: String! -} - -enum CountryCode - @goModel(model: "go.probo.inc/probo/pkg/coredata.CountryCode") { - AD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAD") - AE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAE") - AF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAF") - AG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAG") - AI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAI") - AL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAL") - AM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAM") - AO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAO") - AQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAQ") - AR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAR") - AS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAS") - AT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAT") - AU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAU") - AW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAW") - AX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAX") - AZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAZ") - BA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBA") - BB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBB") - BD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBD") - BE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBE") - BF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBF") - BG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBG") - BH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBH") - BI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBI") - BJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBJ") - BL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBL") - BM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBM") - BN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBN") - BO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBO") - BQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBQ") - BR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBR") - BS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBS") - BT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBT") - BV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBV") - BW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBW") - BY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBY") - BZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBZ") - CA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCA") - CC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCC") - CD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCD") - CF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCF") - CG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCG") - CH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCH") - CI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCI") - CK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCK") - CL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCL") - CM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCM") - CN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCN") - CO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCO") - CR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCR") - CU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCU") - CV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCV") - CW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCW") - CX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCX") - CY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCY") - CZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCZ") - DE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDE") - DJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDJ") - DK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDK") - DM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDM") - DO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDO") - DZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDZ") - EC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEC") - EE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEE") - EG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEG") - EH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEH") - ER @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeER") - ES @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeES") - ET @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeET") - EU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEU") - FI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFI") - FJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFJ") - FK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFK") - FM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFM") - FO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFO") - FR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFR") - GA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGA") - GB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGB") - GD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGD") - GE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGE") - GF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGF") - GG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGG") - GH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGH") - GI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGI") - GL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGL") - GM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGM") - GN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGN") - GP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGP") - GQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGQ") - GR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGR") - GT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGT") - GU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGU") - GW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGW") - GY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGY") - HK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHK") - HM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHM") - HN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHN") - HR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHR") - HT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHT") - HU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHU") - ID @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeID") - IE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIE") - IL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIL") - IM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIM") - IN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIN") - IO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIO") - IQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIQ") - IR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIR") - IS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIS") - IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIT") - JE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJE") - JM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJM") - JO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJO") - JP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJP") - KE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKE") - KG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKG") - KH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKH") - KI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKI") - KM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKM") - KN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKN") - KP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKP") - KR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKR") - KW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKW") - KY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKY") - KZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKZ") - LA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLA") - LB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLB") - LC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLC") - LI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLI") - LK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLK") - LR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLR") - LS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLS") - LT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLT") - LU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLU") - LV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLV") - LY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLY") - MA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMA") - MC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMC") - MD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMD") - ME @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeME") - MF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMF") - MG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMG") - MH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMH") - MK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMK") - ML @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeML") - MM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMM") - MN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMN") - MO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMO") - MP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMP") - MQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMQ") - MR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMR") - MS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMS") - MT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMT") - MU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMU") - MV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMV") - MW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMW") - MX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMX") - MY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMY") - MZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMZ") - NA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNA") - NC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNC") - NE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNE") - NF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNF") - NG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNG") - NI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNI") - NL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNL") - NO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNO") - NP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNP") - NR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNR") - NU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNU") - NZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNZ") - OM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeOM") - PA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePA") - PE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePE") - PF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePF") - PG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePG") - PH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePH") - PK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePK") - PL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePL") - PM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePM") - PN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePN") - PR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePR") - PS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePS") - PT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePT") - PW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePW") - PY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePY") - QA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeQA") - RE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRE") - RO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRO") - RS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRS") - RU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRU") - RW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRW") - SA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSA") - SB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSB") - SC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSC") - SD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSD") - SE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSE") - SG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSG") - SH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSH") - SI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSI") - SJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSJ") - SK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSK") - SL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSL") - SM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSM") - SN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSN") - SO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSO") - SR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSR") - SS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSS") - ST @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeST") - SV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSV") - SX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSX") - SY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSY") - SZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSZ") - TC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTC") - TD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTD") - TF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTF") - TG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTG") - TH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTH") - TJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTJ") - TK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTK") - TL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTL") - TM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTM") - TN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTN") - TO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTO") - TR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTR") - TT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTT") - TV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTV") - TW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTW") - TZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTZ") - UA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUA") - UG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUG") - UM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUM") - US @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUS") - UY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUY") - UZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUZ") - VA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVA") - VC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVC") - VE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVE") - VG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVG") - VI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVI") - VN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVN") - VU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVU") - WF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeWF") - WS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeWS") - YE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeYE") - YT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeYT") - ZA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZA") - ZM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZM") - ZW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZW") -} - type Query { viewer: Identity node(id: ID!): Node @@ -315,11 +30,4 @@ type Query { @session(required: OPTIONAL) } -type Mutation { - sendMagicLink(input: SendMagicLinkInput!): SendMagicLinkPayload - @session(required: OPTIONAL) - verifyMagicLink(input: VerifyMagicLinkInput!): VerifyMagicLinkPayload - @session(required: OPTIONAL) - updateFullName(input: UpdateFullNameInput!): UpdateFullNamePayload - @session(required: PRESENT) -} +type Mutation diff --git a/pkg/server/api/trust/v1/graphql/country_code.graphql b/pkg/server/api/trust/v1/graphql/country_code.graphql new file mode 100644 index 000000000..a25571fdd --- /dev/null +++ b/pkg/server/api/trust/v1/graphql/country_code.graphql @@ -0,0 +1,253 @@ +enum CountryCode + @goModel(model: "go.probo.inc/probo/pkg/coredata.CountryCode") { + AD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAD") + AE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAE") + AF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAF") + AG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAG") + AI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAI") + AL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAL") + AM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAM") + AO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAO") + AQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAQ") + AR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAR") + AS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAS") + AT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAT") + AU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAU") + AW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAW") + AX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAX") + AZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAZ") + BA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBA") + BB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBB") + BD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBD") + BE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBE") + BF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBF") + BG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBG") + BH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBH") + BI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBI") + BJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBJ") + BL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBL") + BM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBM") + BN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBN") + BO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBO") + BQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBQ") + BR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBR") + BS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBS") + BT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBT") + BV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBV") + BW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBW") + BY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBY") + BZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeBZ") + CA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCA") + CC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCC") + CD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCD") + CF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCF") + CG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCG") + CH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCH") + CI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCI") + CK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCK") + CL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCL") + CM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCM") + CN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCN") + CO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCO") + CR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCR") + CU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCU") + CV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCV") + CW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCW") + CX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCX") + CY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCY") + CZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeCZ") + DE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDE") + DJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDJ") + DK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDK") + DM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDM") + DO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDO") + DZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeDZ") + EC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEC") + EE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEE") + EG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEG") + EH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEH") + ER @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeER") + ES @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeES") + ET @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeET") + EU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeEU") + FI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFI") + FJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFJ") + FK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFK") + FM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFM") + FO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFO") + FR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeFR") + GA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGA") + GB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGB") + GD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGD") + GE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGE") + GF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGF") + GG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGG") + GH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGH") + GI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGI") + GL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGL") + GM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGM") + GN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGN") + GP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGP") + GQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGQ") + GR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGR") + GT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGT") + GU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGU") + GW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGW") + GY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeGY") + HK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHK") + HM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHM") + HN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHN") + HR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHR") + HT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHT") + HU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeHU") + ID @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeID") + IE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIE") + IL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIL") + IM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIM") + IN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIN") + IO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIO") + IQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIQ") + IR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIR") + IS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIS") + IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeIT") + JE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJE") + JM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJM") + JO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJO") + JP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeJP") + KE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKE") + KG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKG") + KH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKH") + KI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKI") + KM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKM") + KN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKN") + KP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKP") + KR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKR") + KW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKW") + KY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKY") + KZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeKZ") + LA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLA") + LB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLB") + LC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLC") + LI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLI") + LK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLK") + LR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLR") + LS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLS") + LT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLT") + LU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLU") + LV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLV") + LY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeLY") + MA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMA") + MC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMC") + MD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMD") + ME @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeME") + MF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMF") + MG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMG") + MH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMH") + MK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMK") + ML @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeML") + MM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMM") + MN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMN") + MO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMO") + MP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMP") + MQ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMQ") + MR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMR") + MS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMS") + MT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMT") + MU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMU") + MV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMV") + MW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMW") + MX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMX") + MY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMY") + MZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeMZ") + NA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNA") + NC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNC") + NE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNE") + NF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNF") + NG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNG") + NI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNI") + NL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNL") + NO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNO") + NP @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNP") + NR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNR") + NU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNU") + NZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeNZ") + OM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeOM") + PA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePA") + PE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePE") + PF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePF") + PG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePG") + PH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePH") + PK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePK") + PL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePL") + PM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePM") + PN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePN") + PR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePR") + PS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePS") + PT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePT") + PW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePW") + PY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodePY") + QA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeQA") + RE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRE") + RO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRO") + RS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRS") + RU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRU") + RW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeRW") + SA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSA") + SB @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSB") + SC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSC") + SD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSD") + SE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSE") + SG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSG") + SH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSH") + SI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSI") + SJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSJ") + SK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSK") + SL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSL") + SM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSM") + SN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSN") + SO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSO") + SR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSR") + SS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSS") + ST @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeST") + SV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSV") + SX @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSX") + SY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSY") + SZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeSZ") + TC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTC") + TD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTD") + TF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTF") + TG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTG") + TH @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTH") + TJ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTJ") + TK @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTK") + TL @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTL") + TM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTM") + TN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTN") + TO @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTO") + TR @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTR") + TT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTT") + TV @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTV") + TW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTW") + TZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeTZ") + UA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUA") + UG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUG") + UM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUM") + US @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUS") + UY @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUY") + UZ @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeUZ") + VA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVA") + VC @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVC") + VE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVE") + VG @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVG") + VI @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVI") + VN @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVN") + VU @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeVU") + WF @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeWF") + WS @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeWS") + YE @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeYE") + YT @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeYT") + ZA @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZA") + ZM @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZM") + ZW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZW") +} + diff --git a/pkg/server/api/trust/v1/graphql/identity.graphql b/pkg/server/api/trust/v1/graphql/identity.graphql new file mode 100644 index 000000000..f0c37c88b --- /dev/null +++ b/pkg/server/api/trust/v1/graphql/identity.graphql @@ -0,0 +1,8 @@ +type Identity implements Node { + id: ID! + email: EmailAddr! + fullName: String! + emailVerified: Boolean! + createdAt: Datetime! + updatedAt: Datetime! +} diff --git a/pkg/server/api/trust/v1/graphql/oidc.graphql b/pkg/server/api/trust/v1/graphql/oidc.graphql new file mode 100644 index 000000000..016b85698 --- /dev/null +++ b/pkg/server/api/trust/v1/graphql/oidc.graphql @@ -0,0 +1,4 @@ +type OIDCProviderInfo { + name: String! + loginURL: String! +} diff --git a/pkg/server/api/trust/v1/graphql/organization.graphql b/pkg/server/api/trust/v1/graphql/organization.graphql new file mode 100644 index 000000000..155897fc7 --- /dev/null +++ b/pkg/server/api/trust/v1/graphql/organization.graphql @@ -0,0 +1,10 @@ +type Organization implements Node { + id: ID! + name: String! + logoUrl: String @goField(forceResolver: true) + + description: String + websiteUrl: String + email: String + headquarterAddress: String +} diff --git a/pkg/server/api/trust/v1/graphql/pagination.graphql b/pkg/server/api/trust/v1/graphql/pagination.graphql new file mode 100644 index 000000000..e70263d91 --- /dev/null +++ b/pkg/server/api/trust/v1/graphql/pagination.graphql @@ -0,0 +1,6 @@ +type PageInfo { + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: CursorKey + endCursor: CursorKey +} diff --git a/pkg/server/api/trust/v1/organization.resolvers.go b/pkg/server/api/trust/v1/organization.resolvers.go new file mode 100644 index 000000000..15f091a1b --- /dev/null +++ b/pkg/server/api/trust/v1/organization.resolvers.go @@ -0,0 +1,26 @@ +package trust_v1 + +// This file will be automatically regenerated based on the schema, any resolver +// implementations +// will be copied through when generating and any unknown code will be moved to the end. +// Code generated by github.com/99designs/gqlgen version v0.17.87 + +import ( + "context" + "time" + + "go.probo.inc/probo/pkg/server/api/trust/v1/schema" + "go.probo.inc/probo/pkg/server/api/trust/v1/types" +) + +// LogoURL is the resolver for the logoUrl field. +func (r *organizationResolver) LogoURL(ctx context.Context, obj *types.Organization) (*string, error) { + trustService := r.TrustService(ctx, obj.ID.TenantID()) + + return trustService.Organizations.GenerateLogoURL(ctx, obj.ID, 1*time.Hour) +} + +// Organization returns schema.OrganizationResolver implementation. +func (r *Resolver) Organization() schema.OrganizationResolver { return &organizationResolver{r} } + +type organizationResolver struct{ *Resolver }