Split GraphQL schemas into per-entity files

Split each API's monolithic schema.graphql into per-coredata-model
files under graphql/ subdirectories. gqlgen's follow-schema layout
with {name}.resolvers.go template generates one resolver file per
schema file. Relay uses schema + schemaExtensions to load the split
files.

Connect API: 8 files (base, session, organization, profile,
personal_api_key, saml, scim, audit_log)

Trust API: 5 files (base, trust_center, auth, nda, mailing_list)

Console API: 25 files covering all domain entities

Types extended across files (Organization, Mutation, Viewer,
TrustCenter, Identity) are defined in base.graphql as required by
Relay's schemaExtensions.

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-04-14 17:34:46 +04:00
parent 808fdffc9b
commit 31cca05ca4
87 changed files with 25638 additions and 24800 deletions

View File

@@ -1,6 +1,15 @@
# GraphQL (Go Backend — gqlgen)
Schema-first GraphQL using [gqlgen](https://gqlgen.com/). The schema is hand-written; Go types and resolvers are generated.
Schema-first GraphQL using [gqlgen](https://gqlgen.com/). The schema is hand-written and split into per-entity files under `graphql/`; Go types and resolvers are generated.
## Schema file organization
Each API's schema lives in `pkg/server/api/{api}/v1/graphql/` as multiple `.graphql` files, one per coredata model:
- `base.graphql` — directives, scalars, Node interface, PageInfo, OrderDirection, root Query/Mutation/Organization types
- Entity files (e.g., `vendor.graphql`, `control.graphql`) — use `extend type Organization`, `extend type Mutation`, etc.
gqlgen's `follow-schema` layout generates one resolver file per schema file (e.g., `vendor.resolvers.go`). Types that get extended across files (Organization, Mutation, Viewer, TrustCenter) must be defined in `base.graphql`.
## Connection types and `@goModel`
@@ -40,13 +49,15 @@ enum VendorOrderField
## Schema directives
| Directive | Target | Purpose |
|-----------|--------|---------|
| `@goModel(model: "...")` | `OBJECT`, `ENUM`, `INPUT_OBJECT`, `SCALAR`, `INTERFACE`, `UNION` | Map GraphQL type to existing Go type |
| `@goEnum(value: "...")` | `ENUM_VALUE` | Map enum value to Go constant |
| `@goField(forceResolver: true)` | `FIELD_DEFINITION` | Force a resolver function instead of struct field |
| `@goField(name: "...")` | `FIELD_DEFINITION`, `INPUT_FIELD_DEFINITION` | Override Go field name |
| `@goField(omittable: true)` | `INPUT_FIELD_DEFINITION` | Use `graphql.Omittable[T]` for distinguishing null vs absent |
| Directive | Target | Purpose |
| ------------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------ |
| `@goModel(model: "...")` | `OBJECT`, `ENUM`, `INPUT_OBJECT`, `SCALAR`, `INTERFACE`, `UNION` | Map GraphQL type to existing Go type |
| `@goEnum(value: "...")` | `ENUM_VALUE` | Map enum value to Go constant |
| `@goField(forceResolver: true)` | `FIELD_DEFINITION` | Force a resolver function instead of struct field |
| `@goField(name: "...")` | `FIELD_DEFINITION`, `INPUT_FIELD_DEFINITION` | Override Go field name |
| `@goField(omittable: true)` | `INPUT_FIELD_DEFINITION` | Use `graphql.Omittable[T]` for distinguishing null vs absent |
## Cursor pagination schema types
@@ -158,7 +169,8 @@ Default page size is **25** when neither `first` nor `last` is provided.
## Adding a new paginated field — checklist
1. **Schema** — add `enum XxxOrderField` (with `@goModel`/`@goEnum`), `input XxxOrder`, `type XxxConnection` (with `@goModel` and `totalCount` using `@goField(forceResolver: true)`), `type XxxEdge`, and the connection field with Relay arguments on the parent type
2. **Coredata** — add `*_order_field.go` (with `Column()`, `IsValid()`, marshaling), `CursorKey(field)` method on the entity, and the `LoadAllBy*` query using cursor SQL fragments + `page.NewPage()`
2. **Coredata** — add `*_order_field.go` (with `Column()`, `IsValid()`, marshaling), `CursorKey(field)` method on the entity, and the `LoadAllBy`* query using cursor SQL fragments + `page.NewPage()`
3. **API types** — add `*_connection.go` with `OrderBy` alias, connection struct, `NewXxxConnection`, `NewXxxEdge`
4. **Resolver** — implement the resolver (authorize, build order, build cursor, call service, build connection)
5. **Codegen** — run `go generate` for the relevant API package

View File

@@ -6,16 +6,18 @@ The console app uses [Relay](https://relay.dev/) as its GraphQL client. All Grap
Two Relay environments connect to two separate GraphQL APIs:
| Environment | Endpoint | Purpose |
|-------------|----------|---------|
| `coreEnvironment` | `/api/console/v1/graphql` | Main application data |
| `iamEnvironment` | `/api/connect/v1/graphql` | Authentication / identity |
| Environment | Endpoint | Purpose |
| ----------------- | ------------------------- | ------------------------- |
| `coreEnvironment` | `/api/console/v1/graphql` | Main application data |
| `iamEnvironment` | `/api/connect/v1/graphql` | Authentication / identity |
Configured in `apps/console/src/environments.ts`. Each has its own store with 1-minute query cache expiration.
## Relay compiler
Config lives in `relay.config.json` at the repo root with three projects (`core`, `iam`, `trust`) mapped to different source directories and schemas. Generated files go into `__generated__/` directories.
Config lives in `relay.config.json` at the repo root with three projects (`core`, `iam`, `trust`) mapped to different source directories and schemas. Each project uses `schema` pointing to `base.graphql` and `schemaExtensions` pointing to the `graphql/` directory containing the per-entity schema files. Generated files go into `__generated__/` directories.
```sh
npm run relay # clean + compile (from repo root)
@@ -381,7 +383,7 @@ return () => {
## File organization
GraphQL operations are colocated with the components that use them. See [`contrib/claude/app-arborescence.md`](app-arborescence.md) for the full folder layout.
GraphQL operations are colocated with the components that use them. See `[contrib/claude/app-arborescence.md](app-arborescence.md)` for the full folder layout.
```
pages/organizations/vendors/
@@ -394,4 +396,4 @@ pages/organizations/vendors/
VendorComplianceTab.tsx
```
Component-specific operations (queries, fragments, mutations) are defined inline in the component file that uses them. Shared sub-components live in `_components/` next to the page (scoped to the nearest common ancestor).
Component-specific operations (queries, fragments, mutations) are defined inline in the component file that uses them. Shared sub-components live in `_components/` next to the page (scoped to the nearest common ancestor).

View File

@@ -0,0 +1,100 @@
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"
"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/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"
)
// Organization is the resolver for the organization field.
func (r *auditLogEntryResolver) Organization(ctx context.Context, obj *types.AuditLogEntry) (*types.Organization, error) {
return obj.Organization, nil
}
// Permission is the resolver for the permission field.
func (r *auditLogEntryResolver) Permission(ctx context.Context, obj *types.AuditLogEntry, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *auditLogEntryConnectionResolver) TotalCount(ctx context.Context, obj *types.AuditLogEntryConnection) (int, error) {
filter := coredata.NewAuditLogEntryFilter()
if obj.Filter != nil {
filter = obj.Filter
}
count, err := r.iam.OrganizationService.CountAuditLogEntries(ctx, obj.ParentID, filter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count audit log entries", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
// AuditLogEntries is the resolver for the auditLogEntries field.
func (r *organizationResolver) AuditLogEntries(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.AuditLogEntryOrderBy, filter *types.AuditLogEntryFilter) (*types.AuditLogEntryConnection, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionAuditLogEntryList); err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.AuditLogEntryOrderField]{
Field: coredata.AuditLogEntryOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.AuditLogEntryOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
c := cursor.NewCursor(first, after, last, before, pageOrderBy)
coredataFilter := coredata.NewAuditLogEntryFilter()
if filter != nil {
if filter.Action != nil {
coredataFilter.WithAction(*filter.Action)
}
if filter.ActorID != nil {
coredataFilter.WithActorID(*filter.ActorID)
}
if filter.ResourceType != nil {
coredataFilter.WithResourceType(*filter.ResourceType)
}
if filter.ResourceID != nil {
coredataFilter.WithResourceID(*filter.ResourceID)
}
}
p, err := r.iam.OrganizationService.ListAuditLogEntries(ctx, obj.ID, c, coredataFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list audit log entries", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewAuditLogEntryConnection(p, r, obj.ID, coredataFilter), nil
}
// AuditLogEntry returns schema.AuditLogEntryResolver implementation.
func (r *Resolver) AuditLogEntry() schema.AuditLogEntryResolver { return &auditLogEntryResolver{r} }
// AuditLogEntryConnection returns schema.AuditLogEntryConnectionResolver implementation.
func (r *Resolver) AuditLogEntryConnection() schema.AuditLogEntryConnectionResolver {
return &auditLogEntryConnectionResolver{r}
}
type auditLogEntryResolver struct{ *Resolver }
type auditLogEntryConnectionResolver struct{ *Resolver }

View File

@@ -0,0 +1,809 @@
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"
"errors"
"fmt"
"strings"
"time"
"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/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"
)
// 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
}
// LogoURL is the resolver for the logoUrl field.
func (r *organizationResolver) LogoURL(ctx context.Context, obj *types.Organization) (*string, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionOrganizationGet, authz.WithSkipAssumptionCheck()); err != nil {
return nil, err
}
presignedURL, err := r.iam.OrganizationService.GenerateLogoURL(ctx, obj.ID, 1*time.Hour)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate logo URL", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return presignedURL, nil
}
// HorizontalLogoURL is the resolver for the horizontalLogoUrl field.
func (r *organizationResolver) HorizontalLogoURL(ctx context.Context, obj *types.Organization) (*string, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionOrganizationGet); err != nil {
return nil, err
}
presignedURL, err := r.iam.OrganizationService.GenerateHorizontalLogoURL(ctx, obj.ID, 1*time.Hour)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate horizontal logo URL", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return presignedURL, nil
}
// Viewer is the resolver for the viewer field.
func (r *organizationResolver) Viewer(ctx context.Context, obj *types.Organization) (*types.Profile, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil {
return nil, err
}
identity := authn.IdentityFromContext(ctx)
profile, err := r.iam.OrganizationService.GetProfileForIdentityAndOrganization(ctx, identity.ID, obj.ID)
if err != nil {
var errNotFound *iam.ErrProfileNotFound
if errors.As(err, &errNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get profile", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewProfile(profile), nil
}
// Permission is the resolver for the permission field.
func (r *organizationResolver) Permission(ctx context.Context, obj *types.Organization, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// Node is the resolver for the node field.
func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) {
var (
loadNode func(ctx context.Context, id gid.GID) (types.Node, error)
action string
)
switch id.EntityType() {
case coredata.OrganizationEntityType:
action = iam.ActionOrganizationGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
organization, err := r.iam.OrganizationService.GetOrganization(ctx, id)
if err != nil {
return nil, err
}
return types.NewOrganization(organization), nil
}
case coredata.IdentityEntityType:
action = iam.ActionIdentityGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
identity, err := r.iam.AccountService.GetIdentity(ctx, id)
if err != nil {
return nil, err
}
return types.NewIdentity(identity), nil
}
case coredata.SessionEntityType:
action = iam.ActionSessionGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
session, err := r.iam.GetSession(ctx, id)
if err != nil {
return nil, err
}
return types.NewSession(session), nil
}
case coredata.MembershipProfileEntityType:
action = iam.ActionMembershipGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
profile, err := r.iam.OrganizationService.GetProfile(ctx, id)
if err != nil {
return nil, err
}
return types.NewProfile(profile), nil
}
case coredata.MembershipEntityType:
action = iam.ActionMembershipGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
membership, err := r.iam.GetMembership(ctx, id)
if err != nil {
return nil, err
}
return types.NewMembership(membership), nil
}
case coredata.InvitationEntityType:
action = iam.ActionInvitationGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
invitation, err := r.iam.GetInvitation(ctx, id)
if err != nil {
return nil, err
}
return types.NewInvitation(invitation), nil
}
case coredata.SAMLConfigurationEntityType:
action = iam.ActionSAMLConfigurationGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
samlConfiguration, err := r.iam.GetSAMLconfiguration(ctx, id)
if err != nil {
return nil, err
}
return types.NewSAMLConfiguration(samlConfiguration), nil
}
case coredata.PersonalAPIKeyEntityType:
action = iam.ActionPersonalAPIKeyGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
personalAPIKey, err := r.iam.GetPersonalAPIKey(ctx, id)
if err != nil {
return nil, err
}
return types.NewPersonalAPIKey(personalAPIKey), nil
}
case coredata.SCIMConfigurationEntityType:
action = iam.ActionSCIMConfigurationGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
scimConfiguration, err := r.iam.GetSCIMConfiguration(ctx, id)
if err != nil {
return nil, err
}
return types.NewSCIMConfiguration(scimConfiguration), nil
}
case coredata.SCIMEventEntityType:
action = iam.ActionSCIMEventGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
scimEvent, err := r.iam.GetSCIMEvent(ctx, id)
if err != nil {
return nil, err
}
return types.NewSCIMEvent(scimEvent), nil
}
default:
return nil, fmt.Errorf("unsupported entity type: %d", id.EntityType())
}
if err := r.authorize(ctx, id, action); err != nil {
return nil, err
}
node, err := loadNode(ctx, id)
if err != nil {
var (
errOrganizationNotFound *iam.ErrOrganizationNotFound
errIdentityNotFound *iam.ErrIdentityNotFound
errSessionNotFound *iam.ErrSessionNotFound
errProfileNotFound *iam.ErrProfileNotFound
errMembershipNotFound *iam.ErrMembershipNotFound
errInvitationNotFound *iam.ErrInvitationNotFound
isNotFoundErr = errors.As(err, &errOrganizationNotFound) ||
errors.As(err, &errIdentityNotFound) ||
errors.As(err, &errSessionNotFound) ||
errors.As(err, &errProfileNotFound) ||
errors.As(err, &errMembershipNotFound) ||
errors.As(err, &errInvitationNotFound)
)
if isNotFoundErr {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot load node", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return node, nil
}
// Viewer is the resolver for the viewer field.
func (r *queryResolver) Viewer(ctx context.Context) (*types.Identity, error) {
identity := authn.IdentityFromContext(ctx)
return &types.Identity{
ID: identity.ID,
Email: identity.EmailAddress,
EmailVerified: identity.EmailAddressVerified,
FullName: identity.FullName,
CreatedAt: identity.CreatedAt,
UpdatedAt: identity.UpdatedAt,
}, nil
}
// SsoLoginURL is the resolver for the ssoLoginURL field.
func (r *queryResolver) SsoLoginURL(ctx context.Context, email mail.Addr) (*string, error) {
count, err := r.iam.AccountService.CountSAMLConfigurationsForEmail(ctx, email)
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, email)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list SAML configurations for email", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
samlConfig := samlConfigs[0]
loginURL := r.SSOLoginURL(samlConfig.ID)
return &loginURL, nil
}
// OidcProviders is the resolver for the oidcProviders field.
func (r *queryResolver) OidcProviders(ctx context.Context) ([]*types.OIDCProviderInfo, error) {
providers := r.iam.OIDCService.EnabledProviders()
result := make([]*types.OIDCProviderInfo, 0, len(providers))
for _, p := range providers {
result = append(result, &types.OIDCProviderInfo{
Name: strings.ToLower(p.String()),
LoginURL: r.baseURL.WithPath("/api/connect/v1/oidc/" + strings.ToLower(p.String()) + "/login").MustString(),
})
}
return result, nil
}
// SignUpEnabled is the resolver for the signUpEnabled field.
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} }
// 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 identityResolver struct{ *Resolver }
type mutationResolver struct{ *Resolver }
type organizationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }

View File

@@ -1,5 +1,5 @@
schema:
- "schema.graphql"
- "graphql/*.graphql"
- "../../../gqlutils/directives/session/schema.graphql"
exec:
@@ -14,7 +14,7 @@ resolver:
layout: "follow-schema"
dir: "."
package: "connect_v1"
filename_template: "v1_resolver.go"
filename_template: "{name}.resolvers.go"
autobind: []
call_argument_directives_with_null: true

View File

@@ -0,0 +1,83 @@
extend type Organization {
auditLogEntries(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: AuditLogEntryOrder
filter: AuditLogEntryFilter
): AuditLogEntryConnection! @goField(forceResolver: true)
}
enum AuditLogActorType
@goModel(
model: "go.probo.inc/probo/pkg/coredata.AuditLogActorType"
) {
USER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AuditLogActorTypeUser"
)
API_KEY
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AuditLogActorTypeAPIKey"
)
SYSTEM
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AuditLogActorTypeSystem"
)
}
enum AuditLogEntryOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.AuditLogEntryOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AuditLogEntryOrderFieldCreatedAt"
)
}
input AuditLogEntryOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/connect/v1/types.AuditLogEntryOrderBy"
) {
field: AuditLogEntryOrderField!
direction: OrderDirection!
}
input AuditLogEntryFilter {
action: String
actorId: ID
resourceType: String
resourceId: ID
}
type AuditLogEntry implements Node {
id: ID!
organization: Organization @goField(forceResolver: true)
actorId: ID!
actorType: AuditLogActorType!
action: String!
resourceType: String!
resourceId: ID!
metadata: String
createdAt: Datetime!
permission(action: String!): Boolean!
@goField(forceResolver: true)
@session(required: PRESENT)
}
type AuditLogEntryConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/connect/v1/types.AuditLogEntryConnection"
) {
edges: [AuditLogEntryEdge!]!
pageInfo: PageInfo!
totalCount: Int! @goField(forceResolver: true)
}
type AuditLogEntryEdge {
cursor: CursorKey!
node: AuditLogEntry!
}

View File

@@ -0,0 +1,121 @@
directive @goField(
forceResolver: Boolean
name: String
omittable: Boolean
) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION
directive @goModel(
model: String
models: [String!]
) on OBJECT | INPUT_OBJECT | SCALAR | ENUM | INTERFACE | UNION
directive @goEnum(value: String) on ENUM_VALUE
scalar CursorKey
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)
ssoLoginURL(email: EmailAddr!): String
@goField(forceResolver: true)
@session(required: OPTIONAL)
oidcProviders: [OIDCProviderInfo!]!
@goField(forceResolver: true)
@session(required: OPTIONAL)
signUpEnabled: Boolean!
@goField(forceResolver: true)
@session(required: OPTIONAL)
}
type Identity implements Node {
id: ID!
email: EmailAddr!
fullName: String!
emailVerified: Boolean!
createdAt: Datetime!
updatedAt: Datetime!
ssoLoginURL: String
@goField(forceResolver: true)
@session(required: PRESENT)
permission(action: String!): Boolean!
@goField(forceResolver: true)
@session(required: PRESENT)
}
type Organization implements Node {
id: ID!
name: String!
logoUrl: String @goField(forceResolver: true)
horizontalLogoUrl: String @goField(forceResolver: true)
email: String
description: String
websiteUrl: String
headquarterAddress: String
createdAt: Datetime!
updatedAt: Datetime!
viewer: Profile @goField(forceResolver: true)
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)
}

View File

@@ -0,0 +1,56 @@
extend type Mutation {
createOrganization(
input: CreateOrganizationInput!
): CreateOrganizationPayload @session(required: PRESENT)
updateOrganization(
input: UpdateOrganizationInput!
): UpdateOrganizationPayload @session(required: PRESENT)
deleteOrganization(
input: DeleteOrganizationInput!
): DeleteOrganizationPayload @session(required: PRESENT)
deleteOrganizationHorizontalLogo(
input: DeleteOrganizationHorizontalLogoInput!
): DeleteOrganizationHorizontalLogoPayload @session(required: PRESENT)
}
input CreateOrganizationInput {
name: String!
logoFile: Upload
horizontalLogoFile: Upload
}
input UpdateOrganizationInput {
organizationId: ID!
name: String
logoFile: Upload
horizontalLogoFile: Upload
description: String @goField(omittable: true)
websiteUrl: String @goField(omittable: true)
email: String @goField(omittable: true)
headquarterAddress: String @goField(omittable: true)
}
input DeleteOrganizationInput {
organizationId: ID!
}
input DeleteOrganizationHorizontalLogoInput {
organizationId: ID!
}
type CreateOrganizationPayload {
organization: Organization
profile: Profile!
}
type UpdateOrganizationPayload {
organization: Organization
}
type DeleteOrganizationPayload {
deletedOrganizationId: ID!
}
type DeleteOrganizationHorizontalLogoPayload {
organization: Organization!
}

View File

@@ -0,0 +1,63 @@
extend type Identity {
personalAPIKeys(
first: Int
after: CursorKey
last: Int
before: CursorKey
): PersonalAPIKeyConnection @goField(forceResolver: true)
}
type PersonalAPIKey implements Node {
id: ID!
name: String!
expiresAt: Datetime!
lastUsedAt: Datetime
createdAt: Datetime!
token: String @goField(forceResolver: true)
permission(action: String!): Boolean!
@goField(forceResolver: true)
@session(required: PRESENT)
}
type PersonalAPIKeyConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/connect/v1/types.PersonalAPIKeyConnection"
) {
edges: [PersonalAPIKeyEdge!]!
pageInfo: PageInfo!
totalCount: Int @goField(forceResolver: true)
}
type PersonalAPIKeyEdge {
node: PersonalAPIKey!
cursor: CursorKey!
}
extend type Mutation {
createPersonalAPIKey(
input: CreatePersonalAPIKeyInput!
): CreatePersonalAPIKeyPayload @session(required: PRESENT)
revokePersonalAPIKey(
input: RevokePersonalAPIKeyInput!
): RevokePersonalAPIKeyPayload @session(required: PRESENT)
}
input CreatePersonalAPIKeyInput {
name: String!
expiresAt: Datetime!
}
input RevokePersonalAPIKeyInput {
personalAPIKeyId: ID!
}
type CreatePersonalAPIKeyPayload {
personalAPIKeyEdge: PersonalAPIKeyEdge!
token: String!
}
type RevokePersonalAPIKeyPayload {
personalAPIKeyId: ID!
}

View File

@@ -0,0 +1,271 @@
extend type Identity {
profiles(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ProfileOrder
filter: ProfileFilter
): ProfileConnection @goField(forceResolver: true)
}
extend type Organization {
profiles(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ProfileOrder
): ProfileConnection @goField(forceResolver: true)
}
type Profile implements Node {
id: ID!
fullName: String!
emailAddress: EmailAddr!
source: String!
state: ProfileState!
additionalEmailAddresses: [EmailAddr!]!
kind: String
position: String
contractStartDate: Datetime
contractEndDate: Datetime
createdAt: Datetime!
updatedAt: Datetime!
identity: Identity @goField(forceResolver: true)
organization: Organization @goField(forceResolver: true)
membership: Membership @goField(forceResolver: true)
pendingInvitations(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: InvitationOrder
): InvitationConnection @goField(forceResolver: true)
permission(action: String!): Boolean!
@goField(forceResolver: true)
@session(required: PRESENT)
}
enum ProfileState
@goModel(model: "go.probo.inc/probo/pkg/coredata.ProfileState") {
ACTIVE @goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStateActive")
INACTIVE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStateInactive")
}
enum ProfileSource
@goModel(model: "go.probo.inc/probo/pkg/coredata.ProfileSource") {
MANUAL
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileSourceManual")
SAML @goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileSourceSAML")
SCIM @goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileSourceSCIM")
}
enum MembershipRole
@goModel(model: "go.probo.inc/probo/pkg/coredata.MembershipRole") {
OWNER @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleOwner")
ADMIN @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleAdmin")
EMPLOYEE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleEmployee")
VIEWER @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleViewer")
AUDITOR
@goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleAuditor")
}
type Membership implements Node {
id: ID!
createdAt: Datetime!
role: MembershipRole!
lastSession: Session @goField(forceResolver: true)
permission(action: String!): Boolean!
@goField(forceResolver: true)
@session(required: PRESENT)
}
type Invitation implements Node {
id: ID!
expiresAt: Datetime!
acceptedAt: Datetime
createdAt: Datetime!
status: InvitationStatus!
permission(action: String!): Boolean!
@goField(forceResolver: true)
@session(required: PRESENT)
}
enum InvitationStatus
@goModel(model: "go.probo.inc/probo/pkg/coredata.InvitationStatus") {
PENDING
@goEnum(value: "go.probo.inc/probo/pkg/coredata.InvitationStatusPending")
ACCEPTED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.InvitationStatusAccepted")
EXPIRED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.InvitationStatusExpired")
}
enum InvitationOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.InvitationOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.InvitationOrderFieldCreatedAt"
)
}
input InvitationOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/connect/v1/types.InvitationOrderBy"
) {
direction: OrderDirection!
field: InvitationOrderField!
}
type InvitationConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/connect/v1/types.InvitationConnection"
) {
edges: [InvitationEdge!]!
pageInfo: PageInfo!
}
type InvitationEdge {
node: Invitation!
cursor: CursorKey!
}
enum ProfileOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.MembershipProfileOrderField") {
FULL_NAME
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MembershipProfileOrderFieldFullName"
)
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MembershipProfileOrderFieldCreatedAt"
)
KIND @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipProfileOrderFieldKind")
ORGANIZATION_NAME
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MembershipProfileOrderFieldOrganizationName"
)
STATE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MembershipProfileOrderFieldState"
)
}
input ProfileFilter {
excludeContractEnded: Boolean
state: ProfileState
}
input ProfileOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/connect/v1/types.ProfileOrderBy"
) {
direction: OrderDirection!
field: ProfileOrderField!
}
type ProfileConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/connect/v1/types.ProfileConnection"
) {
totalCount: Int @goField(forceResolver: true)
edges: [ProfileEdge!]!
pageInfo: PageInfo!
}
type ProfileEdge {
cursor: CursorKey!
node: Profile!
}
extend type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload
@session(required: PRESENT)
inviteUser(input: InviteUserInput!): InviteUserPayload
@session(required: PRESENT)
deactivateUser(input: DeactivateUserInput!): DeactivateUserPayload
updateUser(input: UpdateUserInput!): UpdateUserPayload!
updateMembership(input: UpdateMembershipInput!): UpdateMembershipPayload!
removeUser(input: RemoveUserInput!): RemoveUserPayload
@session(required: PRESENT)
}
input CreateUserInput {
organizationId: ID!
fullName: String!
emailAddress: EmailAddr!
role: MembershipRole!
additionalEmailAddresses: [EmailAddr!]
kind: String
position: String
contractStartDate: Datetime @goField(omittable: true)
contractEndDate: Datetime @goField(omittable: true)
}
input InviteUserInput {
organizationId: ID!
profileId: ID!
}
input ActivateUserInput {
organizationId: ID!
profileId: ID!
}
input DeactivateUserInput {
organizationId: ID!
profileId: ID!
}
input UpdateUserInput {
id: ID!
fullName: String!
additionalEmailAddresses: [EmailAddr!]
kind: String
position: String
contractStartDate: Datetime @goField(omittable: true)
contractEndDate: Datetime @goField(omittable: true)
}
input UpdateMembershipInput {
organizationId: ID!
membershipId: ID!
role: MembershipRole!
}
input RemoveUserInput {
organizationId: ID!
profileId: ID!
}
type CreateUserPayload {
profileEdge: ProfileEdge!
}
type InviteUserPayload {
invitationEdge: InvitationEdge!
}
type DeactivateUserPayload {
success: Boolean!
}
type UpdateUserPayload {
profile: Profile!
}
type UpdateMembershipPayload {
membership: Membership!
}
type RemoveUserPayload {
deletedProfileId: ID!
}

View File

@@ -0,0 +1,119 @@
extend type Organization {
samlConfigurations(
first: Int
after: CursorKey
last: Int
before: CursorKey
): SAMLConfigurationConnection @goField(forceResolver: true)
}
type SAMLConfiguration implements Node {
id: ID!
emailDomain: String!
enforcementPolicy: SAMLEnforcementPolicy!
domainVerifiedAt: Datetime
domainVerificationToken: String
idpEntityId: String!
idpSsoUrl: String!
idpCertificate: String!
autoSignupEnabled: Boolean!
createdAt: Datetime!
updatedAt: Datetime!
testLoginUrl: String! @goField(forceResolver: true)
attributeMappings: SAMLAttributeMappings!
permission(action: String!): Boolean!
@goField(forceResolver: true)
@session(required: PRESENT)
}
type SAMLAttributeMappings {
email: String!
firstName: String!
lastName: String!
role: String!
}
enum SAMLEnforcementPolicy
@goModel(model: "go.probo.inc/probo/pkg/coredata.SAMLEnforcementPolicy") {
OFF @goEnum(value: "go.probo.inc/probo/pkg/coredata.SAMLEnforcementPolicyOff")
OPTIONAL
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.SAMLEnforcementPolicyOptional"
)
REQUIRED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.SAMLEnforcementPolicyRequired"
)
}
type SAMLConfigurationConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/connect/v1/types.SAMLConfigurationConnection"
) {
edges: [SAMLConfigurationEdge!]!
pageInfo: PageInfo!
totalCount: Int @goField(forceResolver: true)
}
type SAMLConfigurationEdge {
node: SAMLConfiguration!
cursor: CursorKey!
}
extend type Mutation {
createSAMLConfiguration(
input: CreateSAMLConfigurationInput!
): CreateSAMLConfigurationPayload @session(required: PRESENT)
updateSAMLConfiguration(
input: UpdateSAMLConfigurationInput!
): UpdateSAMLConfigurationPayload @session(required: PRESENT)
deleteSAMLConfiguration(
input: DeleteSAMLConfigurationInput!
): DeleteSAMLConfigurationPayload @session(required: PRESENT)
}
input CreateSAMLConfigurationInput {
organizationId: ID!
emailDomain: String!
idpEntityId: String!
idpSsoUrl: String!
idpCertificate: String!
autoSignupEnabled: Boolean!
attributeMappings: SAMLAttributeMappingsInput
}
input SAMLAttributeMappingsInput {
email: String
firstName: String
lastName: String
role: String
}
input UpdateSAMLConfigurationInput {
organizationId: ID!
samlConfigurationId: ID!
idpEntityId: String
idpSsoUrl: String
idpCertificate: String
autoSignupEnabled: Boolean
enforcementPolicy: SAMLEnforcementPolicy!
attributeMappings: SAMLAttributeMappingsInput
}
input DeleteSAMLConfigurationInput {
organizationId: ID!
samlConfigurationId: ID!
}
type CreateSAMLConfigurationPayload {
samlConfigurationEdge: SAMLConfigurationEdge!
}
type UpdateSAMLConfigurationPayload {
samlConfiguration: SAMLConfiguration
}
type DeleteSAMLConfigurationPayload {
deletedSamlConfigurationId: ID!
}

View File

@@ -0,0 +1,182 @@
extend type Organization {
scimConfiguration: SCIMConfiguration @goField(forceResolver: true)
scimBridgeTypes: [SCIMBridgeTypeInfo!]! @goField(forceResolver: true)
}
type SCIMConfiguration implements Node {
id: ID!
endpointUrl: String! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
organization: Organization @goField(forceResolver: true)
bridge: SCIMBridge @goField(forceResolver: true)
events(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: SCIMEventOrder
): SCIMEventConnection @goField(forceResolver: true)
permission(action: String!): Boolean!
@goField(forceResolver: true)
@session(required: PRESENT)
}
type SCIMBridge implements Node {
id: ID!
state: SCIMBridgeState!
scimConfiguration: SCIMConfiguration @goField(forceResolver: true)
connector: Connector @goField(forceResolver: true)
type: SCIMBridgeType!
excludedUserNames: [String!]!
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean!
@goField(forceResolver: true)
@session(required: PRESENT)
}
type Connector implements Node {
id: ID!
provider: ConnectorProvider!
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean!
@goField(forceResolver: true)
@session(required: PRESENT)
}
enum ConnectorProvider
@goModel(model: "go.probo.inc/probo/pkg/coredata.ConnectorProvider") {
SLACK @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderSlack")
GOOGLE_WORKSPACE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderGoogleWorkspace")
BREX @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderBrex")
TALLY @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderTally")
CLOUDFLARE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderCloudflare")
}
enum SCIMBridgeType
@goModel(model: "go.probo.inc/probo/pkg/coredata.SCIMBridgeType") {
GOOGLE_WORKSPACE @goEnum(value: "go.probo.inc/probo/pkg/coredata.SCIMBridgeTypeGoogleWorkspace")
}
type SCIMBridgeTypeInfo {
type: SCIMBridgeType!
oauth2Scopes: [String!]!
}
enum SCIMBridgeState
@goModel(model: "go.probo.inc/probo/pkg/coredata.SCIMBridgeState") {
PENDING @goEnum(value: "go.probo.inc/probo/pkg/coredata.SCIMBridgeStatePending")
ACTIVE @goEnum(value: "go.probo.inc/probo/pkg/coredata.SCIMBridgeStateActive")
FAILED @goEnum(value: "go.probo.inc/probo/pkg/coredata.SCIMBridgeStateFailed")
}
type SCIMEvent implements Node {
id: ID!
method: String!
path: String!
statusCode: Int!
requestBody: String
responseBody: String
errorMessage: String
userName: String!
ipAddress: String!
createdAt: Datetime!
permission(action: String!): Boolean!
@goField(forceResolver: true)
@session(required: PRESENT)
}
enum SCIMEventOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.SCIMEventOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.SCIMEventOrderFieldCreatedAt"
)
}
input SCIMEventOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/connect/v1/types.SCIMEventOrderBy"
) {
direction: OrderDirection!
field: SCIMEventOrderField!
}
type SCIMEventConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/connect/v1/types.SCIMEventConnection"
) {
edges: [SCIMEventEdge!]!
pageInfo: PageInfo!
totalCount: Int @goField(forceResolver: true)
}
type SCIMEventEdge {
node: SCIMEvent!
cursor: CursorKey!
}
extend type Mutation {
createSCIMConfiguration(
input: CreateSCIMConfigurationInput!
): CreateSCIMConfigurationPayload @session(required: PRESENT)
deleteSCIMConfiguration(
input: DeleteSCIMConfigurationInput!
): DeleteSCIMConfigurationPayload @session(required: PRESENT)
regenerateSCIMToken(
input: RegenerateSCIMTokenInput!
): RegenerateSCIMTokenPayload @session(required: PRESENT)
updateSCIMBridge(
input: UpdateSCIMBridgeInput!
): UpdateSCIMBridgePayload @session(required: PRESENT)
}
input CreateSCIMConfigurationInput {
organizationId: ID!
connectorId: ID
}
input DeleteSCIMConfigurationInput {
organizationId: ID!
scimConfigurationId: ID!
}
input RegenerateSCIMTokenInput {
organizationId: ID!
scimConfigurationId: ID!
}
input UpdateSCIMBridgeInput {
organizationId: ID!
scimBridgeId: ID!
excludedUserNames: [String!]!
}
type CreateSCIMConfigurationPayload {
scimConfiguration: SCIMConfiguration!
scimBridge: SCIMBridge
token: String!
}
type DeleteSCIMConfigurationPayload {
deletedScimConfigurationId: ID!
}
type RegenerateSCIMTokenPayload {
scimConfiguration: SCIMConfiguration!
token: String!
}
type UpdateSCIMBridgePayload {
scimBridge: SCIMBridge!
}

View File

@@ -0,0 +1,169 @@
enum SessionOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.SessionOrderField") {
CREATED_AT
@goEnum(value: "go.probo.inc/probo/pkg/coredata.SessionOrderFieldCreatedAt")
EXPIRED_AT
@goEnum(value: "go.probo.inc/probo/pkg/coredata.SessionOrderFieldExpiredAt")
UPDATED_AT
@goEnum(value: "go.probo.inc/probo/pkg/coredata.SessionOrderFieldUpdatedAt")
}
input SessionOrder {
direction: OrderDirection!
field: SessionOrderField!
}
extend type Identity {
sessions(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: SessionOrder
): SessionConnection @goField(forceResolver: true)
}
type Session implements Node {
id: ID!
identity: Identity @goField(forceResolver: true)
ipAddress: String!
userAgent: String!
updatedAt: Datetime!
createdAt: Datetime!
expiresAt: Datetime!
permission(action: String!): Boolean!
@goField(forceResolver: true)
@session(required: PRESENT)
}
type SessionConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/connect/v1/types.SessionConnection"
) {
edges: [SessionEdge!]!
pageInfo: PageInfo!
totalCount: Int @goField(forceResolver: true)
}
type SessionEdge {
node: Session!
cursor: CursorKey!
}
input SignInInput {
organizationId: ID
email: EmailAddr!
password: String!
}
input SignUpInput {
email: EmailAddr!
password: String!
fullName: String!
}
input ActivateAccountInput {
token: String!
}
input ForgotPasswordInput {
email: EmailAddr!
}
input ResetPasswordInput {
token: String!
password: String!
}
input VerifyEmailInput {
token: String!
}
input ChangePasswordInput {
currentPassword: String!
newPassword: String!
}
input ChangeEmailInput {
newEmail: EmailAddr!
password: String!
}
input AssumeOrganizationSessionInput {
organizationId: ID!
continue: String!
}
input RevokeSessionInput {
sessionId: ID!
}
type SignInPayload {
identity: Identity
session: Session
}
type SignUpPayload {
identity: Identity
}
type SignOutPayload {
success: Boolean!
}
type ActivateAccountPayload {
createPasswordToken: String
ssoLoginUrl: String
profile: Profile
}
type ForgotPasswordPayload {
success: Boolean!
}
type ResetPasswordPayload {
success: Boolean!
}
type VerifyEmailPayload {
success: Boolean!
}
type ChangePasswordPayload {
success: Boolean!
}
type ChangeEmailPayload {
success: Boolean!
}
union AssumeOrganizationSessionResult =
| OrganizationSessionCreated
| PasswordRequired
| SAMLAuthenticationRequired
type OrganizationSessionCreated {
session: Session!
membership: Membership!
}
type PasswordRequired {
reason: ReauthenticationReason!
}
type SAMLAuthenticationRequired {
reason: ReauthenticationReason!
}
type AssumeOrganizationSessionPayload {
result: AssumeOrganizationSessionResult!
}
type RevokeSessionPayload {
success: Boolean!
}
type RevokeAllSessionsPayload {
revokedCount: Int!
}

View File

@@ -0,0 +1,150 @@
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"
"errors"
"fmt"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/iam"
"go.probo.inc/probo/pkg/server/api/authn"
"go.probo.inc/probo/pkg/server/api/connect/v1/types"
"go.probo.inc/probo/pkg/server/gqlutils"
)
// CreateOrganization is the resolver for the createOrganization field.
func (r *mutationResolver) CreateOrganization(ctx context.Context, input types.CreateOrganizationInput) (*types.CreateOrganizationPayload, error) {
identity := authn.IdentityFromContext(ctx)
// FIXME check email domain and related IDP config
// if ok := r.authorize(ctx, identity.ID, iam.ActionOrganizationCreate); !ok {
// return nil, nil
// }
var (
logoFile *iam.UploadedFile
horizontalLogoFile *iam.UploadedFile
)
if input.LogoFile != nil {
logoFile = &iam.UploadedFile{
Content: input.LogoFile.File,
Filename: input.LogoFile.Filename,
ContentType: input.LogoFile.ContentType,
Size: input.LogoFile.Size,
}
}
if input.HorizontalLogoFile != nil {
horizontalLogoFile = &iam.UploadedFile{
Content: input.HorizontalLogoFile.File,
Filename: input.HorizontalLogoFile.Filename,
ContentType: input.HorizontalLogoFile.ContentType,
Size: input.HorizontalLogoFile.Size,
}
}
organization, profile, err := r.iam.OrganizationService.CreateOrganization(
ctx,
identity.ID,
&iam.CreateOrganizationRequest{
Name: input.Name,
LogoFile: logoFile,
HorizontalLogoFile: horizontalLogoFile,
},
)
if err != nil {
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot create organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateOrganizationPayload{
Organization: types.NewOrganization(organization),
Profile: types.NewProfile(profile),
}, nil
}
// UpdateOrganization is the resolver for the updateOrganization field.
func (r *mutationResolver) UpdateOrganization(ctx context.Context, input types.UpdateOrganizationInput) (*types.UpdateOrganizationPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionOrganizationUpdate); err != nil {
return nil, err
}
req := &iam.UpdateOrganizationRequest{
Name: input.Name,
Description: gqlutils.UnwrapOmittable(input.Description),
WebsiteURL: gqlutils.UnwrapOmittable(input.WebsiteURL),
Email: gqlutils.UnwrapOmittable(input.Email),
HeadquarterAddress: gqlutils.UnwrapOmittable(input.HeadquarterAddress),
}
if input.LogoFile != nil {
req.LogoFile = &iam.UploadedFile{
Filename: input.LogoFile.Filename,
ContentType: input.LogoFile.ContentType,
Size: input.LogoFile.Size,
Content: input.LogoFile.File,
}
}
if input.HorizontalLogoFile != nil {
req.HorizontalLogoFile = &iam.UploadedFile{
Filename: input.HorizontalLogoFile.Filename,
ContentType: input.HorizontalLogoFile.ContentType,
Size: input.HorizontalLogoFile.Size,
Content: input.HorizontalLogoFile.File,
}
}
organization, err := r.iam.OrganizationService.UpdateOrganization(
ctx,
input.OrganizationID,
req,
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot update organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateOrganizationPayload{
Organization: &types.Organization{
ID: organization.ID,
Name: organization.Name,
Description: organization.Description,
WebsiteURL: organization.WebsiteURL,
Email: organization.Email,
HeadquarterAddress: organization.HeadquarterAddress,
CreatedAt: organization.CreatedAt,
UpdatedAt: organization.UpdatedAt,
},
}, nil
}
// DeleteOrganization is the resolver for the deleteOrganization field.
func (r *mutationResolver) DeleteOrganization(ctx context.Context, input types.DeleteOrganizationInput) (*types.DeleteOrganizationPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionOrganizationDelete); err != nil {
return nil, err
}
err := r.iam.OrganizationService.DeleteOrganization(ctx, input.OrganizationID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteOrganizationPayload{DeletedOrganizationID: input.OrganizationID}, nil
}
// DeleteOrganizationHorizontalLogo is the resolver for the deleteOrganizationHorizontalLogo field.
func (r *mutationResolver) DeleteOrganizationHorizontalLogo(ctx context.Context, input types.DeleteOrganizationHorizontalLogoInput) (*types.DeleteOrganizationHorizontalLogoPayload, error) {
panic(fmt.Errorf("not implemented: DeleteOrganizationHorizontalLogo - deleteOrganizationHorizontalLogo"))
}

View File

@@ -0,0 +1,145 @@
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"
"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/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"
)
// 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
}
// CreatePersonalAPIKey is the resolver for the createPersonalAPIKey field.
func (r *mutationResolver) CreatePersonalAPIKey(ctx context.Context, input types.CreatePersonalAPIKeyInput) (*types.CreatePersonalAPIKeyPayload, error) {
identity := authn.IdentityFromContext(ctx)
if err := r.authorize(ctx, identity.ID, iam.ActionPersonalAPIKeyCreate); err != nil {
return nil, err
}
userAPIKey, token, err := r.iam.AccountService.CreatePersonalAPIKey(
ctx,
identity.ID,
input.Name,
input.ExpiresAt,
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create personal api key", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreatePersonalAPIKeyPayload{
PersonalAPIKeyEdge: types.NewPersonalAPIKeyEdge(userAPIKey, coredata.PersonalAPIKeyOrderFieldCreatedAt),
Token: token,
}, nil
}
// RevokePersonalAPIKey is the resolver for the revokePersonalAPIKey field.
func (r *mutationResolver) RevokePersonalAPIKey(ctx context.Context, input types.RevokePersonalAPIKeyInput) (*types.RevokePersonalAPIKeyPayload, error) {
if err := r.authorize(ctx, input.PersonalAPIKeyID, iam.ActionPersonalAPIKeyDelete); err != nil {
return nil, err
}
identity := authn.IdentityFromContext(ctx)
err := r.iam.AccountService.DeletePersonalAPIKey(ctx, identity.ID, input.PersonalAPIKeyID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete personal api key", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RevokePersonalAPIKeyPayload{PersonalAPIKeyID: input.PersonalAPIKeyID}, nil
}
// Token is the resolver for the token field.
func (r *personalAPIKeyResolver) Token(ctx context.Context, obj *types.PersonalAPIKey) (*string, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionPersonalAPIKeyGet); err != nil {
return nil, err
}
identity := authn.IdentityFromContext(ctx)
token, err := r.iam.AccountService.RevealPersonalAPIKeyToken(ctx, identity.ID, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot reveal personal api key token", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &token, nil
}
// Permission is the resolver for the permission field.
func (r *personalAPIKeyResolver) Permission(ctx context.Context, obj *types.PersonalAPIKey, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *personalAPIKeyConnectionResolver) TotalCount(ctx context.Context, obj *types.PersonalAPIKeyConnection) (*int, error) {
switch obj.Resolver.(type) {
case *identityResolver:
if err := r.authorize(ctx, obj.ParentID, iam.ActionPersonalAPIKeyList); err != nil {
return nil, err
}
count, err := r.iam.AccountService.CountPersonalAPIKeys(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count personal api keys", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
return nil, gqlutils.Internal(ctx)
}
// PersonalAPIKey returns schema.PersonalAPIKeyResolver implementation.
func (r *Resolver) PersonalAPIKey() schema.PersonalAPIKeyResolver { return &personalAPIKeyResolver{r} }
// PersonalAPIKeyConnection returns schema.PersonalAPIKeyConnectionResolver implementation.
func (r *Resolver) PersonalAPIKeyConnection() schema.PersonalAPIKeyConnectionResolver {
return &personalAPIKeyConnectionResolver{r}
}
type personalAPIKeyResolver struct{ *Resolver }
type personalAPIKeyConnectionResolver struct{ *Resolver }

View File

@@ -0,0 +1,441 @@
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"
"errors"
"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
}
// Permission is the resolver for the permission field.
func (r *invitationResolver) Permission(ctx context.Context, obj *types.Invitation, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// LastSession is the resolver for the lastSession field.
func (r *membershipResolver) LastSession(ctx context.Context, obj *types.Membership) (*types.Session, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipGet, authz.WithSkipAssumptionCheck()); err != nil {
return nil, err
}
session := authn.SessionFromContext(ctx)
if session == nil {
return nil, nil
}
childSession, err := r.iam.SessionService.GetActiveSessionForMembership(ctx, session.ID, obj.ID)
if err != nil {
var errSessionNotFound *iam.ErrSessionNotFound
if errors.As(err, &errSessionNotFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get active session for membership", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSession(childSession), nil
}
// Permission is the resolver for the permission field.
func (r *membershipResolver) Permission(ctx context.Context, obj *types.Membership, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// CreateUser is the resolver for the createUser field.
func (r *mutationResolver) CreateUser(ctx context.Context, input types.CreateUserInput) (*types.CreateUserPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionMembershipProfileCreate); err != nil {
return nil, err
}
profile, err := r.iam.OrganizationService.CreateUser(
ctx,
&iam.CreateUserRequest{
OrganizationID: input.OrganizationID,
EmailAddress: input.EmailAddress,
Role: input.Role,
FullName: input.FullName,
AdditionalEmailAddresses: input.AdditionalEmailAddresses,
Kind: input.Kind,
Position: input.Position,
ContractStartDate: gqlutils.UnwrapOmittable(input.ContractStartDate),
ContractEndDate: gqlutils.UnwrapOmittable(input.ContractEndDate),
},
)
if err != nil {
var errAlreadyExists *iam.ErrUserAlreadyExists
if errors.As(err, &errAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot create user", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateUserPayload{
ProfileEdge: types.NewProfileEdge(profile, coredata.MembershipProfileOrderFieldCreatedAt),
}, nil
}
// InviteUser is the resolver for the inviteUser field.
func (r *mutationResolver) InviteUser(ctx context.Context, input types.InviteUserInput) (*types.InviteUserPayload, error) {
if err := r.authorize(ctx, input.ProfileID, iam.ActionInvitationCreate); err != nil {
return nil, err
}
invitation, err := r.iam.OrganizationService.InviteUser(
ctx,
&iam.CreateInvitationRequest{
OrganizationID: input.OrganizationID,
ProfileID: input.ProfileID,
},
)
if err != nil {
var errOrganizationNotFound *iam.ErrOrganizationNotFound
var errUserAlreadyExists *iam.ErrUserAlreadyExists
if errors.As(err, &errOrganizationNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
if errors.As(err, &errUserAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot invite user", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.InviteUserPayload{
InvitationEdge: types.NewInvitationEdge(invitation, coredata.InvitationOrderFieldCreatedAt),
}, nil
}
// DeactivateUser is the resolver for the deactivateUser field.
func (r *mutationResolver) DeactivateUser(ctx context.Context, input types.DeactivateUserInput) (*types.DeactivateUserPayload, error) {
if err := r.authorize(ctx, input.ProfileID, iam.ActionMembershipProfileDeactivate); err != nil {
return nil, err
}
_, err := r.iam.OrganizationService.UpdateUserState(
ctx,
input.ProfileID,
coredata.ProfileStateInactive,
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot deactivate profile", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeactivateUserPayload{
Success: true,
}, nil
}
// UpdateUser is the resolver for the updateUser field.
func (r *mutationResolver) UpdateUser(ctx context.Context, input types.UpdateUserInput) (*types.UpdateUserPayload, error) {
if err := r.authorize(ctx, input.ID, iam.ActionMembershipProfileUpdate); err != nil {
return nil, err
}
profile, err := r.iam.OrganizationService.UpdateUser(
ctx,
&iam.UpdateUserRequest{
ID: input.ID,
FullName: input.FullName,
AdditionalEmailAddresses: input.AdditionalEmailAddresses,
Kind: input.Kind,
Position: input.Position,
ContractStartDate: gqlutils.UnwrapOmittable(input.ContractStartDate),
ContractEndDate: gqlutils.UnwrapOmittable(input.ContractEndDate),
},
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot update profile", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateUserPayload{
Profile: types.NewProfile(profile),
}, nil
}
// UpdateMembership is the resolver for the updateMembership field.
func (r *mutationResolver) UpdateMembership(ctx context.Context, input types.UpdateMembershipInput) (*types.UpdateMembershipPayload, error) {
if err := r.authorize(ctx, input.MembershipID, iam.ActionMembershipUpdate); err != nil {
return nil, err
}
if input.Role == coredata.MembershipRoleOwner {
if err := r.authorize(ctx, input.MembershipID, iam.ActionMembershipRoleSetOwner); err != nil {
return nil, err
}
}
membership, err := r.iam.OrganizationService.UpdateMempership(ctx, input.OrganizationID, input.MembershipID, input.Role)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot update membership", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateMembershipPayload{
Membership: types.NewMembership(membership),
}, nil
}
// RemoveUser is the resolver for the removeUser field.
func (r *mutationResolver) RemoveUser(ctx context.Context, input types.RemoveUserInput) (*types.RemoveUserPayload, error) {
if err := r.authorize(ctx, input.ProfileID, iam.ActionMembershipProfileDelete); err != nil {
return nil, err
}
err := r.iam.OrganizationService.RemoveUser(ctx, input.OrganizationID, input.ProfileID)
if err != nil {
var errManagedBySCIM *iam.ErrUserManagedBySCIM
var errLastActiveOwner *iam.ErrLastActiveOwner
if errors.As(err, &errManagedBySCIM) {
return nil, gqlutils.Conflict(ctx, err)
}
if errors.As(err, &errLastActiveOwner) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot remove user from organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RemoveUserPayload{DeletedProfileID: input.ProfileID}, nil
}
// Profiles is the resolver for the profiles field.
func (r *organizationResolver) Profiles(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ProfileOrderBy) (*types.ProfileConnection, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileList); err != nil {
return nil, err
}
filter := coredata.NewMembershipProfileFilter(nil).WithMembership()
if gqlutils.OnlyTotalCountSelected(ctx) {
return &types.ProfileConnection{
Resolver: r,
ParentID: obj.ID,
Filters: filter,
}, 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.OrganizationService.ListProfiles(ctx, obj.ID, cursor, filter)
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, filter), nil
}
// Identity is the resolver for the identity field.
func (r *profileResolver) Identity(ctx context.Context, obj *types.Profile) (*types.Identity, error) {
if err := r.authorize(
ctx,
obj.ID,
iam.ActionMembershipProfileGet,
authz.WithSkipAssumptionCheck(),
); err != nil {
return nil, err
}
identity, err := r.iam.AccountService.GetIdentity(ctx, obj.Identity.ID)
if err != nil {
var errNotFound *iam.ErrIdentityNotFound
if errors.As(err, &errNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get identity", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewIdentity(identity), nil
}
// Organization is the resolver for the organization field.
func (r *profileResolver) Organization(ctx context.Context, obj *types.Profile) (*types.Organization, error) {
if err := r.authorize(ctx, obj.Organization.ID, iam.ActionOrganizationGet, authz.WithSkipAssumptionCheck()); err != nil {
return nil, err
}
organization, err := r.iam.OrganizationService.GetOrganization(ctx, obj.Organization.ID)
if err != nil {
var errNotFound *iam.ErrOrganizationNotFound
if errors.As(err, &errNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Membership is the resolver for the membership field.
func (r *profileResolver) Membership(ctx context.Context, obj *types.Profile) (*types.Membership, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipGet, authz.WithSkipAssumptionCheck()); err != nil {
return nil, err
}
membership, err := r.iam.AccountService.GetMembershipForOrganization(ctx, obj.Identity.ID, obj.Organization.ID)
if err != nil {
var errNotFound *iam.ErrMembershipNotFound
if errors.As(err, &errNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get membership", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMembership(membership), nil
}
// PendingInvitations is the resolver for the pendingInvitations field.
func (r *profileResolver) PendingInvitations(ctx context.Context, obj *types.Profile, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.InvitationOrderBy) (*types.InvitationConnection, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionInvitationList); err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.InvitationOrderField]{
Field: coredata.InvitationOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
cursor := cursor.NewCursor(first, after, last, before, pageOrderBy)
page, err := r.iam.AccountService.ListPendingInvitations(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list pending invitations", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewInvitationConnection(page, r, obj.ID, nil), nil
}
// Permission is the resolver for the permission field.
func (r *profileResolver) Permission(ctx context.Context, obj *types.Profile, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *profileConnectionResolver) TotalCount(ctx context.Context, obj *types.ProfileConnection) (*int, error) {
switch obj.Resolver.(type) {
case *identityResolver:
count, err := r.iam.AccountService.CountProfiles(ctx, obj.ParentID, obj.Filters)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count profiles", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
case *organizationResolver:
count, err := r.iam.OrganizationService.CountProfiles(ctx, obj.ParentID, obj.Filters)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count profiles", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
return nil, gqlutils.Internal(ctx)
}
// Invitation returns schema.InvitationResolver implementation.
func (r *Resolver) Invitation() schema.InvitationResolver { return &invitationResolver{r} }
// Membership returns schema.MembershipResolver implementation.
func (r *Resolver) Membership() schema.MembershipResolver { return &membershipResolver{r} }
// Profile returns schema.ProfileResolver implementation.
func (r *Resolver) Profile() schema.ProfileResolver { return &profileResolver{r} }
// ProfileConnection returns schema.ProfileConnectionResolver implementation.
func (r *Resolver) ProfileConnection() schema.ProfileConnectionResolver {
return &profileConnectionResolver{r}
}
type invitationResolver struct{ *Resolver }
type membershipResolver struct{ *Resolver }
type profileResolver struct{ *Resolver }
type profileConnectionResolver struct{ *Resolver }

View File

@@ -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"
"errors"
"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/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"
)
// CreateSAMLConfiguration is the resolver for the createSAMLConfiguration field.
func (r *mutationResolver) CreateSAMLConfiguration(ctx context.Context, input types.CreateSAMLConfigurationInput) (*types.CreateSAMLConfigurationPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionSAMLConfigurationCreate); err != nil {
return nil, err
}
req := &iam.CreateSAMLConfigurationRequest{
EmailDomain: input.EmailDomain,
IdPEntityID: input.IdpEntityID,
IdPSsoURL: input.IdpSsoURL,
IdPCertificate: input.IdpCertificate,
AutoSignupEnabled: input.AutoSignupEnabled,
}
if input.AttributeMappings != nil {
req.AttributeEmail = input.AttributeMappings.Email
req.AttributeFirstname = input.AttributeMappings.FirstName
req.AttributeLastname = input.AttributeMappings.LastName
req.AttributeRole = input.AttributeMappings.Role
}
samlConfiguration, err := r.iam.OrganizationService.CreateSAMLConfiguration(
ctx,
input.OrganizationID,
req,
)
if err != nil {
var errSAMLConfigurationEmailDomainAlreadyExists *iam.ErrSAMLConfigurationEmailDomainAlreadyExists
if errors.As(err, &errSAMLConfigurationEmailDomainAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot create saml configuration", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateSAMLConfigurationPayload{
SamlConfigurationEdge: types.NewSAMLConfigurationEdge(
samlConfiguration,
coredata.SAMLConfigurationOrderFieldCreatedAt,
),
}, nil
}
// UpdateSAMLConfiguration is the resolver for the updateSAMLConfiguration field.
func (r *mutationResolver) UpdateSAMLConfiguration(ctx context.Context, input types.UpdateSAMLConfigurationInput) (*types.UpdateSAMLConfigurationPayload, error) {
if err := r.authorize(ctx, input.SamlConfigurationID, iam.ActionSAMLConfigurationUpdate); err != nil {
return nil, err
}
req := &iam.UpdateSAMLConfigurationRequest{
IdPEntityID: input.IdpEntityID,
IdPSsoURL: input.IdpSsoURL,
IdPCertificate: input.IdpCertificate,
AutoSignupEnabled: input.AutoSignupEnabled,
EnforcementPolicy: &input.EnforcementPolicy,
}
if input.AttributeMappings != nil {
req.AttributeEmail = input.AttributeMappings.Email
req.AttributeFirstname = input.AttributeMappings.FirstName
req.AttributeLastname = input.AttributeMappings.LastName
req.AttributeRole = input.AttributeMappings.Role
}
samlConfiguration, err := r.iam.OrganizationService.UpdateSAMLConfiguration(
ctx,
input.OrganizationID,
input.SamlConfigurationID,
req,
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot update saml configuration", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateSAMLConfigurationPayload{
SamlConfiguration: types.NewSAMLConfiguration(samlConfiguration),
}, nil
}
// DeleteSAMLConfiguration is the resolver for the deleteSAMLConfiguration field.
func (r *mutationResolver) DeleteSAMLConfiguration(ctx context.Context, input types.DeleteSAMLConfigurationInput) (*types.DeleteSAMLConfigurationPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionSAMLConfigurationDelete); err != nil {
return nil, err
}
err := r.iam.OrganizationService.DeleteSAMLConfiguration(ctx, input.OrganizationID, input.SamlConfigurationID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete saml configuration", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteSAMLConfigurationPayload{DeletedSamlConfigurationID: input.SamlConfigurationID}, nil
}
// SamlConfigurations is the resolver for the samlConfigurations field.
func (r *organizationResolver) SamlConfigurations(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.SAMLConfigurationConnection, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionSAMLConfigurationList); err != nil {
return nil, err
}
if gqlutils.OnlyTotalCountSelected(ctx) {
return &types.SAMLConfigurationConnection{
Resolver: r,
ParentID: obj.ID,
}, nil
}
pageOrderBy := page.OrderBy[coredata.SAMLConfigurationOrderField]{
Field: coredata.SAMLConfigurationOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
cursor := cursor.NewCursor(first, after, last, before, pageOrderBy)
page, err := r.iam.OrganizationService.ListSAMLConfigurations(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list saml configurations", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSAMLConfigurationConnection(page, r, obj.ID), nil
}
// TestLoginURL is the resolver for the testLoginUrl field.
func (r *sAMLConfigurationResolver) TestLoginURL(ctx context.Context, obj *types.SAMLConfiguration) (string, error) {
return r.baseURL.WithPath("/api/connect/v1/saml/2.0/" + obj.ID.String()).MustString(), nil
}
// Permission is the resolver for the permission field.
func (r *sAMLConfigurationResolver) Permission(ctx context.Context, obj *types.SAMLConfiguration, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *sAMLConfigurationConnectionResolver) TotalCount(ctx context.Context, obj *types.SAMLConfigurationConnection) (*int, error) {
switch obj.Resolver.(type) {
case *organizationResolver:
count, err := r.iam.OrganizationService.CountSAMLConfigurations(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count saml configurations", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
return nil, gqlutils.Internal(ctx)
}
// SAMLConfiguration returns schema.SAMLConfigurationResolver implementation.
func (r *Resolver) SAMLConfiguration() schema.SAMLConfigurationResolver {
return &sAMLConfigurationResolver{r}
}
// SAMLConfigurationConnection returns schema.SAMLConfigurationConnectionResolver implementation.
func (r *Resolver) SAMLConfigurationConnection() schema.SAMLConfigurationConnectionResolver {
return &sAMLConfigurationConnectionResolver{r}
}
type sAMLConfigurationResolver struct{ *Resolver }
type sAMLConfigurationConnectionResolver struct{ *Resolver }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,331 @@
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"
"errors"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/iam"
"go.probo.inc/probo/pkg/iam/scim/bridge/provider/googleworkspace"
"go.probo.inc/probo/pkg/page"
"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"
)
// Permission is the resolver for the permission field.
func (r *connectorResolver) Permission(ctx context.Context, obj *types.Connector, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// CreateSCIMConfiguration is the resolver for the createSCIMConfiguration field.
func (r *mutationResolver) CreateSCIMConfiguration(ctx context.Context, input types.CreateSCIMConfigurationInput) (*types.CreateSCIMConfigurationPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionSCIMConfigurationCreate); err != nil {
return nil, err
}
config, token, err := r.iam.OrganizationService.CreateSCIMConfiguration(ctx, input.OrganizationID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create scim configuration", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
var bridge *types.SCIMBridge
if input.ConnectorID != nil {
scimBridge, err := r.iam.OrganizationService.CreateSCIMBridge(ctx, input.OrganizationID, config.ID, *input.ConnectorID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create scim bridge", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
bridge = types.NewSCIMBridge(scimBridge)
}
payload := &types.CreateSCIMConfigurationPayload{
ScimConfiguration: types.NewSCIMConfiguration(config),
ScimBridge: bridge,
Token: token,
}
return payload, nil
}
// DeleteSCIMConfiguration is the resolver for the deleteSCIMConfiguration field.
func (r *mutationResolver) DeleteSCIMConfiguration(ctx context.Context, input types.DeleteSCIMConfigurationInput) (*types.DeleteSCIMConfigurationPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionSCIMConfigurationDelete); err != nil {
return nil, err
}
err := r.iam.OrganizationService.DeleteSCIMConfiguration(ctx, input.OrganizationID, input.ScimConfigurationID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete scim configuration", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteSCIMConfigurationPayload{DeletedScimConfigurationID: input.ScimConfigurationID}, nil
}
// RegenerateSCIMToken is the resolver for the regenerateSCIMToken field.
func (r *mutationResolver) RegenerateSCIMToken(ctx context.Context, input types.RegenerateSCIMTokenInput) (*types.RegenerateSCIMTokenPayload, error) {
if err := r.authorize(ctx, input.ScimConfigurationID, iam.ActionSCIMConfigurationUpdate); err != nil {
return nil, err
}
config, token, err := r.iam.OrganizationService.RegenerateSCIMToken(ctx, input.OrganizationID, input.ScimConfigurationID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot regenerate scim token", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RegenerateSCIMTokenPayload{
ScimConfiguration: types.NewSCIMConfiguration(config),
Token: token,
}, nil
}
// UpdateSCIMBridge is the resolver for the updateSCIMBridge field.
func (r *mutationResolver) UpdateSCIMBridge(ctx context.Context, input types.UpdateSCIMBridgeInput) (*types.UpdateSCIMBridgePayload, error) {
if err := r.authorize(ctx, input.ScimBridgeID, iam.ActionSCIMBridgeUpdate); err != nil {
return nil, err
}
bridge, err := r.iam.OrganizationService.UpdateSCIMBridge(ctx, input.OrganizationID, input.ScimBridgeID, input.ExcludedUserNames)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot update scim bridge excluded user names", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateSCIMBridgePayload{
ScimBridge: types.NewSCIMBridge(bridge),
}, nil
}
// ScimConfiguration is the resolver for the scimConfiguration field.
func (r *organizationResolver) ScimConfiguration(ctx context.Context, obj *types.Organization) (*types.SCIMConfiguration, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionSCIMConfigurationGet); err != nil {
return nil, err
}
config, err := r.iam.OrganizationService.GetSCIMConfiguration(ctx, obj.ID)
if err != nil {
var notFound *iam.ErrNoSCIMConfigurationFound
if errors.As(err, &notFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get scim configuration", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSCIMConfiguration(config), nil
}
// ScimBridgeTypes is the resolver for the scimBridgeTypes field.
func (r *organizationResolver) ScimBridgeTypes(ctx context.Context, obj *types.Organization) ([]*types.SCIMBridgeTypeInfo, error) {
return []*types.SCIMBridgeTypeInfo{
{
Type: coredata.SCIMBridgeTypeGoogleWorkspace,
Oauth2Scopes: googleworkspace.OAuth2Scopes,
},
}, nil
}
// ScimConfiguration is the resolver for the scimConfiguration field.
func (r *sCIMBridgeResolver) ScimConfiguration(ctx context.Context, obj *types.SCIMBridge) (*types.SCIMConfiguration, error) {
if err := r.authorize(ctx, obj.ScimConfiguration.ID, iam.ActionSCIMConfigurationGet); err != nil {
return nil, err
}
if gqlutils.OnlyIDSelected(ctx) {
return &types.SCIMConfiguration{
ID: obj.ScimConfiguration.ID,
}, nil
}
scimConfiguration, err := r.iam.GetSCIMConfiguration(ctx, obj.ScimConfiguration.ID)
if err != nil {
var errNoSCIMConfigurationFound *iam.ErrNoSCIMConfigurationFound
if errors.As(err, &errNoSCIMConfigurationFound) {
return nil, nil
}
return nil, err
}
return types.NewSCIMConfiguration(scimConfiguration), nil
}
// Connector is the resolver for the connector field.
func (r *sCIMBridgeResolver) Connector(ctx context.Context, obj *types.SCIMBridge) (*types.Connector, error) {
if obj.Connector == nil {
return nil, nil
}
// Authorize based on the SCIM configuration (connector accessed via bridge is a sub-resource)
if err := r.authorize(ctx, obj.ScimConfiguration.ID, iam.ActionSCIMConfigurationGet); err != nil {
return nil, err
}
if gqlutils.OnlyIDSelected(ctx) {
return &types.Connector{
ID: obj.Connector.ID,
}, nil
}
// Use metadata-only loading since we don't need the encrypted connection data
connector, err := r.iam.OrganizationService.GetConnectorMetadataByID(ctx, obj.Connector.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get connector", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewConnector(connector), nil
}
// Permission is the resolver for the permission field.
func (r *sCIMBridgeResolver) Permission(ctx context.Context, obj *types.SCIMBridge, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// EndpointURL is the resolver for the endpointUrl field.
func (r *sCIMConfigurationResolver) EndpointURL(ctx context.Context, obj *types.SCIMConfiguration) (string, error) {
return r.baseURL.WithPath("/api/connect/v1/scim/2.0").MustString(), nil
}
// Organization is the resolver for the organization field.
func (r *sCIMConfigurationResolver) Organization(ctx context.Context, obj *types.SCIMConfiguration) (*types.Organization, error) {
if err := r.authorize(ctx, obj.Organization.ID, iam.ActionOrganizationGet); err != nil {
return nil, err
}
if gqlutils.OnlyIDSelected(ctx) {
return &types.Organization{
ID: obj.Organization.ID,
}, nil
}
organization, err := r.iam.OrganizationService.GetOrganization(ctx, obj.Organization.ID)
if err != nil {
var errOrganizationNotFound *iam.ErrOrganizationNotFound
if errors.As(err, &errOrganizationNotFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get organization for scim configuration", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Bridge is the resolver for the bridge field.
func (r *sCIMConfigurationResolver) Bridge(ctx context.Context, obj *types.SCIMConfiguration) (*types.SCIMBridge, error) {
if obj.Bridge == nil {
return nil, nil
}
if err := r.authorize(ctx, obj.ID, iam.ActionSCIMConfigurationGet); err != nil {
return nil, err
}
bridge, err := r.iam.OrganizationService.GetSCIMBridgeByID(ctx, obj.Bridge.ID)
if err != nil {
var errSCIMBridgeNotFound *iam.ErrSCIMBridgeNotFound
if errors.As(err, &errSCIMBridgeNotFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get scim bridge", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSCIMBridge(bridge), nil
}
// Events is the resolver for the events field.
func (r *sCIMConfigurationResolver) Events(ctx context.Context, obj *types.SCIMConfiguration, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.SCIMEventOrderBy) (*types.SCIMEventConnection, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionSCIMEventList); err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.SCIMEventOrderField]{
Field: coredata.SCIMEventOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy.Field = coredata.SCIMEventOrderField(orderBy.Field)
pageOrderBy.Direction = page.OrderDirection(orderBy.Direction)
}
cursor := cursor.NewCursor(first, after, last, before, pageOrderBy)
events, err := r.iam.OrganizationService.ListSCIMEventsByConfigID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list scim events", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSCIMEventConnection(events, r, obj.ID), nil
}
// Permission is the resolver for the permission field.
func (r *sCIMConfigurationResolver) Permission(ctx context.Context, obj *types.SCIMConfiguration, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// Permission is the resolver for the permission field.
func (r *sCIMEventResolver) Permission(ctx context.Context, obj *types.SCIMEvent, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *sCIMEventConnectionResolver) TotalCount(ctx context.Context, obj *types.SCIMEventConnection) (*int, error) {
if err := r.authorize(ctx, obj.ParentID, iam.ActionSCIMEventList); err != nil {
return nil, err
}
switch obj.Resolver.(type) {
case *sCIMConfigurationResolver:
count, err := r.iam.OrganizationService.CountSCIMEvents(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count scim events", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
return nil, gqlutils.Internal(ctx)
}
// Connector returns schema.ConnectorResolver implementation.
func (r *Resolver) Connector() schema.ConnectorResolver { return &connectorResolver{r} }
// SCIMBridge returns schema.SCIMBridgeResolver implementation.
func (r *Resolver) SCIMBridge() schema.SCIMBridgeResolver { return &sCIMBridgeResolver{r} }
// SCIMConfiguration returns schema.SCIMConfigurationResolver implementation.
func (r *Resolver) SCIMConfiguration() schema.SCIMConfigurationResolver {
return &sCIMConfigurationResolver{r}
}
// SCIMEvent returns schema.SCIMEventResolver implementation.
func (r *Resolver) SCIMEvent() schema.SCIMEventResolver { return &sCIMEventResolver{r} }
// SCIMEventConnection returns schema.SCIMEventConnectionResolver implementation.
func (r *Resolver) SCIMEventConnection() schema.SCIMEventConnectionResolver {
return &sCIMEventConnectionResolver{r}
}
type connectorResolver struct{ *Resolver }
type sCIMBridgeResolver struct{ *Resolver }
type sCIMConfigurationResolver struct{ *Resolver }
type sCIMEventResolver struct{ *Resolver }
type sCIMEventConnectionResolver struct{ *Resolver }

View File

@@ -0,0 +1,104 @@
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"
"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/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"
)
// 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
}
// 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) {
return &types.Identity{
ID: obj.Identity.ID,
}, nil
}
identity, err := r.iam.AccountService.GetIdentity(ctx, obj.Identity.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get identity for session", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewIdentity(identity), nil
}
// Permission is the resolver for the permission field.
func (r *sessionResolver) Permission(ctx context.Context, obj *types.Session, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *sessionConnectionResolver) TotalCount(ctx context.Context, obj *types.SessionConnection) (*int, error) {
switch obj.Resolver.(type) {
case *identityResolver:
count, err := r.iam.AccountService.CountSessions(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count sessions", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
return nil, gqlutils.Internal(ctx)
}
// Session returns schema.SessionResolver implementation.
func (r *Resolver) Session() schema.SessionResolver { return &sessionResolver{r} }
// SessionConnection returns schema.SessionConnectionResolver implementation.
func (r *Resolver) SessionConnection() schema.SessionConnectionResolver {
return &sessionConnectionResolver{r}
}
type sessionResolver struct{ *Resolver }
type sessionConnectionResolver struct{ *Resolver }

File diff suppressed because it is too large Load Diff

View File

@@ -6,18 +6,26 @@ GraphQL API using `gqlgen`. Schema-first approach.
| File | Type | Notes |
|------|------|-------|
| `schema.graphql` | Hand-written | GraphQL schema definition |
| `graphql/*.graphql` | Hand-written | GraphQL schema split by entity (one file per coredata model) |
| `gqlgen.yaml` | Hand-written | Codegen config |
| `resolver.go` | Hand-written | Root `Resolver` struct and `NewMux` |
| `graphql_handler.go` | Hand-written | Handler setup |
| `v1_resolver.go` | Generated stubs | Resolver method implementations (edit the bodies) |
| `*.resolvers.go` | Generated stubs | Per-entity resolver files (edit the bodies) |
| `schema/schema.go` | **Generated — DO NOT EDIT** | Executable schema |
| `types/types.go` | **Generated — DO NOT EDIT** | Type definitions |
## Schema file organization
Schema files live in `graphql/` and are split by coredata model:
- `base.graphql` — directives, scalars, Node, PageInfo, root Query/Mutation/Organization/Viewer types
- Entity files (e.g., `vendor.graphql`, `control.graphql`) — use `extend type Organization`, `extend type Mutation`, etc. to add fields
When adding a new entity, create a new `.graphql` file in `graphql/`. Types that get extended across files (Organization, Mutation, Viewer) must be defined in `base.graphql`.
## Important rules
- **Never edit generated files** (`schema/schema.go`, `types/types.go`). Only edit `schema.graphql` and resolver bodies.
- **After any change to `schema.graphql`**, always run codegen:
- **Never edit generated files** (`schema/schema.go`, `types/types.go`). Only edit `graphql/*.graphql` and resolver bodies.
- **After any change to `graphql/*.graphql`**, always run codegen:
```
go generate ./pkg/server/api/console/v1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,497 @@
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"
"fmt"
"github.com/vikstrous/dataloadgen"
"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/probo"
"go.probo.inc/probo/pkg/server/api/console/v1/dataloader"
"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"
"go.probo.inc/probo/pkg/validator"
)
// Owner is the resolver for the owner field.
func (r *assetResolver) Owner(ctx context.Context, obj *types.Asset) (*types.Profile, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
owner, err := loaders.Profile.Load(ctx, obj.Owner.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get owner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewProfile(owner), nil
}
// Vendors is the resolver for the vendors field.
func (r *assetResolver) Vendors(ctx context.Context, obj *types.Asset, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorOrderBy) (*types.VendorConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionVendorList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.VendorOrderField]{
Field: coredata.VendorOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.VendorOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.Vendors.ListForAssetID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list asset vendors", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewVendorConnection(page, r, obj.ID), nil
}
// Organization is the resolver for the organization field.
func (r *assetResolver) Organization(ctx context.Context, obj *types.Asset) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
asset, err := prb.Assets.Get(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
org, err := prb.Organizations.Get(ctx, asset.OrganizationID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(org), nil
}
// Permission is the resolver for the permission field.
func (r *assetResolver) Permission(ctx context.Context, obj *types.Asset, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *assetConnectionResolver) TotalCount(ctx context.Context, obj *types.AssetConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionAssetList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *organizationResolver:
assetFilter := coredata.NewAssetFilter(nil)
if obj.Filter != nil {
assetFilter = coredata.NewAssetFilter(&obj.Filter.SnapshotID)
}
count, err := prb.Assets.CountForOrganizationID(ctx, obj.ParentID, assetFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count assets", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver")
return 0, gqlutils.Internal(ctx)
}
// Owner is the resolver for the owner field.
func (r *datumResolver) Owner(ctx context.Context, obj *types.Datum) (*types.Profile, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
owner, err := loaders.Profile.Load(ctx, obj.Owner.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
return nil, fmt.Errorf("cannot get owner: %w", err)
}
return types.NewProfile(owner), nil
}
// Vendors is the resolver for the vendors field.
func (r *datumResolver) Vendors(ctx context.Context, obj *types.Datum, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorOrderBy) (*types.VendorConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionVendorList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.VendorOrderField]{
Field: coredata.VendorOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.VendorOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.Data.ListVendors(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list data vendors", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewVendorConnection(page, r, obj.ID), nil
}
// Organization is the resolver for the organization field.
func (r *datumResolver) Organization(ctx context.Context, obj *types.Datum) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
org, err := loaders.Organization.Load(ctx, obj.OrganizationID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(org), nil
}
// Permission is the resolver for the permission field.
func (r *datumResolver) Permission(ctx context.Context, obj *types.Datum, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *datumConnectionResolver) TotalCount(ctx context.Context, obj *types.DatumConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionDatumList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *organizationResolver:
datumFilter := coredata.NewDatumFilter(nil)
if obj.Filter != nil {
datumFilter = coredata.NewDatumFilter(&obj.Filter.SnapshotID)
}
count, err := prb.Data.CountForOrganizationID(ctx, obj.ParentID, datumFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count data", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver")
return 0, gqlutils.Internal(ctx)
}
// CreateAsset is the resolver for the createAsset field.
func (r *mutationResolver) CreateAsset(ctx context.Context, input types.CreateAssetInput) (*types.CreateAssetPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionAssetCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
asset, err := prb.Assets.Create(
ctx,
probo.CreateAssetRequest{
OrganizationID: input.OrganizationID,
Name: input.Name,
Amount: input.Amount,
OwnerID: input.OwnerID,
AssetType: input.AssetType,
DataTypesStored: input.DataTypesStored,
VendorIDs: input.VendorIds,
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create asset", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateAssetPayload{
AssetEdge: types.NewAssetEdge(asset, coredata.AssetOrderFieldCreatedAt),
}, nil
}
// UpdateAsset is the resolver for the updateAsset field.
func (r *mutationResolver) UpdateAsset(ctx context.Context, input types.UpdateAssetInput) (*types.UpdateAssetPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionAssetUpdate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ID.TenantID())
asset, err := prb.Assets.Update(
ctx,
probo.UpdateAssetRequest{
ID: input.ID,
Name: input.Name,
Amount: input.Amount,
OwnerID: input.OwnerID,
AssetType: input.AssetType,
DataTypesStored: input.DataTypesStored,
VendorIDs: input.VendorIds,
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update asset", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateAssetPayload{
Asset: types.NewAsset(asset),
}, nil
}
// DeleteAsset is the resolver for the deleteAsset field.
func (r *mutationResolver) DeleteAsset(ctx context.Context, input types.DeleteAssetInput) (*types.DeleteAssetPayload, error) {
if err := r.authorize(ctx, input.AssetID, probo.ActionAssetDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.AssetID.TenantID())
err := prb.Assets.Delete(ctx, input.AssetID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete asset", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteAssetPayload{
DeletedAssetID: input.AssetID,
}, nil
}
// CreateDatum is the resolver for the createDatum field.
func (r *mutationResolver) CreateDatum(ctx context.Context, input types.CreateDatumInput) (*types.CreateDatumPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionDatumCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
data, err := prb.Data.Create(
ctx,
probo.CreateDatumRequest{
OrganizationID: input.OrganizationID,
Name: input.Name,
DataClassification: input.DataClassification,
OwnerID: input.OwnerID,
VendorIDs: input.VendorIds,
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create datum", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateDatumPayload{
DatumEdge: types.NewDatumEdge(data, coredata.DatumOrderFieldCreatedAt),
}, nil
}
// UpdateDatum is the resolver for the updateDatum field.
func (r *mutationResolver) UpdateDatum(ctx context.Context, input types.UpdateDatumInput) (*types.UpdateDatumPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionDatumUpdate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ID.TenantID())
datum, err := prb.Data.Update(
ctx,
probo.UpdateDatumRequest{
ID: input.ID,
Name: input.Name,
DataClassification: input.DataClassification,
OwnerID: input.OwnerID,
VendorIDs: input.VendorIds,
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update datum", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateDatumPayload{
Datum: types.NewDatum(datum),
}, nil
}
// DeleteDatum is the resolver for the deleteDatum field.
func (r *mutationResolver) DeleteDatum(ctx context.Context, input types.DeleteDatumInput) (*types.DeleteDatumPayload, error) {
if err := r.authorize(ctx, input.DatumID, probo.ActionDatumDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.DatumID.TenantID())
if err := prb.Data.Delete(ctx, input.DatumID); err != nil {
r.logger.ErrorCtx(ctx, "cannot delete datum", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteDatumPayload{
DeletedDatumID: input.DatumID,
}, nil
}
// Assets is the resolver for the assets field.
func (r *organizationResolver) Assets(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.AssetOrderBy, filter *types.AssetFilter) (*types.AssetConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionAssetList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.AssetOrderField]{
Field: coredata.AssetOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.AssetOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
assetFilter := coredata.NewAssetFilter(nil)
if filter != nil {
assetFilter = coredata.NewAssetFilter(&filter.SnapshotID)
}
page, err := prb.Assets.ListForOrganizationID(ctx, obj.ID, cursor, assetFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization assets", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewAssetConnection(page, r, obj.ID, filter), nil
}
// Assets is the resolver for the assets field.
func (r *organizationResolver) Data(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DatumOrderBy, filter *types.DatumFilter) (*types.DatumConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionDatumList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.DatumOrderField]{
Field: coredata.DatumOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.DatumOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
datumFilter := coredata.NewDatumFilter(nil)
if filter != nil {
datumFilter = coredata.NewDatumFilter(&filter.SnapshotID)
}
page, err := prb.Data.ListForOrganizationID(ctx, obj.ID, cursor, datumFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization data", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewDataConnection(page, r, obj.ID, filter), nil
}
// Asset returns schema.AssetResolver implementation.
func (r *Resolver) Asset() schema.AssetResolver { return &assetResolver{r} }
// AssetConnection returns schema.AssetConnectionResolver implementation.
func (r *Resolver) AssetConnection() schema.AssetConnectionResolver {
return &assetConnectionResolver{r}
}
// Datum returns schema.DatumResolver implementation.
func (r *Resolver) Datum() schema.DatumResolver { return &datumResolver{r} }
// DatumConnection returns schema.DatumConnectionResolver implementation.
func (r *Resolver) DatumConnection() schema.DatumConnectionResolver {
return &datumConnectionResolver{r}
}
type assetResolver struct{ *Resolver }
type assetConnectionResolver struct{ *Resolver }
type datumResolver struct{ *Resolver }
type datumConnectionResolver struct{ *Resolver }

View File

@@ -0,0 +1,821 @@
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"
"time"
"github.com/vikstrous/dataloadgen"
"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/console/v1/dataloader"
"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"
"go.probo.inc/probo/pkg/validator"
)
// Organization is the resolver for the organization field.
func (r *auditResolver) Organization(ctx context.Context, obj *types.Audit) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
organization, err := loaders.Organization.Load(ctx, obj.Organization.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot load organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Framework is the resolver for the framework field.
func (r *auditResolver) Framework(ctx context.Context, obj *types.Audit) (*types.Framework, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionFrameworkGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
framework, err := loaders.Framework.Load(ctx, obj.Framework.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot load framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewFramework(framework), nil
}
// Report is the resolver for the report field.
func (r *auditResolver) Report(ctx context.Context, obj *types.Audit) (*types.Report, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionReportGet); err != nil {
return nil, err
}
if obj.Report == nil {
return nil, nil
}
loaders := dataloader.FromContext(ctx)
report, err := loaders.Report.Load(ctx, obj.Report.ID)
if err != nil {
if errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot load report", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewReport(report), nil
}
// ReportURL is the resolver for the reportUrl field.
func (r *auditResolver) ReportURL(ctx context.Context, obj *types.Audit) (*string, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionReportGetReportUrl); err != nil {
return nil, err
}
if obj.Report == nil {
return nil, nil
}
prb := r.ProboService(ctx, obj.ID.TenantID())
url, err := prb.Audits.GenerateReportURL(ctx, obj.ID, 15*time.Minute)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate report URL", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return url, nil
}
// Controls is the resolver for the controls field.
func (r *auditResolver) Controls(ctx context.Context, obj *types.Audit, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionControlList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ControlOrderField]{
Field: coredata.ControlOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.ControlOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
var controlFilter = coredata.NewControlFilter(nil)
if filter != nil {
controlFilter = coredata.NewControlFilter(filter.Query)
}
page, err := prb.Controls.ListForAuditID(ctx, obj.ID, cursor, controlFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list audit controls", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewControlConnection(page, r, obj.ID, controlFilter), nil
}
// Findings is the resolver for the findings field.
func (r *auditResolver) Findings(ctx context.Context, obj *types.Audit, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.FindingOrder, filter *types.FindingFilter) (*types.FindingConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionFindingList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.FindingOrderField]{
Field: coredata.FindingOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.FindingOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
var (
kind *coredata.FindingKind
status *coredata.FindingStatus
priority *coredata.FindingPriority
ownerID *gid.GID
)
if filter != nil {
kind = filter.Kind
status = filter.Status
priority = filter.Priority
ownerID = filter.OwnerID
}
findingFilter := coredata.NewFindingFilter(nil, kind, status, priority, ownerID)
if filter != nil {
findingFilter = coredata.NewFindingFilter(&filter.SnapshotID, kind, status, priority, ownerID)
}
p, err := prb.Findings.ListForAuditID(ctx, obj.ID, cursor, findingFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list audit findings", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewFindingConnection(p, r, obj.ID, filter), nil
}
// Permission is the resolver for the permission field.
func (r *auditResolver) Permission(ctx context.Context, obj *types.Audit, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *auditConnectionResolver) TotalCount(ctx context.Context, obj *types.AuditConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionAuditList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *organizationResolver:
count, err := prb.Audits.CountForOrganizationID(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count audits", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
case *findingResolver:
count, err := prb.Audits.CountForFindingID(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count audits", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
case *controlResolver:
count, err := prb.Audits.CountForControlID(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count audits", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
default:
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
return 0, gqlutils.Internal(ctx)
}
}
// Organization is the resolver for the organization field.
func (r *findingResolver) Organization(ctx context.Context, obj *types.Finding) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
organization, err := loaders.Organization.Load(ctx, obj.Organization.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get finding organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Audits is the resolver for the audits field.
func (r *findingResolver) Audits(ctx context.Context, obj *types.Finding, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.AuditOrderBy) (*types.AuditConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionAuditList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.AuditOrderField]{
Field: coredata.AuditOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.AuditOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
p, err := prb.Audits.ListForFindingID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list finding audits", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewAuditConnection(p, r, obj.ID), nil
}
// Owner is the resolver for the owner field.
func (r *findingResolver) Owner(ctx context.Context, obj *types.Finding) (*types.Profile, error) {
if obj.Owner == nil {
return nil, nil
}
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
owner, err := loaders.Profile.Load(ctx, obj.Owner.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get finding owner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewProfile(owner), nil
}
// Risk is the resolver for the risk field.
func (r *findingResolver) Risk(ctx context.Context, obj *types.Finding) (*types.Risk, error) {
if obj.Risk == nil {
return nil, nil
}
if err := r.authorize(ctx, obj.ID, probo.ActionRiskGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
risk, err := loaders.Risk.Load(ctx, obj.Risk.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get finding risk", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewRisk(risk), nil
}
// Permission is the resolver for the permission field.
func (r *findingResolver) Permission(ctx context.Context, obj *types.Finding, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *findingConnectionResolver) TotalCount(ctx context.Context, obj *types.FindingConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionFindingList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
var (
kind *coredata.FindingKind
status *coredata.FindingStatus
priority *coredata.FindingPriority
ownerID *gid.GID
)
if obj.Filter != nil {
kind = obj.Filter.Kind
status = obj.Filter.Status
priority = obj.Filter.Priority
ownerID = obj.Filter.OwnerID
}
findingFilter := coredata.NewFindingFilter(nil, kind, status, priority, ownerID)
if obj.Filter != nil {
findingFilter = coredata.NewFindingFilter(&obj.Filter.SnapshotID, kind, status, priority, ownerID)
}
switch obj.Resolver.(type) {
case *organizationResolver:
count, err := prb.Findings.CountForOrganizationID(ctx, obj.ParentID, findingFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count findings", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
case *auditResolver:
count, err := prb.Findings.CountForAuditID(ctx, obj.ParentID, findingFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count findings", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
return 0, gqlutils.Internal(ctx)
}
// CreateAudit is the resolver for the createAudit field.
func (r *mutationResolver) CreateAudit(ctx context.Context, input types.CreateAuditInput) (*types.CreateAuditPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionAuditCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
req := probo.CreateAuditRequest{
OrganizationID: input.OrganizationID,
FrameworkID: input.FrameworkID,
Name: input.Name,
ValidFrom: input.ValidFrom,
ValidUntil: input.ValidUntil,
State: input.State,
TrustCenterVisibility: input.TrustCenterVisibility,
}
audit, err := prb.Audits.Create(ctx, &req)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create audit", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if input.File != nil {
uploadReq := probo.UploadAuditReportRequest{
AuditID: audit.ID,
File: probo.File{
Content: input.File.File,
Filename: input.File.Filename,
Size: input.File.Size,
ContentType: input.File.ContentType,
},
}
audit, err = prb.Audits.UploadReport(ctx, uploadReq)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot upload audit report", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
}
return &types.CreateAuditPayload{
AuditEdge: types.NewAuditEdge(audit, coredata.AuditOrderFieldCreatedAt),
}, nil
}
// UpdateAudit is the resolver for the updateAudit field.
func (r *mutationResolver) UpdateAudit(ctx context.Context, input types.UpdateAuditInput) (*types.UpdateAuditPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionAuditUpdate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ID.TenantID())
req := probo.UpdateAuditRequest{
ID: input.ID,
Name: gqlutils.UnwrapOmittable(input.Name),
ValidFrom: input.ValidFrom,
ValidUntil: input.ValidUntil,
State: input.State,
TrustCenterVisibility: input.TrustCenterVisibility,
}
audit, err := prb.Audits.Update(ctx, &req)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update audit", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateAuditPayload{
Audit: types.NewAudit(audit),
}, nil
}
// DeleteAudit is the resolver for the deleteAudit field.
func (r *mutationResolver) DeleteAudit(ctx context.Context, input types.DeleteAuditInput) (*types.DeleteAuditPayload, error) {
if err := r.authorize(ctx, input.AuditID, probo.ActionAuditDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.AuditID.TenantID())
err := prb.Audits.Delete(ctx, input.AuditID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete audit", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteAuditPayload{
DeletedAuditID: &input.AuditID,
}, nil
}
// UploadAuditReport is the resolver for the uploadAuditReport field.
func (r *mutationResolver) UploadAuditReport(ctx context.Context, input types.UploadAuditReportInput) (*types.UploadAuditReportPayload, error) {
if err := r.authorize(ctx, input.AuditID, probo.ActionAuditReportUpload); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.AuditID.TenantID())
req := probo.UploadAuditReportRequest{
AuditID: input.AuditID,
File: probo.File{
Content: input.File.File,
Filename: input.File.Filename,
Size: input.File.Size,
ContentType: input.File.ContentType,
},
}
audit, err := prb.Audits.UploadReport(ctx, req)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot upload audit report", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UploadAuditReportPayload{
Audit: types.NewAudit(audit),
}, nil
}
// DeleteAuditReport is the resolver for the deleteAuditReport field.
func (r *mutationResolver) DeleteAuditReport(ctx context.Context, input types.DeleteAuditReportInput) (*types.DeleteAuditReportPayload, error) {
if err := r.authorize(ctx, input.AuditID, probo.ActionAuditReportDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.AuditID.TenantID())
audit, err := prb.Audits.DeleteReport(ctx, input.AuditID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete audit report", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteAuditReportPayload{
Audit: types.NewAudit(audit),
}, nil
}
// CreateFinding is the resolver for the createFinding field.
func (r *mutationResolver) CreateFinding(ctx context.Context, input types.CreateFindingInput) (*types.CreateFindingPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionFindingCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
req := probo.CreateFindingRequest{
OrganizationID: input.OrganizationID,
Kind: input.Kind,
Description: input.Description,
Source: input.Source,
IdentifiedOn: input.IdentifiedOn,
RootCause: input.RootCause,
CorrectiveAction: input.CorrectiveAction,
OwnerID: input.OwnerID,
DueDate: input.DueDate,
Status: &input.Status,
Priority: &input.Priority,
RiskID: input.RiskID,
EffectivenessCheck: input.EffectivenessCheck,
}
finding, err := prb.Findings.Create(ctx, &req)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create finding", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateFindingPayload{
FindingEdge: types.NewFindingEdge(finding, coredata.FindingOrderFieldCreatedAt),
}, nil
}
// UpdateFinding is the resolver for the updateFinding field.
func (r *mutationResolver) UpdateFinding(ctx context.Context, input types.UpdateFindingInput) (*types.UpdateFindingPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionFindingUpdate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ID.TenantID())
req := probo.UpdateFindingRequest{
ID: input.ID,
Description: gqlutils.UnwrapOmittable(input.Description),
Source: gqlutils.UnwrapOmittable(input.Source),
IdentifiedOn: gqlutils.UnwrapOmittable(input.IdentifiedOn),
RootCause: gqlutils.UnwrapOmittable(input.RootCause),
CorrectiveAction: gqlutils.UnwrapOmittable(input.CorrectiveAction),
OwnerID: input.OwnerID,
DueDate: gqlutils.UnwrapOmittable(input.DueDate),
Status: input.Status,
Priority: input.Priority,
RiskID: gqlutils.UnwrapOmittable(input.RiskID),
EffectivenessCheck: gqlutils.UnwrapOmittable(input.EffectivenessCheck),
}
finding, err := prb.Findings.Update(ctx, &req)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update finding", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateFindingPayload{
Finding: types.NewFinding(finding),
}, nil
}
// DeleteFinding is the resolver for the deleteFinding field.
func (r *mutationResolver) DeleteFinding(ctx context.Context, input types.DeleteFindingInput) (*types.DeleteFindingPayload, error) {
if err := r.authorize(ctx, input.FindingID, probo.ActionFindingDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.FindingID.TenantID())
err := prb.Findings.Delete(ctx, input.FindingID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete finding", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteFindingPayload{
DeletedFindingID: &input.FindingID,
}, nil
}
// CreateFindingAuditMapping is the resolver for the createFindingAuditMapping field.
func (r *mutationResolver) CreateFindingAuditMapping(ctx context.Context, input types.CreateFindingAuditMappingInput) (*types.CreateFindingAuditMappingPayload, error) {
if err := r.authorize(ctx, input.FindingID, probo.ActionFindingAuditMappingCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.FindingID.TenantID())
finding, audit, err := prb.Findings.CreateAuditMapping(ctx, input.FindingID, input.AuditID, input.ReferenceID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create finding audit mapping", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateFindingAuditMappingPayload{
FindingEdge: types.NewFindingEdge(finding, coredata.FindingOrderFieldCreatedAt),
AuditEdge: types.NewAuditEdge(audit, coredata.AuditOrderFieldCreatedAt),
}, nil
}
// DeleteFindingAuditMapping is the resolver for the deleteFindingAuditMapping field.
func (r *mutationResolver) DeleteFindingAuditMapping(ctx context.Context, input types.DeleteFindingAuditMappingInput) (*types.DeleteFindingAuditMappingPayload, error) {
if err := r.authorize(ctx, input.FindingID, probo.ActionFindingAuditMappingDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.FindingID.TenantID())
finding, audit, err := prb.Findings.DeleteAuditMapping(ctx, input.FindingID, input.AuditID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete finding audit mapping", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteFindingAuditMappingPayload{
DeletedFindingID: &finding.ID,
DeletedAuditID: &audit.ID,
}, nil
}
// Audits is the resolver for the audits field.
func (r *organizationResolver) Audits(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.AuditOrderBy) (*types.AuditConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionAuditList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.AuditOrderField]{
Field: coredata.AuditOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.AuditOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.Audits.ListForOrganizationID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization audits", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewAuditConnection(page, r, obj.ID), nil
}
// Findings is the resolver for the findings field.
func (r *organizationResolver) Findings(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.FindingOrder, filter *types.FindingFilter) (*types.FindingConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionFindingList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.FindingOrderField]{
Field: coredata.FindingOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.FindingOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
var (
kind *coredata.FindingKind
status *coredata.FindingStatus
priority *coredata.FindingPriority
ownerID *gid.GID
)
if filter != nil {
kind = filter.Kind
status = filter.Status
priority = filter.Priority
ownerID = filter.OwnerID
}
findingFilter := coredata.NewFindingFilter(nil, kind, status, priority, ownerID)
if filter != nil {
findingFilter = coredata.NewFindingFilter(&filter.SnapshotID, kind, status, priority, ownerID)
}
page, err := prb.Findings.ListForOrganizationID(ctx, obj.ID, cursor, findingFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization findings", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewFindingConnection(page, r, obj.ID, filter), nil
}
// DownloadURL is the resolver for the downloadUrl field.
func (r *reportResolver) DownloadURL(ctx context.Context, obj *types.Report) (*string, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionReportDownloadUrlGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
url, err := prb.Reports.GenerateDownloadURL(ctx, obj.ID, 15*time.Minute)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate download URL", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return url, nil
}
// Audit is the resolver for the audit field.
func (r *reportResolver) Audit(ctx context.Context, obj *types.Report) (*types.Audit, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionAuditGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
audit, err := prb.Audits.GetByReportID(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load audit for report", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewAudit(audit), nil
}
// Permission is the resolver for the permission field.
func (r *reportResolver) Permission(ctx context.Context, obj *types.Report, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// Audit returns schema.AuditResolver implementation.
func (r *Resolver) Audit() schema.AuditResolver { return &auditResolver{r} }
// AuditConnection returns schema.AuditConnectionResolver implementation.
func (r *Resolver) AuditConnection() schema.AuditConnectionResolver {
return &auditConnectionResolver{r}
}
// Finding returns schema.FindingResolver implementation.
func (r *Resolver) Finding() schema.FindingResolver { return &findingResolver{r} }
// FindingConnection returns schema.FindingConnectionResolver implementation.
func (r *Resolver) FindingConnection() schema.FindingConnectionResolver {
return &findingConnectionResolver{r}
}
// Report returns schema.ReportResolver implementation.
func (r *Resolver) Report() schema.ReportResolver { return &reportResolver{r} }
type auditResolver struct{ *Resolver }
type auditConnectionResolver struct{ *Resolver }
type findingResolver struct{ *Resolver }
type findingConnectionResolver struct{ *Resolver }
type reportResolver struct{ *Resolver }

View File

@@ -0,0 +1,99 @@
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"
"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/console/v1/schema"
"go.probo.inc/probo/pkg/server/api/console/v1/types"
"go.probo.inc/probo/pkg/server/gqlutils"
)
// Organization is the resolver for the organization field.
func (r *auditLogEntryResolver) Organization(ctx context.Context, obj *types.AuditLogEntry) (*types.Organization, error) {
return obj.Organization, nil
}
// Permission is the resolver for the permission field.
func (r *auditLogEntryResolver) Permission(ctx context.Context, obj *types.AuditLogEntry, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *auditLogEntryConnectionResolver) TotalCount(ctx context.Context, obj *types.AuditLogEntryConnection) (int, error) {
filter := coredata.NewAuditLogEntryFilter()
if obj.Filter != nil {
filter = obj.Filter
}
count, err := r.iam.OrganizationService.CountAuditLogEntries(ctx, obj.ParentID, filter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count audit log entries", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
// AuditLogEntries is the resolver for the auditLogEntries field.
func (r *organizationResolver) AuditLogEntries(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.AuditLogEntryOrderBy, filter *types.AuditLogEntryFilter) (*types.AuditLogEntryConnection, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionAuditLogEntryList); err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.AuditLogEntryOrderField]{
Field: coredata.AuditLogEntryOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.AuditLogEntryOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
coredataFilter := coredata.NewAuditLogEntryFilter()
if filter != nil {
if filter.Action != nil {
coredataFilter.WithAction(*filter.Action)
}
if filter.ActorID != nil {
coredataFilter.WithActorID(*filter.ActorID)
}
if filter.ResourceType != nil {
coredataFilter.WithResourceType(*filter.ResourceType)
}
if filter.ResourceID != nil {
coredataFilter.WithResourceID(*filter.ResourceID)
}
}
p, err := r.iam.OrganizationService.ListAuditLogEntries(ctx, obj.ID, cursor, coredataFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list audit log entries", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewAuditLogEntryConnection(p, r, obj.ID, coredataFilter), nil
}
// AuditLogEntry returns schema.AuditLogEntryResolver implementation.
func (r *Resolver) AuditLogEntry() schema.AuditLogEntryResolver { return &auditLogEntryResolver{r} }
// AuditLogEntryConnection returns schema.AuditLogEntryConnectionResolver implementation.
func (r *Resolver) AuditLogEntryConnection() schema.AuditLogEntryConnectionResolver {
return &auditLogEntryConnectionResolver{r}
}
type auditLogEntryResolver struct{ *Resolver }
type auditLogEntryConnectionResolver struct{ *Resolver }

View File

@@ -0,0 +1,565 @@
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"
"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"
"go.probo.inc/probo/pkg/server/api/console/v1/types"
"go.probo.inc/probo/pkg/server/gqlutils"
"go.probo.inc/probo/pkg/server/gqlutils/types/cursor"
"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 {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
req := probo.UpdateOrganizationContextRequest{
OrganizationID: input.OrganizationID,
Product: gqlutils.UnwrapOmittable(input.Product),
Architecture: gqlutils.UnwrapOmittable(input.Architecture),
Team: gqlutils.UnwrapOmittable(input.Team),
Processes: gqlutils.UnwrapOmittable(input.Processes),
Customers: gqlutils.UnwrapOmittable(input.Customers),
}
organizationContext, err := prb.Organizations.UpdateContext(ctx, req)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update organization context", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateOrganizationContextPayload{
Context: types.NewOrganizationContext(organizationContext),
}, nil
}
// LogoURL is the resolver for the logoUrl field.
func (r *organizationResolver) LogoURL(ctx context.Context, obj *types.Organization) (*string, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGetLogoUrl); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
logoURL, err := prb.Organizations.GenerateLogoURL(ctx, obj.ID, 1*time.Hour)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate logo url", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return logoURL, nil
}
// HorizontalLogoURL is the resolver for the horizontalLogoUrl field.
func (r *organizationResolver) HorizontalLogoURL(ctx context.Context, obj *types.Organization) (*string, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGetHorizontalLogoUrl); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
horizontalLogoURL, err := prb.Organizations.GenerateHorizontalLogoURL(ctx, obj.ID, 1*time.Hour)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate horizontal logo url", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return horizontalLogoURL, nil
}
// Context is the resolver for the context field.
func (r *organizationResolver) Context(ctx context.Context, obj *types.Organization) (*types.OrganizationContext, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationContextGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
orgContext, err := prb.Organizations.GetContext(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load organization context", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganizationContext(orgContext), nil
}
// Profiles is the resolver for the profiles field.
func (r *organizationResolver) Profiles(ctx context.Context, obj *types.Organization, 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); err != nil {
return nil, err
}
if gqlutils.OnlyTotalCountSelected(ctx) {
return &types.ProfileConnection{
Resolver: r,
ParentID: obj.ID,
}, nil
}
filters := coredata.NewMembershipProfileFilter(nil).WithMembership()
if filter != nil {
filters = coredata.NewMembershipProfileFilter(filter.ExcludeContractEnded).WithMembership()
}
pageOrderBy := page.OrderBy[coredata.MembershipProfileOrderField]{
Field: coredata.MembershipProfileOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy.Field = coredata.MembershipProfileOrderField(orderBy.Field)
pageOrderBy.Direction = page.OrderDirection(orderBy.Direction)
}
cursor := cursor.NewCursor(first, after, last, before, pageOrderBy)
page, err := r.iam.OrganizationService.ListProfiles(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
}
// MeasureCategories is the resolver for the measureCategories field.
func (r *organizationResolver) MeasureCategories(ctx context.Context, obj *types.Organization) ([]string, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionMeasureList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
categories, err := prb.Measures.ListDistinctCategoriesForOrganizationID(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list measure categories", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return categories, nil
}
// Permission is the resolver for the permission field.
func (r *organizationResolver) Permission(ctx context.Context, obj *types.Organization, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// Node is the resolver for the node field.
func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) {
var (
loadNode func(ctx context.Context, id gid.GID) (types.Node, error)
action string
prb = r.ProboService(ctx, id.TenantID())
)
switch id.EntityType() {
case coredata.OrganizationEntityType:
action = iam.ActionOrganizationGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
organization, err := prb.Organizations.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewOrganization(organization), nil
}
case coredata.VendorEntityType:
action = probo.ActionVendorGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
vendor, err := prb.Vendors.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewVendor(vendor), nil
}
case coredata.FrameworkEntityType:
action = probo.ActionFrameworkGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
framework, err := prb.Frameworks.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewFramework(framework), nil
}
case coredata.MeasureEntityType:
action = probo.ActionMeasureGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
measure, err := prb.Measures.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewMeasure(measure), nil
}
case coredata.TaskEntityType:
action = probo.ActionTaskGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
task, err := prb.Tasks.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewTask(task), nil
}
case coredata.EvidenceEntityType:
action = probo.ActionEvidenceList
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
evidence, err := prb.Evidences.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewEvidence(evidence), nil
}
case coredata.DocumentEntityType:
action = probo.ActionDocumentGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
document, err := prb.Documents.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewDocument(document), nil
}
case coredata.ControlEntityType:
action = probo.ActionControlList
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
control, err := prb.Controls.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewControl(control), nil
}
case coredata.RiskEntityType:
action = probo.ActionRiskGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
risk, err := prb.Risks.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewRisk(risk), nil
}
case coredata.VendorComplianceReportEntityType:
action = probo.ActionVendorComplianceReportGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
vendorComplianceReport, err := prb.VendorComplianceReports.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewVendorComplianceReport(vendorComplianceReport), nil
}
case coredata.VendorContactEntityType:
action = probo.ActionVendorContactGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
vendorContact, err := prb.VendorContacts.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewVendorContact(vendorContact), nil
}
case coredata.VendorServiceEntityType:
action = probo.ActionVendorServiceGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
vendorService, err := prb.VendorServices.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewVendorService(vendorService), nil
}
case coredata.DocumentVersionEntityType:
action = probo.ActionDocumentVersionList
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
documentVersion, err := prb.Documents.GetVersion(ctx, id)
if err != nil {
return nil, err
}
return types.NewDocumentVersion(documentVersion), nil
}
case coredata.DocumentVersionSignatureEntityType:
action = probo.ActionDocumentVersionSignatureList
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
documentVersionSignature, err := prb.Documents.GetVersionSignature(ctx, id)
if err != nil {
return nil, err
}
return types.NewDocumentVersionSignature(documentVersionSignature), nil
}
case coredata.AssetEntityType:
action = probo.ActionAssetList
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
asset, err := prb.Assets.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewAsset(asset), nil
}
case coredata.DatumEntityType:
action = probo.ActionDatumList
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
datum, err := prb.Data.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewDatum(datum), nil
}
case coredata.AuditEntityType:
action = probo.ActionAuditList
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
audit, err := prb.Audits.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewAudit(audit), nil
}
case coredata.FindingEntityType:
action = probo.ActionFindingList
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
finding, err := prb.Findings.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewFinding(finding), nil
}
case coredata.ObligationEntityType:
action = probo.ActionObligationList
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
obligation, err := prb.Obligations.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewObligation(obligation), nil
}
case coredata.ReportEntityType:
action = probo.ActionReportGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
report, err := prb.Reports.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewReport(report), nil
}
case coredata.ProcessingActivityEntityType:
action = probo.ActionProcessingActivityList
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
processingActivity, err := prb.ProcessingActivities.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewProcessingActivity(processingActivity), nil
}
case coredata.DataProtectionImpactAssessmentEntityType:
// TODO: add action
// action = probo.ActionDataProtectionImpactAssessmentGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
dpia, err := prb.DataProtectionImpactAssessments.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewDataProtectionImpactAssessment(dpia), nil
}
case coredata.TransferImpactAssessmentEntityType:
// TODO: add action
//action = probo.ActionTransferImpactAssessmentGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
tia, err := prb.TransferImpactAssessments.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewTransferImpactAssessment(tia), nil
}
case coredata.SnapshotEntityType:
action = probo.ActionSnapshotList
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
snapshot, err := prb.Snapshots.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewSnapshot(snapshot), nil
}
case coredata.TrustCenterEntityType:
action = probo.ActionTrustCenterGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
trustCenter, err := prb.TrustCenters.Get(ctx, id)
if err != nil {
return nil, err
}
var file *coredata.File
if trustCenter.NonDisclosureAgreementFileID != nil {
file, err = prb.Files.Get(ctx, *trustCenter.NonDisclosureAgreementFileID)
if err != nil {
return nil, fmt.Errorf("cannot get NDA file: %w", err)
}
}
return types.NewTrustCenter(trustCenter, file), nil
}
case coredata.TrustCenterAccessEntityType:
action = probo.ActionTrustCenterAccessGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
trustCenterAccess, err := prb.TrustCenterAccesses.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewTrustCenterAccess(trustCenterAccess), nil
}
case coredata.MeetingEntityType:
action = probo.ActionMeetingGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
meeting, err := prb.Meetings.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewMeeting(meeting), nil
}
case coredata.RightsRequestEntityType:
action = probo.ActionRightsRequestGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
rightsRequest, err := prb.RightsRequests.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewRightsRequest(rightsRequest), nil
}
case coredata.StatementOfApplicabilityEntityType:
action = probo.ActionStatementOfApplicabilityGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
statementOfApplicability, err := prb.StatementsOfApplicability.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewStatementOfApplicability(statementOfApplicability), nil
}
case coredata.WebhookSubscriptionEntityType:
action = probo.ActionWebhookSubscriptionGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
wc, err := prb.WebhookSubscriptions.Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewWebhookSubscription(wc), nil
}
case coredata.AccessReviewCampaignEntityType:
action = probo.ActionAccessReviewCampaignGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
scope := coredata.NewScopeFromObjectID(id)
campaign, err := r.accessReview.Campaigns(scope).Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewAccessReviewCampaign(campaign), nil
}
case coredata.AccessSourceEntityType:
action = probo.ActionAccessSourceGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
scope := coredata.NewScopeFromObjectID(id)
source, err := r.accessReview.Sources(scope).Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewAccessSource(source), nil
}
case coredata.AccessEntryEntityType:
action = probo.ActionAccessEntryGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
scope := coredata.NewScopeFromObjectID(id)
entry, err := r.accessReview.Entries(scope).Get(ctx, id)
if err != nil {
return nil, err
}
return types.NewAccessEntry(entry), nil
}
default:
}
if err := r.authorize(ctx, id, action); err != nil {
return nil, err
}
node, err := loadNode(ctx, id)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot load node", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return node, nil
}
// Viewer is the resolver for the viewer field.
func (r *queryResolver) Viewer(ctx context.Context) (*types.Viewer, error) {
identity := authn.IdentityFromContext(ctx)
session := authn.SessionFromContext(ctx)
apiKey := authn.APIKeyFromContext(ctx)
var viewerID gid.GID
if session != nil {
viewerID = session.ID
} else if apiKey != nil {
viewerID = apiKey.ID
} else {
viewerID = identity.ID
}
return &types.Viewer{ID: viewerID}, 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} }
// 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} }
// Viewer returns schema.ViewerResolver implementation.
func (r *Resolver) Viewer() schema.ViewerResolver { return &viewerResolver{r} }
type fileResolver struct{ *Resolver }
type mutationResolver struct{ *Resolver }
type organizationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
type viewerResolver struct{ *Resolver }

View File

@@ -0,0 +1,274 @@
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"
"fmt"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/accessreview/drivers"
"go.probo.inc/probo/pkg/connector"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/page"
"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"
"go.probo.inc/probo/pkg/slack"
)
// Oauth2Scopes is the resolver for the oauth2Scopes field.
func (r *connectorResolver) Oauth2Scopes(ctx context.Context, obj *types.Connector) ([]string, error) {
scopes := drivers.ProviderOAuth2Scopes(obj.Provider)
if scopes == nil {
return []string{}, nil
}
return scopes, nil
}
// CreateAPIKeyConnector is the resolver for the createAPIKeyConnector field.
func (r *mutationResolver) CreateAPIKeyConnector(ctx context.Context, input types.CreateAPIKeyConnectorInput) (*types.CreateAPIKeyConnectorPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionConnectorCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
req := probo.CreateConnectorRequest{
OrganizationID: input.OrganizationID,
Provider: input.Provider,
Protocol: coredata.ConnectorProtocolAPIKey,
Connection: &connector.APIKeyConnection{APIKey: input.APIKey},
}
if input.TallyOrganizationID != nil {
req.TallySettings = &coredata.TallyConnectorSettings{
OrganizationID: *input.TallyOrganizationID,
}
}
if input.SentryOrganizationSlug != nil {
req.SentrySettings = &coredata.SentryConnectorSettings{
OrganizationSlug: *input.SentryOrganizationSlug,
}
}
if input.SupabaseOrganizationSlug != nil {
req.SupabaseSettings = &coredata.SupabaseConnectorSettings{
OrganizationSlug: *input.SupabaseOrganizationSlug,
}
}
if input.GithubOrganization != nil {
req.GitHubSettings = &coredata.GitHubConnectorSettings{
Organization: *input.GithubOrganization,
}
}
if input.OnePasswordScimBridgeURL != nil {
req.OnePasswordSettings = &coredata.OnePasswordConnectorSettings{
SCIMBridgeURL: *input.OnePasswordScimBridgeURL,
}
}
cnnctr, err := prb.Connectors.Create(ctx, req)
if err != nil {
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
panic(fmt.Errorf("cannot create API key connector: %w", err))
}
return &types.CreateAPIKeyConnectorPayload{
Connector: types.NewConnector(cnnctr),
}, nil
}
// CreateClientCredentialsConnector is the resolver for the createClientCredentialsConnector field.
func (r *mutationResolver) CreateClientCredentialsConnector(ctx context.Context, input types.CreateClientCredentialsConnectorInput) (*types.CreateClientCredentialsConnectorPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionConnectorCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
oauth2Conn := &connector.OAuth2Connection{
GrantType: connector.OAuth2GrantTypeClientCredentials,
ClientID: input.ClientID,
ClientSecret: input.ClientSecret,
TokenURL: input.TokenURL,
}
if input.Scope != nil {
oauth2Conn.Scope = *input.Scope
}
req := probo.CreateConnectorRequest{
OrganizationID: input.OrganizationID,
Provider: input.Provider,
Protocol: coredata.ConnectorProtocolOAuth2,
Connection: oauth2Conn,
}
if input.OnePasswordAccountID != nil && input.OnePasswordRegion != nil {
req.OnePasswordUsersAPISettings = &coredata.OnePasswordUsersAPISettings{
AccountID: *input.OnePasswordAccountID,
Region: *input.OnePasswordRegion,
}
}
cnnctr, err := prb.Connectors.Create(ctx, req)
if err != nil {
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
panic(fmt.Errorf("cannot create client credentials connector: %w", err))
}
return &types.CreateClientCredentialsConnectorPayload{
Connector: types.NewConnector(cnnctr),
}, nil
}
// DeleteConnector is the resolver for the deleteConnector field.
func (r *mutationResolver) DeleteConnector(ctx context.Context, input types.DeleteConnectorInput) (*types.DeleteConnectorPayload, error) {
if err := r.authorize(ctx, input.ConnectorID, probo.ActionConnectorDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ConnectorID.TenantID())
if err := prb.Connectors.Delete(ctx, input.ConnectorID); err != nil {
panic(fmt.Errorf("cannot delete connector: %w", err))
}
return &types.DeleteConnectorPayload{
DeletedConnectorID: input.ConnectorID,
}, nil
}
// DeleteSlackConnection is the resolver for the deleteSlackConnection field.
func (r *mutationResolver) DeleteSlackConnection(ctx context.Context, input types.DeleteSlackConnectionInput) (*types.DeleteSlackConnectionPayload, error) {
if err := r.authorize(ctx, input.SlackConnectionID, probo.ActionConnectorDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.SlackConnectionID.TenantID())
err := prb.Connectors.Delete(ctx, input.SlackConnectionID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete slack connection", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteSlackConnectionPayload{
DeletedSlackConnectionID: input.SlackConnectionID,
}, nil
}
// SlackConnections is the resolver for the slackConnections field.
func (r *organizationResolver) SlackConnections(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.SlackConnectionConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionSlackConnectionList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
slackProvider := coredata.ConnectorProviderSlack
filter := coredata.NewConnectorProviderFilter(&slackProvider)
pageOrderBy := page.OrderBy[coredata.ConnectorOrderField]{
Field: coredata.ConnectorOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.Connectors.ListForOrganizationID(ctx, obj.ID, cursor, filter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization slack connections", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSlackConnectionConnection(page), nil
}
// SlackOAuth2Scopes is the resolver for the slackOAuth2Scopes field.
func (r *organizationResolver) SlackOAuth2Scopes(ctx context.Context, obj *types.Organization) ([]string, error) {
return slack.OAuth2Scopes, nil
}
// Connectors is the resolver for the connectors field.
func (r *organizationResolver) Connectors(ctx context.Context, obj *types.Organization, filter *types.ConnectorFilter) ([]*types.Connector, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionConnectorList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
connectors, err := prb.Connectors.ListAllForOrganizationID(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot list organization connectors: %w", err))
}
if filter != nil && len(filter.Providers) > 0 {
allowed := make(map[coredata.ConnectorProvider]struct{}, len(filter.Providers))
for _, provider := range filter.Providers {
allowed[provider] = struct{}{}
}
filtered := make(coredata.Connectors, 0, len(connectors))
for _, cnnctr := range connectors {
if _, ok := allowed[cnnctr.Provider]; ok {
filtered = append(filtered, cnnctr)
}
}
connectors = filtered
}
return types.NewConnectors(connectors), nil
}
// ConnectorProviderInfos is the resolver for the connectorProviderInfos field.
func (r *organizationResolver) ConnectorProviderInfos(ctx context.Context, obj *types.Organization) ([]*types.ConnectorProviderInfo, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionConnectorList); err != nil {
return nil, err
}
var infos []*types.ConnectorProviderInfo
for _, provider := range coredata.ConnectorProviders() {
_, oauthErr := r.connectorRegistry.Get(string(provider))
scopes := drivers.ProviderOAuth2Scopes(provider)
if scopes == nil {
scopes = []string{}
}
info := &types.ConnectorProviderInfo{
Provider: provider,
DisplayName: providerDisplayName(provider),
OauthConfigured: oauthErr == nil,
APIKeySupported: providerSupportsAPIKey(provider),
ClientCredentialsSupported: providerSupportsClientCredentials(provider),
Oauth2Scopes: scopes,
ExtraSettings: providerExtraSettings(provider),
}
infos = append(infos, info)
}
return infos, nil
}
// Permission is the resolver for the permission field.
func (r *slackConnectionResolver) Permission(ctx context.Context, obj *types.SlackConnection, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// Connector returns schema.ConnectorResolver implementation.
func (r *Resolver) Connector() schema.ConnectorResolver { return &connectorResolver{r} }
// SlackConnection returns schema.SlackConnectionResolver implementation.
func (r *Resolver) SlackConnection() schema.SlackConnectionResolver {
return &slackConnectionResolver{r}
}
type connectorResolver struct{ *Resolver }
type slackConnectionResolver struct{ *Resolver }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,489 @@
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"
"encoding/base64"
"errors"
"fmt"
"github.com/vikstrous/dataloadgen"
"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/console/v1/dataloader"
"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"
"go.probo.inc/probo/pkg/validator"
)
// ProcessingActivity is the resolver for the processingActivity field.
func (r *dataProtectionImpactAssessmentResolver) ProcessingActivity(ctx context.Context, obj *types.DataProtectionImpactAssessment) (*types.ProcessingActivity, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionProcessingActivityList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
dpia, err := prb.DataProtectionImpactAssessments.Get(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get processing activity dpia", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
processingActivity, err := prb.ProcessingActivities.Get(ctx, dpia.ProcessingActivityID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get processing activity", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewProcessingActivity(processingActivity), nil
}
// Organization is the resolver for the organization field.
func (r *dataProtectionImpactAssessmentResolver) Organization(ctx context.Context, obj *types.DataProtectionImpactAssessment) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
dpia, err := prb.DataProtectionImpactAssessments.Get(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get processing activity dpia", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
organization, err := prb.Organizations.Get(ctx, dpia.OrganizationID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Permission is the resolver for the permission field.
func (r *dataProtectionImpactAssessmentResolver) Permission(ctx context.Context, obj *types.DataProtectionImpactAssessment, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *dataProtectionImpactAssessmentConnectionResolver) TotalCount(ctx context.Context, obj *types.DataProtectionImpactAssessmentConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionDataProtectionImpactAssessmentList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *organizationResolver:
count, err := prb.DataProtectionImpactAssessments.CountForOrganizationID(ctx, obj.ParentID, obj.Filter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count organization data protection impact assessments", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver")
return 0, gqlutils.Internal(ctx)
}
// CreateDataProtectionImpactAssessment is the resolver for the createDataProtectionImpactAssessment field.
func (r *mutationResolver) CreateDataProtectionImpactAssessment(ctx context.Context, input types.CreateDataProtectionImpactAssessmentInput) (*types.CreateDataProtectionImpactAssessmentPayload, error) {
if err := r.authorize(ctx, input.ProcessingActivityID, probo.ActionDataProtectionImpactAssessmentCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ProcessingActivityID.TenantID())
req := probo.CreateDataProtectionImpactAssessmentRequest{
ProcessingActivityID: input.ProcessingActivityID,
Description: input.Description,
NecessityAndProportionality: input.NecessityAndProportionality,
PotentialRisk: input.PotentialRisk,
Mitigations: input.Mitigations,
ResidualRisk: input.ResidualRisk,
}
dpia, err := prb.DataProtectionImpactAssessments.Create(ctx, &req)
if err != nil {
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create data protection impact assessment", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateDataProtectionImpactAssessmentPayload{
DataProtectionImpactAssessment: types.NewDataProtectionImpactAssessment(dpia),
}, nil
}
// UpdateDataProtectionImpactAssessment is the resolver for the updateDataProtectionImpactAssessment field.
func (r *mutationResolver) UpdateDataProtectionImpactAssessment(ctx context.Context, input types.UpdateDataProtectionImpactAssessmentInput) (*types.UpdateDataProtectionImpactAssessmentPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionDataProtectionImpactAssessmentUpdate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ID.TenantID())
req := probo.UpdateDataProtectionImpactAssessmentRequest{
ID: input.ID,
Description: gqlutils.UnwrapOmittable(input.Description),
NecessityAndProportionality: gqlutils.UnwrapOmittable(input.NecessityAndProportionality),
PotentialRisk: gqlutils.UnwrapOmittable(input.PotentialRisk),
Mitigations: gqlutils.UnwrapOmittable(input.Mitigations),
ResidualRisk: input.ResidualRisk,
}
dpia, err := prb.DataProtectionImpactAssessments.Update(ctx, &req)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update data protection impact assessment", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateDataProtectionImpactAssessmentPayload{
DataProtectionImpactAssessment: types.NewDataProtectionImpactAssessment(dpia),
}, nil
}
// DeleteDataProtectionImpactAssessment is the resolver for the deleteDataProtectionImpactAssessment field.
func (r *mutationResolver) DeleteDataProtectionImpactAssessment(ctx context.Context, input types.DeleteDataProtectionImpactAssessmentInput) (*types.DeleteDataProtectionImpactAssessmentPayload, error) {
if err := r.authorize(ctx, input.DataProtectionImpactAssessmentID, probo.ActionDataProtectionImpactAssessmentDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.DataProtectionImpactAssessmentID.TenantID())
err := prb.DataProtectionImpactAssessments.Delete(ctx, input.DataProtectionImpactAssessmentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete data protection impact assessment", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteDataProtectionImpactAssessmentPayload{
DeletedDataProtectionImpactAssessmentID: input.DataProtectionImpactAssessmentID,
}, nil
}
// CreateTransferImpactAssessment is the resolver for the createTransferImpactAssessment field.
func (r *mutationResolver) CreateTransferImpactAssessment(ctx context.Context, input types.CreateTransferImpactAssessmentInput) (*types.CreateTransferImpactAssessmentPayload, error) {
if err := r.authorize(ctx, input.ProcessingActivityID, probo.ActionTransferImpactAssessmentCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ProcessingActivityID.TenantID())
req := probo.CreateTransferImpactAssessmentRequest{
ProcessingActivityID: input.ProcessingActivityID,
DataSubjects: input.DataSubjects,
LegalMechanism: input.LegalMechanism,
Transfer: input.Transfer,
LocalLawRisk: input.LocalLawRisk,
SupplementaryMeasures: input.SupplementaryMeasures,
}
tia, err := prb.TransferImpactAssessments.Create(ctx, &req)
if err != nil {
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create transfer impact assessment", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateTransferImpactAssessmentPayload{
TransferImpactAssessment: types.NewTransferImpactAssessment(tia),
}, nil
}
// UpdateTransferImpactAssessment is the resolver for the updateTransferImpactAssessment field.
func (r *mutationResolver) UpdateTransferImpactAssessment(ctx context.Context, input types.UpdateTransferImpactAssessmentInput) (*types.UpdateTransferImpactAssessmentPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionTransferImpactAssessmentUpdate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ID.TenantID())
req := probo.UpdateTransferImpactAssessmentRequest{
ID: input.ID,
DataSubjects: gqlutils.UnwrapOmittable(input.DataSubjects),
LegalMechanism: gqlutils.UnwrapOmittable(input.LegalMechanism),
Transfer: gqlutils.UnwrapOmittable(input.Transfer),
LocalLawRisk: gqlutils.UnwrapOmittable(input.LocalLawRisk),
SupplementaryMeasures: gqlutils.UnwrapOmittable(input.SupplementaryMeasures),
}
tia, err := prb.TransferImpactAssessments.Update(ctx, &req)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update transfer impact assessment", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateTransferImpactAssessmentPayload{
TransferImpactAssessment: types.NewTransferImpactAssessment(tia),
}, nil
}
// DeleteTransferImpactAssessment is the resolver for the deleteTransferImpactAssessment field.
func (r *mutationResolver) DeleteTransferImpactAssessment(ctx context.Context, input types.DeleteTransferImpactAssessmentInput) (*types.DeleteTransferImpactAssessmentPayload, error) {
if err := r.authorize(ctx, input.TransferImpactAssessmentID, probo.ActionTransferImpactAssessmentDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.TransferImpactAssessmentID.TenantID())
err := prb.TransferImpactAssessments.Delete(ctx, input.TransferImpactAssessmentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete transfer impact assessment", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteTransferImpactAssessmentPayload{
DeletedTransferImpactAssessmentID: input.TransferImpactAssessmentID,
}, nil
}
// ExportDataProtectionImpactAssessmentsPDF is the resolver for the exportDataProtectionImpactAssessmentsPDF field.
func (r *mutationResolver) ExportDataProtectionImpactAssessmentsPDF(ctx context.Context, input types.ExportDataProtectionImpactAssessmentsPDFInput) (*types.ExportDataProtectionImpactAssessmentsPDFPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionDataProtectionImpactAssessmentExport); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
var snapshotIDPtr *gid.GID
if input.Filter != nil {
snapshotIDPtr = input.Filter.SnapshotID
}
dpiaFilter := coredata.NewDataProtectionImpactAssessmentFilter(&snapshotIDPtr)
pdf, err := prb.DataProtectionImpactAssessments.ExportPDF(ctx, input.OrganizationID, dpiaFilter)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot export data protection impact assessments PDF", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportDataProtectionImpactAssessmentsPDFPayload{
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
}, nil
}
// ExportTransferImpactAssessmentsPDF is the resolver for the exportTransferImpactAssessmentsPDF field.
func (r *mutationResolver) ExportTransferImpactAssessmentsPDF(ctx context.Context, input types.ExportTransferImpactAssessmentsPDFInput) (*types.ExportTransferImpactAssessmentsPDFPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionTransferImpactAssessmentExport); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
var snapshotIDPtr *gid.GID
if input.Filter != nil {
snapshotIDPtr = input.Filter.SnapshotID
}
tiaFilter := coredata.NewTransferImpactAssessmentFilter(&snapshotIDPtr)
pdf, err := prb.TransferImpactAssessments.ExportPDF(ctx, input.OrganizationID, tiaFilter)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot export transfer impact assessments PDF", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportTransferImpactAssessmentsPDFPayload{
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
}, nil
}
// DataProtectionImpactAssessments is the resolver for the dataProtectionImpactAssessments field.
func (r *organizationResolver) DataProtectionImpactAssessments(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DataProtectionImpactAssessmentOrderBy, filter *types.DataProtectionImpactAssessmentFilter) (*types.DataProtectionImpactAssessmentConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionDataProtectionImpactAssessmentList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.DataProtectionImpactAssessmentOrderField]{
Field: coredata.DataProtectionImpactAssessmentOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.DataProtectionImpactAssessmentOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
dpiaFilter := coredata.NewDataProtectionImpactAssessmentFilter(nil)
if filter != nil {
dpiaFilter = coredata.NewDataProtectionImpactAssessmentFilter(&filter.SnapshotID)
}
page, err := prb.DataProtectionImpactAssessments.ListForOrganizationID(ctx, obj.ID, cursor, dpiaFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization data protection impact assessments", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewDataProtectionImpactAssessmentConnection(page, r, obj.ID, dpiaFilter), nil
}
// TransferImpactAssessments is the resolver for the transferImpactAssessments field.
func (r *organizationResolver) TransferImpactAssessments(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.TransferImpactAssessmentOrderBy, filter *types.TransferImpactAssessmentFilter) (*types.TransferImpactAssessmentConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionTransferImpactAssessmentList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.TransferImpactAssessmentOrderField]{
Field: coredata.TransferImpactAssessmentOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.TransferImpactAssessmentOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
tiaFilter := coredata.NewTransferImpactAssessmentFilter(nil)
if filter != nil {
tiaFilter = coredata.NewTransferImpactAssessmentFilter(&filter.SnapshotID)
}
page, err := prb.TransferImpactAssessments.ListForOrganizationID(ctx, obj.ID, cursor, tiaFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization transfer impact assessments", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewTransferImpactAssessmentConnection(page, r, obj.ID, tiaFilter), nil
}
// ProcessingActivity is the resolver for the processingActivity field.
func (r *transferImpactAssessmentResolver) ProcessingActivity(ctx context.Context, obj *types.TransferImpactAssessment) (*types.ProcessingActivity, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionProcessingActivityGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
processingActivity, err := prb.ProcessingActivities.Get(ctx, obj.ProcessingActivity.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get processing activity", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewProcessingActivity(processingActivity), nil
}
// Organization is the resolver for the organization field.
func (r *transferImpactAssessmentResolver) Organization(ctx context.Context, obj *types.TransferImpactAssessment) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
organization, err := loaders.Organization.Load(ctx, obj.Organization.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Permission is the resolver for the permission field.
func (r *transferImpactAssessmentResolver) Permission(ctx context.Context, obj *types.TransferImpactAssessment, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *transferImpactAssessmentConnectionResolver) TotalCount(ctx context.Context, obj *types.TransferImpactAssessmentConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionTransferImpactAssessmentList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *organizationResolver:
count, err := prb.TransferImpactAssessments.CountForOrganizationID(ctx, obj.ParentID, obj.Filter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count organization transfer impact assessments", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver")
return 0, gqlutils.Internal(ctx)
}
// DataProtectionImpactAssessment returns schema.DataProtectionImpactAssessmentResolver implementation.
func (r *Resolver) DataProtectionImpactAssessment() schema.DataProtectionImpactAssessmentResolver {
return &dataProtectionImpactAssessmentResolver{r}
}
// DataProtectionImpactAssessmentConnection returns schema.DataProtectionImpactAssessmentConnectionResolver implementation.
func (r *Resolver) DataProtectionImpactAssessmentConnection() schema.DataProtectionImpactAssessmentConnectionResolver {
return &dataProtectionImpactAssessmentConnectionResolver{r}
}
// TransferImpactAssessment returns schema.TransferImpactAssessmentResolver implementation.
func (r *Resolver) TransferImpactAssessment() schema.TransferImpactAssessmentResolver {
return &transferImpactAssessmentResolver{r}
}
// TransferImpactAssessmentConnection returns schema.TransferImpactAssessmentConnectionResolver implementation.
func (r *Resolver) TransferImpactAssessmentConnection() schema.TransferImpactAssessmentConnectionResolver {
return &transferImpactAssessmentConnectionResolver{r}
}
type dataProtectionImpactAssessmentResolver struct{ *Resolver }
type dataProtectionImpactAssessmentConnectionResolver struct{ *Resolver }
type transferImpactAssessmentResolver struct{ *Resolver }
type transferImpactAssessmentConnectionResolver struct{ *Resolver }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
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"
"fmt"
"time"
"go.probo.inc/probo/pkg/server/api/console/v1/schema"
"go.probo.inc/probo/pkg/server/api/console/v1/types"
)
// CertificateFileURL is the resolver for the certificateFileUrl field.
func (r *electronicSignatureResolver) CertificateFileURL(ctx context.Context, obj *types.ElectronicSignature) (*string, error) {
signature, err := r.esign.GetSignatureByID(ctx, obj.ID)
if err != nil {
return nil, fmt.Errorf("cannot load signature: %w", err)
}
if signature.CertificateFileID == nil {
return nil, nil
}
url, err := r.esign.GenerateCertificateFileURL(ctx, *signature.CertificateFileID, 1*time.Hour)
if err != nil {
return nil, fmt.Errorf("cannot generate certificate file URL: %w", err)
}
return &url, nil
}
// Events is the resolver for the events field.
func (r *electronicSignatureResolver) Events(ctx context.Context, obj *types.ElectronicSignature) ([]*types.ElectronicSignatureEvent, error) {
events, err := r.esign.GetEventsBySignatureID(ctx, obj.ID)
if err != nil {
return nil, fmt.Errorf("cannot load signature events: %w", err)
}
result := make([]*types.ElectronicSignatureEvent, len(events))
for i := range events {
result[i] = types.NewElectronicSignatureEvent(events[i])
}
return result, nil
}
// ElectronicSignature returns schema.ElectronicSignatureResolver implementation.
func (r *Resolver) ElectronicSignature() schema.ElectronicSignatureResolver {
return &electronicSignatureResolver{r}
}
type electronicSignatureResolver struct{ *Resolver }

View File

@@ -0,0 +1,197 @@
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"
"fmt"
"github.com/vikstrous/dataloadgen"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/probo"
"go.probo.inc/probo/pkg/server/api/console/v1/dataloader"
"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"
"go.probo.inc/probo/pkg/validator"
)
// File is the resolver for the file field.
func (r *evidenceResolver) File(ctx context.Context, obj *types.Evidence) (*types.File, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionFileGet); err != nil {
return nil, err
}
if obj.File == nil {
return nil, nil
}
loaders := dataloader.FromContext(ctx)
file, err := loaders.File.Load(ctx, obj.File.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot load evidence file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewFile(file), nil
}
// Task is the resolver for the task field.
func (r *evidenceResolver) Task(ctx context.Context, obj *types.Evidence) (*types.Task, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionTaskGet); err != nil {
return nil, err
}
if obj.Task == nil {
r.logger.ErrorCtx(ctx, "evidence is not associated with a task")
return nil, gqlutils.Internal(ctx)
}
loaders := dataloader.FromContext(ctx)
task, err := loaders.Task.Load(ctx, obj.Task.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot load task", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewTask(task), nil
}
// Measure is the resolver for the measure field.
func (r *evidenceResolver) Measure(ctx context.Context, obj *types.Evidence) (*types.Measure, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionMeasureGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
measure, err := loaders.Measure.Load(ctx, obj.Measure.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot load measure", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMeasure(measure), nil
}
// Permission is the resolver for the permission field.
func (r *evidenceResolver) Permission(ctx context.Context, obj *types.Evidence, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *evidenceConnectionResolver) TotalCount(ctx context.Context, obj *types.EvidenceConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionEvidenceList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *measureResolver:
count, err := prb.Evidences.CountForMeasureID(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count measure evidence", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
case *taskResolver:
count, err := prb.Evidences.CountForTaskID(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count task evidence", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver")
return 0, gqlutils.Internal(ctx)
}
// DeleteEvidence is the resolver for the deleteEvidence field.
func (r *mutationResolver) DeleteEvidence(ctx context.Context, input types.DeleteEvidenceInput) (*types.DeleteEvidencePayload, error) {
if err := r.authorize(ctx, input.EvidenceID, probo.ActionEvidenceDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.EvidenceID.TenantID())
err := prb.Evidences.Delete(ctx, input.EvidenceID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete evidence", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteEvidencePayload{
DeletedEvidenceID: input.EvidenceID,
}, nil
}
// UploadMeasureEvidence is the resolver for the uploadMeasureEvidence field.
func (r *mutationResolver) UploadMeasureEvidence(ctx context.Context, input types.UploadMeasureEvidenceInput) (*types.UploadMeasureEvidencePayload, error) {
if err := r.authorize(ctx, input.MeasureID, probo.ActionMeasureEvidenceUpload); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.MeasureID.TenantID())
evidence, err := prb.Evidences.UploadMeasureEvidence(
ctx,
probo.UploadMeasureEvidenceRequest{
MeasureID: input.MeasureID,
File: probo.FileUpload{
Content: input.File.File,
Filename: input.File.Filename,
Size: input.File.Size,
ContentType: input.File.ContentType,
},
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot upload measure evidence", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UploadMeasureEvidencePayload{
EvidenceEdge: types.NewEvidenceEdge(evidence, coredata.EvidenceOrderFieldCreatedAt),
}, nil
}
// Evidences is the resolver for the evidences field.
func (r *organizationResolver) Evidences(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.EvidenceOrderBy) (*types.EvidenceConnection, error) {
panic(fmt.Errorf("not implemented: Evidences - evidences"))
}
// Evidence returns schema.EvidenceResolver implementation.
func (r *Resolver) Evidence() schema.EvidenceResolver { return &evidenceResolver{r} }
// EvidenceConnection returns schema.EvidenceConnectionResolver implementation.
func (r *Resolver) EvidenceConnection() schema.EvidenceConnectionResolver {
return &evidenceConnectionResolver{r}
}
type evidenceResolver struct{ *Resolver }
type evidenceConnectionResolver struct{ *Resolver }

View File

@@ -0,0 +1,301 @@
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"
"encoding/json"
"errors"
"time"
"github.com/vikstrous/dataloadgen"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/coredata"
"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/dataloader"
"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"
"go.probo.inc/probo/pkg/validator"
)
// Organization is the resolver for the organization field.
func (r *frameworkResolver) Organization(ctx context.Context, obj *types.Framework) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
organization, err := loaders.Organization.Load(ctx, obj.Organization.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot load organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Controls is the resolver for the controls field.
func (r *frameworkResolver) Controls(ctx context.Context, obj *types.Framework, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionControlList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ControlOrderField]{
Field: coredata.ControlOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.ControlOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
var controlFilter = coredata.NewControlFilter(nil)
if filter != nil {
controlFilter = coredata.NewControlFilter(filter.Query)
}
page, err := prb.Controls.ListForFrameworkID(ctx, obj.ID, cursor, controlFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list controls", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewControlConnection(page, r, obj.ID, controlFilter), nil
}
// LightLogoURL is the resolver for the lightLogoURL field.
func (r *frameworkResolver) LightLogoURL(ctx context.Context, obj *types.Framework) (*string, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionFrameworkGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
return prb.Frameworks.GenerateLightLogoURL(ctx, obj.ID, 1*time.Hour)
}
// DarkLogoURL is the resolver for the darkLogoURL field.
func (r *frameworkResolver) DarkLogoURL(ctx context.Context, obj *types.Framework) (*string, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionFrameworkGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
return prb.Frameworks.GenerateDarkLogoURL(ctx, obj.ID, 1*time.Hour)
}
// Permission is the resolver for the permission field.
func (r *frameworkResolver) Permission(ctx context.Context, obj *types.Framework, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *frameworkConnectionResolver) TotalCount(ctx context.Context, obj *types.FrameworkConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionFrameworkList); err != nil {
return 0, err
}
switch obj.Resolver.(type) {
case *organizationResolver:
prb := r.ProboService(ctx, obj.ParentID.TenantID())
count, err := prb.Frameworks.CountForOrganizationID(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count frameworks", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver")
return 0, gqlutils.Internal(ctx)
}
// CreateFramework is the resolver for the createFramework field.
func (r *mutationResolver) CreateFramework(ctx context.Context, input types.CreateFrameworkInput) (*types.CreateFrameworkPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionFrameworkCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
framework, err := prb.Frameworks.Create(
ctx,
probo.CreateFrameworkRequest{
OrganizationID: input.OrganizationID,
Name: input.Name,
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateFrameworkPayload{
FrameworkEdge: types.NewFrameworkEdge(framework, coredata.FrameworkOrderFieldCreatedAt),
}, nil
}
// UpdateFramework is the resolver for the updateFramework field.
func (r *mutationResolver) UpdateFramework(ctx context.Context, input types.UpdateFrameworkInput) (*types.UpdateFrameworkPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionFrameworkUpdate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ID.TenantID())
framework, err := prb.Frameworks.Update(
ctx,
probo.UpdateFrameworkRequest{
ID: input.ID,
Name: input.Name,
Description: gqlutils.UnwrapOmittable(input.Description),
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateFrameworkPayload{
Framework: types.NewFramework(framework),
}, nil
}
// ImportFramework is the resolver for the importFramework field.
func (r *mutationResolver) ImportFramework(ctx context.Context, input types.ImportFrameworkInput) (*types.ImportFrameworkPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionFrameworkImport); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
req := probo.ImportFrameworkRequest{}
if err := json.NewDecoder(input.File.File).Decode(&req.Framework); err != nil {
r.logger.ErrorCtx(ctx, "cannot decode framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
framework, err := prb.Frameworks.Import(ctx, input.OrganizationID, req)
if err != nil {
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot import framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ImportFrameworkPayload{
FrameworkEdge: types.NewFrameworkEdge(framework, coredata.FrameworkOrderFieldCreatedAt),
}, nil
}
// DeleteFramework is the resolver for the deleteFramework field.
func (r *mutationResolver) DeleteFramework(ctx context.Context, input types.DeleteFrameworkInput) (*types.DeleteFrameworkPayload, error) {
if err := r.authorize(ctx, input.FrameworkID, probo.ActionFrameworkDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.FrameworkID.TenantID())
err := prb.Frameworks.Delete(ctx, input.FrameworkID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteFrameworkPayload{
DeletedFrameworkID: input.FrameworkID,
}, nil
}
// ExportFramework is the resolver for the exportFramework field.
func (r *mutationResolver) ExportFramework(ctx context.Context, input types.ExportFrameworkInput) (*types.ExportFrameworkPayload, error) {
if err := r.authorize(ctx, input.FrameworkID, probo.ActionFrameworkExport); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.FrameworkID.TenantID())
identity := authn.IdentityFromContext(ctx)
exportJob, exportErr := prb.Frameworks.RequestExport(
ctx,
input.FrameworkID,
identity.EmailAddress,
identity.FullName,
)
if exportErr != nil {
r.logger.ErrorCtx(ctx, "cannot export framework", log.Error(exportErr))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportFrameworkPayload{
ExportJobID: exportJob.ID,
}, nil
}
// Frameworks is the resolver for the frameworks field.
func (r *organizationResolver) Frameworks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.FrameworkOrderBy) (*types.FrameworkConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionFrameworkList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.FrameworkOrderField]{
Field: coredata.FrameworkOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.FrameworkOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.Frameworks.ListForOrganizationID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization frameworks", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewFrameworkConnection(page, r, obj.ID), nil
}
// Framework returns schema.FrameworkResolver implementation.
func (r *Resolver) Framework() schema.FrameworkResolver { return &frameworkResolver{r} }
// FrameworkConnection returns schema.FrameworkConnectionResolver implementation.
func (r *Resolver) FrameworkConnection() schema.FrameworkConnectionResolver {
return &frameworkConnectionResolver{r}
}
type frameworkResolver struct{ *Resolver }
type frameworkConnectionResolver struct{ *Resolver }

View File

@@ -1,4 +1,5 @@
schema: ["schema.graphql"]
schema:
- "graphql/*.graphql"
exec:
filename: "schema/schema.go"
@@ -12,7 +13,7 @@ resolver:
layout: "follow-schema"
dir: "."
package: "console_v1"
filename_template: "v1_resolver.go"
filename_template: "{name}.resolvers.go"
autobind: []
call_argument_directives_with_null: true

View File

@@ -0,0 +1,709 @@
extend type Organization {
accessSources(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: AccessSourceOrder
): AccessSourceConnection! @goField(forceResolver: true)
accessReviewCampaigns(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: AccessReviewCampaignOrder
): AccessReviewCampaignConnection! @goField(forceResolver: true)
}
enum AccessReviewCampaignStatus
@goModel(
model: "go.probo.inc/probo/pkg/coredata.AccessReviewCampaignStatus"
) {
DRAFT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessReviewCampaignStatusDraft"
)
IN_PROGRESS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessReviewCampaignStatusInProgress"
)
PENDING_ACTIONS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessReviewCampaignStatusPendingActions"
)
FAILED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessReviewCampaignStatusFailed"
)
COMPLETED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessReviewCampaignStatusCompleted"
)
CANCELLED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessReviewCampaignStatusCancelled"
)
}
enum AccessSourceCategory
@goModel(
model: "go.probo.inc/probo/pkg/coredata.AccessSourceCategory"
) {
SAAS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessSourceCategorySaaS"
)
CLOUD_INFRA
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessSourceCategoryCloudInfra"
)
SOURCE_CODE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessSourceCategorySourceCode"
)
OTHER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessSourceCategoryOther"
)
}
enum AccessReviewCampaignSourceFetchStatus
@goModel(
model: "go.probo.inc/probo/pkg/coredata.AccessReviewCampaignSourceFetchStatus"
) {
QUEUED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessReviewCampaignSourceFetchStatusQueued"
)
FETCHING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessReviewCampaignSourceFetchStatusFetching"
)
SUCCESS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessReviewCampaignSourceFetchStatusSuccess"
)
FAILED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessReviewCampaignSourceFetchStatusFailed"
)
}
enum AccessEntryFlag
@goModel(model: "go.probo.inc/probo/pkg/coredata.AccessEntryFlag") {
NONE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryFlagNone"
)
ORPHANED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryFlagOrphaned"
)
INACTIVE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryFlagInactive"
)
EXCESSIVE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryFlagExcessive"
)
ROLE_MISMATCH
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryFlagRoleMismatch"
)
NEW
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryFlagNew"
)
DORMANT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryFlagDormant"
)
TERMINATED_USER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryFlagTerminatedUser"
)
CONTRACTOR_EXPIRED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryFlagContractorExpired"
)
SOD_CONFLICT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryFlagSoDConflict"
)
PRIVILEGED_ACCESS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryFlagPrivilegedAccess"
)
ROLE_CREEP
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryFlagRoleCreep"
)
NO_BUSINESS_JUSTIFICATION
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryFlagNoBusinessJustification"
)
OUT_OF_DEPARTMENT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryFlagOutOfDepartment"
)
SHARED_ACCOUNT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryFlagSharedAccount"
)
}
enum AccessEntryDecision
@goModel(
model: "go.probo.inc/probo/pkg/coredata.AccessEntryDecision"
) {
PENDING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryDecisionPending"
)
APPROVED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryDecisionApproved"
)
REVOKE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryDecisionRevoke"
)
DEFER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryDecisionDefer"
)
ESCALATE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryDecisionEscalate"
)
}
enum AccessEntryIncrementalTag
@goModel(model: "go.probo.inc/probo/pkg/coredata.AccessEntryIncrementalTag") {
NEW
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryIncrementalTagNew"
)
REMOVED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryIncrementalTagRemoved"
)
UNCHANGED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryIncrementalTagUnchanged"
)
}
enum MfaStatus
@goModel(model: "go.probo.inc/probo/pkg/coredata.MFAStatus") {
ENABLED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MFAStatusEnabled"
)
DISABLED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MFAStatusDisabled"
)
UNKNOWN
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MFAStatusUnknown"
)
}
enum AccessEntryAuthMethod
@goModel(
model: "go.probo.inc/probo/pkg/coredata.AccessEntryAuthMethod"
) {
SSO
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryAuthMethodSSO"
)
PASSWORD
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryAuthMethodPassword"
)
API_KEY
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryAuthMethodAPIKey"
)
SERVICE_ACCOUNT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryAuthMethodServiceAccount"
)
UNKNOWN
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryAuthMethodUnknown"
)
}
enum AccessEntryAccountType
@goModel(
model: "go.probo.inc/probo/pkg/coredata.AccessEntryAccountType"
) {
USER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryAccountTypeUser"
)
SERVICE_ACCOUNT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AccessEntryAccountTypeServiceAccount"
)
}
enum AccessSourceOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.AccessSourceOrderField"
) {
CREATED_AT
}
enum AccessReviewCampaignOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.AccessReviewCampaignOrderField"
) {
CREATED_AT
}
enum AccessEntryOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.AccessEntryOrderField"
) {
CREATED_AT
}
enum AccessSourceConnectionStatus {
CONNECTED
DISCONNECTED
NOT_APPLICABLE
}
input AccessSourceOrder {
direction: OrderDirection!
field: AccessSourceOrderField!
}
input AccessReviewCampaignOrder {
direction: OrderDirection!
field: AccessReviewCampaignOrderField!
}
input AccessEntryOrder {
direction: OrderDirection!
field: AccessEntryOrderField!
}
input AccessEntryFilter
@goModel(
model: "go.probo.inc/probo/pkg/coredata.AccessEntryFilter"
) {
decision: AccessEntryDecision
flag: AccessEntryFlag
incrementalTag: AccessEntryIncrementalTag
isAdmin: Boolean
authMethod: AccessEntryAuthMethod
accountType: AccessEntryAccountType
}
type AccessReview implements Node {
id: ID!
organization: Organization! @goField(forceResolver: true)
identitySource: AccessSource @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
accessSources(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: AccessSourceOrder
): AccessSourceConnection! @goField(forceResolver: true)
campaigns(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: AccessReviewCampaignOrder
): AccessReviewCampaignConnection! @goField(forceResolver: true)
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type ProviderOrganization {
slug: String!
displayName: String!
}
type AccessSource implements Node {
id: ID!
organization: Organization! @goField(forceResolver: true)
connectorId: ID
connector: Connector @goField(forceResolver: true)
name: String!
csvData: String
providerOrganizations: [ProviderOrganization!]! @goField(forceResolver: true)
needsConfiguration: Boolean! @goField(forceResolver: true)
connectionStatus: AccessSourceConnectionStatus! @goField(forceResolver: true)
selectedOrganization: String @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type AccessReviewCampaignScopeSource
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.AccessReviewCampaignScopeSource"
) {
id: ID!
source: AccessSource!
name: String!
fetchStatus: AccessReviewCampaignSourceFetchStatus!
fetchedAccountsCount: Int!
attemptCount: Int!
lastError: String
fetchStartedAt: Datetime
fetchCompletedAt: Datetime
entries(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: AccessEntryOrder
filter: AccessEntryFilter
): AccessEntryConnection! @goField(forceResolver: true)
statistics: AccessReviewCampaignStatistics! @goField(forceResolver: true)
}
type AccessReviewCampaign implements Node {
id: ID!
organization: Organization! @goField(forceResolver: true)
name: String!
description: String!
status: AccessReviewCampaignStatus!
startedAt: Datetime
completedAt: Datetime
frameworkControls: [String!]
createdAt: Datetime!
updatedAt: Datetime!
scopeSources: [AccessReviewCampaignScopeSource!]! @goField(forceResolver: true)
entries(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: AccessEntryOrder
accessSourceId: ID
filter: AccessEntryFilter
): AccessEntryConnection! @goField(forceResolver: true)
pendingEntryCount: Int! @goField(forceResolver: true)
statistics: AccessReviewCampaignStatistics! @goField(forceResolver: true)
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type AccessEntry implements Node {
id: ID!
campaign: AccessReviewCampaign! @goField(forceResolver: true)
accessSource: AccessSource! @goField(forceResolver: true)
email: String!
fullName: String!
role: String!
jobTitle: String!
isAdmin: Boolean!
mfaStatus: MfaStatus!
authMethod: AccessEntryAuthMethod!
accountType: AccessEntryAccountType!
lastLogin: Datetime
accountCreatedAt: Datetime
externalId: String!
incrementalTag: AccessEntryIncrementalTag!
flags: [AccessEntryFlag!]!
flagReasons: [String!]!
decision: AccessEntryDecision!
decisionNote: String
decidedBy: ID
decidedAt: Datetime
decisionHistory: [AccessEntryDecisionHistoryEntry!]!
@goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type AccessEntryDecisionHistoryEntry {
id: ID!
decision: AccessEntryDecision!
decisionNote: String
decidedBy: ID
decidedAt: Datetime!
createdAt: Datetime!
}
type AccessSourceConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.AccessSourceConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [AccessSourceEdge!]!
pageInfo: PageInfo!
}
type AccessSourceEdge {
cursor: CursorKey!
node: AccessSource!
}
type AccessReviewCampaignConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.AccessReviewCampaignConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [AccessReviewCampaignEdge!]!
pageInfo: PageInfo!
}
type AccessReviewCampaignEdge {
cursor: CursorKey!
node: AccessReviewCampaign!
}
type AccessEntryConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.AccessEntryConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [AccessEntryEdge!]!
pageInfo: PageInfo!
}
type AccessEntryEdge {
cursor: CursorKey!
node: AccessEntry!
}
type AccessReviewCampaignStatistics {
totalCount: Int!
decisionCounts: [AccessEntryDecisionCount!]!
flagCounts: [AccessEntryFlagCount!]!
incrementalTagCounts: [AccessEntryIncrementalTagCount!]!
}
type AccessEntryDecisionCount {
decision: AccessEntryDecision!
count: Int!
}
type AccessEntryFlagCount {
flag: AccessEntryFlag!
count: Int!
}
type AccessEntryIncrementalTagCount {
incrementalTag: AccessEntryIncrementalTag!
count: Int!
}
extend type Mutation {
createAccessSource(
input: CreateAccessSourceInput!
): CreateAccessSourcePayload!
updateAccessSource(
input: UpdateAccessSourceInput!
): UpdateAccessSourcePayload!
deleteAccessSource(
input: DeleteAccessSourceInput!
): DeleteAccessSourcePayload!
configureAccessSource(
input: ConfigureAccessSourceInput!
): ConfigureAccessSourcePayload!
createAccessReviewCampaign(
input: CreateAccessReviewCampaignInput!
): CreateAccessReviewCampaignPayload!
updateAccessReviewCampaign(
input: UpdateAccessReviewCampaignInput!
): UpdateAccessReviewCampaignPayload!
deleteAccessReviewCampaign(
input: DeleteAccessReviewCampaignInput!
): DeleteAccessReviewCampaignPayload!
startAccessReviewCampaign(
input: StartAccessReviewCampaignInput!
): StartAccessReviewCampaignPayload!
closeAccessReviewCampaign(
input: CloseAccessReviewCampaignInput!
): CloseAccessReviewCampaignPayload!
cancelAccessReviewCampaign(
input: CancelAccessReviewCampaignInput!
): CancelAccessReviewCampaignPayload!
addAccessReviewCampaignScopeSource(
input: AddAccessReviewCampaignScopeSourceInput!
): AddAccessReviewCampaignScopeSourcePayload!
removeAccessReviewCampaignScopeSource(
input: RemoveAccessReviewCampaignScopeSourceInput!
): RemoveAccessReviewCampaignScopeSourcePayload!
recordAccessEntryDecision(
input: RecordAccessEntryDecisionInput!
): RecordAccessEntryDecisionPayload!
recordAccessEntryDecisions(
input: RecordAccessEntryDecisionsInput!
): RecordAccessEntryDecisionsPayload!
flagAccessEntry(
input: FlagAccessEntryInput!
): FlagAccessEntryPayload!
}
input CreateAccessSourceInput {
organizationId: ID!
connectorId: ID
name: String!
csvData: String
}
type CreateAccessSourcePayload {
accessSourceEdge: AccessSourceEdge!
}
input UpdateAccessSourceInput {
accessSourceId: ID!
name: String @goField(omittable: true)
connectorId: ID @goField(omittable: true)
csvData: String @goField(omittable: true)
}
type UpdateAccessSourcePayload {
accessSource: AccessSource!
}
input DeleteAccessSourceInput {
accessSourceId: ID!
}
type DeleteAccessSourcePayload {
deletedAccessSourceId: ID!
}
input ConfigureAccessSourceInput {
accessSourceId: ID!
organizationSlug: String!
}
type ConfigureAccessSourcePayload {
accessSource: AccessSource!
}
input CreateAccessReviewCampaignInput {
organizationId: ID!
name: String!
description: String
frameworkControls: [String!]
accessSourceIds: [ID!]
}
type CreateAccessReviewCampaignPayload {
accessReviewCampaignEdge: AccessReviewCampaignEdge!
}
input UpdateAccessReviewCampaignInput {
accessReviewCampaignId: ID!
name: String @goField(omittable: true)
description: String @goField(omittable: true)
frameworkControls: [String!] @goField(omittable: true)
}
type UpdateAccessReviewCampaignPayload {
accessReviewCampaign: AccessReviewCampaign!
}
input DeleteAccessReviewCampaignInput {
accessReviewCampaignId: ID!
}
type DeleteAccessReviewCampaignPayload {
deletedAccessReviewCampaignId: ID!
}
input StartAccessReviewCampaignInput {
accessReviewCampaignId: ID!
}
type StartAccessReviewCampaignPayload {
accessReviewCampaign: AccessReviewCampaign!
}
input CloseAccessReviewCampaignInput {
accessReviewCampaignId: ID!
}
type CloseAccessReviewCampaignPayload {
accessReviewCampaign: AccessReviewCampaign!
}
input CancelAccessReviewCampaignInput {
accessReviewCampaignId: ID!
}
type CancelAccessReviewCampaignPayload {
accessReviewCampaign: AccessReviewCampaign!
}
input AddAccessReviewCampaignScopeSourceInput {
accessReviewCampaignId: ID!
accessSourceId: ID!
}
type AddAccessReviewCampaignScopeSourcePayload {
accessReviewCampaign: AccessReviewCampaign!
}
input RemoveAccessReviewCampaignScopeSourceInput {
accessReviewCampaignId: ID!
accessSourceId: ID!
}
type RemoveAccessReviewCampaignScopeSourcePayload {
accessReviewCampaign: AccessReviewCampaign!
}
input RecordAccessEntryDecisionInput {
accessEntryId: ID!
decision: AccessEntryDecision!
decisionNote: String
}
type RecordAccessEntryDecisionPayload {
accessEntry: AccessEntry!
}
input RecordAccessEntryDecisionsInput {
decisions: [AccessEntryDecisionInput!]!
}
input AccessEntryDecisionInput {
accessEntryId: ID!
decision: AccessEntryDecision!
decisionNote: String
}
type RecordAccessEntryDecisionsPayload {
accessEntries: [AccessEntry!]!
}
input FlagAccessEntryInput {
accessEntryId: ID!
flags: [AccessEntryFlag!]!
flagReasons: [String!]
}
type FlagAccessEntryPayload {
accessEntry: AccessEntry!
}

View File

@@ -0,0 +1,241 @@
extend type Organization {
assets(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: AssetOrder
filter: AssetFilter = { snapshotId: null }
): AssetConnection! @goField(forceResolver: true)
data(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: DatumOrder
filter: DatumFilter = { snapshotId: null }
): DatumConnection! @goField(forceResolver: true)
}
enum AssetType @goModel(model: "go.probo.inc/probo/pkg/coredata.AssetType") {
PHYSICAL @goEnum(value: "go.probo.inc/probo/pkg/coredata.AssetTypePhysical")
VIRTUAL @goEnum(value: "go.probo.inc/probo/pkg/coredata.AssetTypeVirtual")
}
enum AssetOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.AssetOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AssetOrderFieldCreatedAt"
)
AMOUNT
@goEnum(value: "go.probo.inc/probo/pkg/coredata.AssetOrderFieldAmount")
}
enum DatumOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.DatumOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DatumOrderFieldCreatedAt"
)
NAME @goEnum(value: "go.probo.inc/probo/pkg/coredata.DatumOrderFieldName")
DATA_CLASSIFICATION
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DatumOrderFieldDataClassification"
)
}
enum DataClassification
@goModel(model: "go.probo.inc/probo/pkg/coredata.DataClassification") {
PUBLIC
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DataClassificationPublic"
)
INTERNAL
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DataClassificationInternal"
)
CONFIDENTIAL
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DataClassificationConfidential"
)
SECRET
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DataClassificationSecret"
)
}
input AssetOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.AssetOrderBy"
) {
direction: OrderDirection!
field: AssetOrderField!
}
input DatumOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.DatumOrderBy"
) {
direction: OrderDirection!
field: DatumOrderField!
}
input AssetFilter {
snapshotId: ID
}
input DatumFilter {
snapshotId: ID
}
type Asset implements Node {
id: ID!
snapshotId: ID
name: String!
amount: Int!
owner: Profile! @goField(forceResolver: true)
vendors(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: VendorOrder
): VendorConnection! @goField(forceResolver: true)
assetType: AssetType!
dataTypesStored: String!
organization: Organization! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type Datum implements Node
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.Datum"
) {
id: ID!
snapshotId: ID
name: String!
dataClassification: DataClassification!
owner: Profile! @goField(forceResolver: true)
vendors(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: VendorOrder
): VendorConnection! @goField(forceResolver: true)
organization: Organization! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type AssetConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.AssetConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [AssetEdge!]!
pageInfo: PageInfo!
}
type AssetEdge {
cursor: CursorKey!
node: Asset!
}
type DatumConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.DatumConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [DatumEdge!]!
pageInfo: PageInfo!
}
type DatumEdge {
cursor: CursorKey!
node: Datum!
}
extend type Mutation {
createAsset(input: CreateAssetInput!): CreateAssetPayload!
updateAsset(input: UpdateAssetInput!): UpdateAssetPayload!
deleteAsset(input: DeleteAssetInput!): DeleteAssetPayload!
createDatum(input: CreateDatumInput!): CreateDatumPayload!
updateDatum(input: UpdateDatumInput!): UpdateDatumPayload!
deleteDatum(input: DeleteDatumInput!): DeleteDatumPayload!
}
input CreateAssetInput {
organizationId: ID!
name: String!
amount: Int!
ownerId: ID!
assetType: AssetType!
dataTypesStored: String!
vendorIds: [ID!]
}
input UpdateAssetInput {
id: ID!
name: String
amount: Int
ownerId: ID
assetType: AssetType
dataTypesStored: String
vendorIds: [ID!]
}
input DeleteAssetInput {
assetId: ID!
}
input CreateDatumInput {
organizationId: ID!
name: String!
dataClassification: DataClassification!
ownerId: ID!
vendorIds: [ID!]
}
input UpdateDatumInput {
id: ID!
name: String
dataClassification: DataClassification
ownerId: ID
vendorIds: [ID!]
}
input DeleteDatumInput {
datumId: ID!
}
type CreateAssetPayload {
assetEdge: AssetEdge!
}
type UpdateAssetPayload {
asset: Asset!
}
type DeleteAssetPayload {
deletedAssetId: ID!
}
type CreateDatumPayload {
datumEdge: DatumEdge!
}
type UpdateDatumPayload {
datum: Datum!
}
type DeleteDatumPayload {
deletedDatumId: ID!
}

View File

@@ -0,0 +1,413 @@
extend type Organization {
audits(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: AuditOrder
): AuditConnection! @goField(forceResolver: true)
findings(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: FindingOrder
filter: FindingFilter = { snapshotId: null }
): FindingConnection @goField(forceResolver: true)
}
enum AuditState @goModel(model: "go.probo.inc/probo/pkg/coredata.AuditState") {
NOT_STARTED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.AuditStateNotStarted")
IN_PROGRESS
@goEnum(value: "go.probo.inc/probo/pkg/coredata.AuditStateInProgress")
COMPLETED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.AuditStateCompleted")
REJECTED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.AuditStateRejected")
OUTDATED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.AuditStateOutdated")
}
enum FindingKind
@goModel(model: "go.probo.inc/probo/pkg/coredata.FindingKind") {
MINOR_NONCONFORMITY
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingKindMinorNonconformity"
)
MAJOR_NONCONFORMITY
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingKindMajorNonconformity"
)
OBSERVATION
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingKindObservation"
)
EXCEPTION
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingKindException"
)
}
enum FindingStatus
@goModel(model: "go.probo.inc/probo/pkg/coredata.FindingStatus") {
OPEN
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingStatusOpen"
)
IN_PROGRESS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingStatusInProgress"
)
CLOSED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingStatusClosed"
)
RISK_ACCEPTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingStatusRiskAccepted"
)
MITIGATED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingStatusMitigated"
)
FALSE_POSITIVE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingStatusFalsePositive"
)
}
enum FindingPriority
@goModel(model: "go.probo.inc/probo/pkg/coredata.FindingPriority") {
LOW
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingPriorityLow"
)
MEDIUM
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingPriorityMedium"
)
HIGH
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingPriorityHigh"
)
}
enum AuditOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.AuditOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AuditOrderFieldCreatedAt"
)
VALID_FROM
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AuditOrderFieldValidFrom"
)
VALID_UNTIL
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AuditOrderFieldValidUntil"
)
STATE @goEnum(value: "go.probo.inc/probo/pkg/coredata.AuditOrderFieldState")
}
enum FindingOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.FindingOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingOrderFieldCreatedAt"
)
REFERENCE_ID
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingOrderFieldReferenceId"
)
IDENTIFIED_ON
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingOrderFieldIdentifiedOn"
)
DUE_DATE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingOrderFieldDueDate"
)
STATUS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingOrderFieldStatus"
)
PRIORITY
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingOrderFieldPriority"
)
KIND
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FindingOrderFieldKind"
)
}
input AuditOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.AuditOrderBy"
) {
direction: OrderDirection!
field: AuditOrderField!
}
input FindingOrder {
field: FindingOrderField!
direction: OrderDirection!
}
input FindingFilter {
snapshotId: ID
kind: FindingKind
status: FindingStatus
priority: FindingPriority
ownerId: ID
}
type Audit implements Node {
id: ID!
name: String
organization: Organization @goField(forceResolver: true)
framework: Framework @goField(forceResolver: true)
validFrom: Datetime
validUntil: Datetime
report: Report @goField(forceResolver: true)
reportUrl: String @goField(forceResolver: true)
state: AuditState!
controls(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection @goField(forceResolver: true)
findings(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: FindingOrder
filter: FindingFilter = { snapshotId: null }
): FindingConnection @goField(forceResolver: true)
trustCenterVisibility: TrustCenterVisibility!
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type Finding implements Node {
id: ID!
snapshotId: ID
organization: Organization @goField(forceResolver: true)
kind: FindingKind!
referenceId: String!
description: String
source: String
audits(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: AuditOrder
): AuditConnection @goField(forceResolver: true)
identifiedOn: Datetime
rootCause: String
correctiveAction: String
owner: Profile @goField(forceResolver: true)
dueDate: Datetime
status: FindingStatus!
priority: FindingPriority!
risk: Risk @goField(forceResolver: true)
effectivenessCheck: String
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type Report implements Node {
id: ID!
objectKey: String!
mimeType: String!
filename: String!
size: Int!
downloadUrl: String @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
audit: Audit @goField(forceResolver: true)
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type AuditConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.AuditConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [AuditEdge!]!
pageInfo: PageInfo!
}
type AuditEdge {
cursor: CursorKey!
node: Audit!
}
type FindingConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.FindingConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [FindingEdge!]!
pageInfo: PageInfo!
}
type FindingEdge
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.FindingEdge"
) {
cursor: CursorKey!
node: Finding!
}
extend type Mutation {
createAudit(input: CreateAuditInput!): CreateAuditPayload
updateAudit(input: UpdateAuditInput!): UpdateAuditPayload
deleteAudit(input: DeleteAuditInput!): DeleteAuditPayload
uploadAuditReport(input: UploadAuditReportInput!): UploadAuditReportPayload
deleteAuditReport(input: DeleteAuditReportInput!): DeleteAuditReportPayload
createFinding(input: CreateFindingInput!): CreateFindingPayload
updateFinding(input: UpdateFindingInput!): UpdateFindingPayload
deleteFinding(input: DeleteFindingInput!): DeleteFindingPayload
createFindingAuditMapping(
input: CreateFindingAuditMappingInput!
): CreateFindingAuditMappingPayload
deleteFindingAuditMapping(
input: DeleteFindingAuditMappingInput!
): DeleteFindingAuditMappingPayload
}
input CreateAuditInput {
organizationId: ID!
frameworkId: ID!
name: String
validFrom: Datetime
validUntil: Datetime
state: AuditState
trustCenterVisibility: TrustCenterVisibility
file: Upload
}
input UpdateAuditInput {
id: ID!
name: String @goField(omittable: true)
validFrom: Datetime
validUntil: Datetime
state: AuditState
trustCenterVisibility: TrustCenterVisibility
}
input DeleteAuditInput {
auditId: ID!
}
input UploadAuditReportInput {
auditId: ID!
file: Upload!
}
input DeleteAuditReportInput {
auditId: ID!
}
input CreateFindingInput {
organizationId: ID!
kind: FindingKind!
description: String
source: String
identifiedOn: Datetime
rootCause: String
correctiveAction: String
ownerId: ID
dueDate: Datetime
status: FindingStatus!
priority: FindingPriority!
riskId: ID
effectivenessCheck: String
}
input UpdateFindingInput {
id: ID!
description: String @goField(omittable: true)
source: String @goField(omittable: true)
identifiedOn: Datetime @goField(omittable: true)
rootCause: String @goField(omittable: true)
correctiveAction: String @goField(omittable: true)
ownerId: ID
dueDate: Datetime @goField(omittable: true)
status: FindingStatus
priority: FindingPriority
riskId: ID @goField(omittable: true)
effectivenessCheck: String @goField(omittable: true)
}
input DeleteFindingInput {
findingId: ID!
}
input CreateFindingAuditMappingInput {
findingId: ID!
auditId: ID!
referenceId: String!
}
input DeleteFindingAuditMappingInput {
findingId: ID!
auditId: ID!
}
type CreateAuditPayload {
auditEdge: AuditEdge
}
type UpdateAuditPayload {
audit: Audit
}
type DeleteAuditPayload {
deletedAuditId: ID
}
type UploadAuditReportPayload {
audit: Audit
}
type DeleteAuditReportPayload {
audit: Audit
}
type CreateFindingPayload {
findingEdge: FindingEdge
}
type UpdateFindingPayload {
finding: Finding
}
type DeleteFindingPayload {
deletedFindingId: ID
}
type CreateFindingAuditMappingPayload {
findingEdge: FindingEdge
auditEdge: AuditEdge
}
type DeleteFindingAuditMappingPayload {
deletedFindingId: ID
deletedAuditId: ID
}

View File

@@ -0,0 +1,81 @@
extend type Organization {
auditLogEntries(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: AuditLogEntryOrder
filter: AuditLogEntryFilter
): AuditLogEntryConnection! @goField(forceResolver: true)
}
enum AuditLogActorType
@goModel(
model: "go.probo.inc/probo/pkg/coredata.AuditLogActorType"
) {
USER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AuditLogActorTypeUser"
)
API_KEY
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AuditLogActorTypeAPIKey"
)
SYSTEM
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AuditLogActorTypeSystem"
)
}
enum AuditLogEntryOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.AuditLogEntryOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.AuditLogEntryOrderFieldCreatedAt"
)
}
input AuditLogEntryOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.AuditLogEntryOrderBy"
) {
field: AuditLogEntryOrderField!
direction: OrderDirection!
}
input AuditLogEntryFilter {
action: String
actorId: ID
resourceType: String
resourceId: ID
}
type AuditLogEntry implements Node {
id: ID!
organization: Organization @goField(forceResolver: true)
actorId: ID!
actorType: AuditLogActorType!
action: String!
resourceType: String!
resourceId: ID!
metadata: String
createdAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type AuditLogEntryConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.AuditLogEntryConnection"
) {
edges: [AuditLogEntryEdge!]!
pageInfo: PageInfo!
totalCount: Int! @goField(forceResolver: true)
}
type AuditLogEntryEdge {
cursor: CursorKey!
node: AuditLogEntry!
}

View File

@@ -0,0 +1,349 @@
# Directives
directive @goField(
forceResolver: Boolean
name: String
omittable: Boolean
) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION
directive @goModel(
model: String
models: [String!]
) on OBJECT | INPUT_OBJECT | SCALAR | ENUM | INTERFACE | UNION
directive @goEnum(value: String) on ENUM_VALUE
# Scalars
scalar CursorKey
scalar Datetime
scalar Upload
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!
}
type Organization implements Node {
id: ID!
name: String!
logoUrl: String @goField(forceResolver: true)
horizontalLogoUrl: String @goField(forceResolver: true)
description: String
websiteUrl: String
email: String
headquarterAddress: String
context: OrganizationContext @goField(forceResolver: true)
profiles(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ProfileOrder
filter: ProfileFilter
): ProfileConnection! @goField(forceResolver: true)
measureCategories: [String!]! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type Mutation {
updateOrganizationContext(
input: UpdateOrganizationContextInput!
): UpdateOrganizationContextPayload!
}

View File

@@ -0,0 +1,148 @@
extend type Organization {
slackConnections(
first: Int
after: CursorKey
last: Int
before: CursorKey
): SlackConnectionConnection! @goField(forceResolver: true)
slackOAuth2Scopes: [String!]! @goField(forceResolver: true)
connectors(filter: ConnectorFilter): [Connector!]! @goField(forceResolver: true)
connectorProviderInfos: [ConnectorProviderInfo!]! @goField(forceResolver: true)
}
enum ConnectorProvider
@goModel(model: "go.probo.inc/probo/pkg/coredata.ConnectorProvider") {
SLACK @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderSlack")
GOOGLE_WORKSPACE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderGoogleWorkspace"
)
LINEAR @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderLinear")
ONE_PASSWORD
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderOnePassword"
)
HUBSPOT
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderHubSpot")
DOCUSIGN
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderDocuSign")
NOTION @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderNotion")
BREX @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderBrex")
TALLY @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderTally")
CLOUDFLARE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderCloudflare")
OPENAI @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderOpenAI")
SENTRY @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderSentry")
SUPABASE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderSupabase")
GITHUB @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderGitHub")
INTERCOM
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderIntercom")
RESEND @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderResend")
}
type ConnectorProviderInfo {
provider: ConnectorProvider!
displayName: String!
oauthConfigured: Boolean!
apiKeySupported: Boolean!
clientCredentialsSupported: Boolean!
oauth2Scopes: [String!]!
extraSettings: [ConnectorProviderSettingInfo!]!
}
type ConnectorProviderSettingInfo {
key: String!
label: String!
required: Boolean!
}
input ConnectorFilter {
providers: [ConnectorProvider!]
}
type Connector {
id: ID!
provider: ConnectorProvider!
oauth2Scopes: [String!]! @goField(forceResolver: true)
createdAt: Datetime!
}
type SlackConnection implements Node {
id: ID!
channel: String
channelId: String
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type SlackConnectionConnection {
edges: [SlackConnectionEdge!]!
pageInfo: PageInfo!
}
type SlackConnectionEdge {
cursor: CursorKey!
node: SlackConnection!
}
extend type Mutation {
createAPIKeyConnector(
input: CreateAPIKeyConnectorInput!
): CreateAPIKeyConnectorPayload!
createClientCredentialsConnector(
input: CreateClientCredentialsConnectorInput!
): CreateClientCredentialsConnectorPayload!
deleteConnector(input: DeleteConnectorInput!): DeleteConnectorPayload!
deleteSlackConnection(
input: DeleteSlackConnectionInput!
): DeleteSlackConnectionPayload!
}
input CreateAPIKeyConnectorInput {
organizationId: ID!
provider: ConnectorProvider!
apiKey: String!
tallyOrganizationId: String
sentryOrganizationSlug: String
supabaseOrganizationSlug: String
githubOrganization: String
onePasswordScimBridgeUrl: String
}
type CreateAPIKeyConnectorPayload {
connector: Connector!
}
input CreateClientCredentialsConnectorInput {
organizationId: ID!
provider: ConnectorProvider!
clientId: String!
clientSecret: String!
tokenUrl: String!
scope: String
onePasswordAccountId: String
onePasswordRegion: String
}
type CreateClientCredentialsConnectorPayload {
connector: Connector
}
input DeleteConnectorInput {
connectorId: ID!
}
type DeleteConnectorPayload {
deletedConnectorId: ID!
}
input DeleteSlackConnectionInput {
slackConnectionId: ID!
}
type DeleteSlackConnectionPayload {
deletedSlackConnectionId: ID!
}

View File

@@ -0,0 +1,506 @@
extend type Organization {
controls(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
statementsOfApplicability(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: StatementOfApplicabilityOrder
filter: StatementOfApplicabilityFilter = { snapshotId: null }
): StatementOfApplicabilityConnection! @goField(forceResolver: true)
}
enum ControlImplementationState
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ControlImplementationState"
) {
IMPLEMENTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ControlImplementationStateImplemented"
)
NOT_IMPLEMENTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ControlImplementationStateNotImplemented"
)
}
enum ControlOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.ControlOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ControlOrderFieldCreatedAt"
)
SECTION_TITLE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ControlOrderFieldSectionTitle"
)
}
enum ApplicabilityStatementOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.ApplicabilityStatementOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ApplicabilityStatementOrderFieldCreatedAt"
)
CONTROL_SECTION_TITLE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ApplicabilityStatementOrderFieldControlSectionTitle"
)
}
enum StatementOfApplicabilityOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.StatementOfApplicabilityOrderField"
) {
NAME
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.StatementOfApplicabilityOrderFieldName"
)
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.StatementOfApplicabilityOrderFieldCreatedAt"
)
}
input ControlOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ControlOrderBy"
) {
direction: OrderDirection!
field: ControlOrderField!
}
input ApplicabilityStatementOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ApplicabilityStatementOrderBy"
) {
direction: OrderDirection!
field: ApplicabilityStatementOrderField!
}
input StatementOfApplicabilityOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.StatementOfApplicabilityOrderBy"
) {
direction: OrderDirection!
field: StatementOfApplicabilityOrderField!
}
input ControlFilter {
query: String
}
input StatementOfApplicabilityFilter {
snapshotId: ID
}
type Control implements Node {
id: ID!
organization: Organization @goField(forceResolver: true)
sectionTitle: String!
name: String!
description: String
bestPractice: Boolean!
implemented: ControlImplementationState!
notImplementedJustification: String
regulatory: Boolean! @goField(forceResolver: true)
contractual: Boolean! @goField(forceResolver: true)
riskAssessment: Boolean! @goField(forceResolver: true)
framework: Framework! @goField(forceResolver: true)
measures(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: MeasureOrder
filter: MeasureFilter
): MeasureConnection! @goField(forceResolver: true)
documents(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: DocumentOrder
filter: DocumentFilter
): DocumentConnection! @goField(forceResolver: true)
audits(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: AuditOrder
): AuditConnection! @goField(forceResolver: true)
obligations(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ObligationOrder
filter: ObligationFilter
): ObligationConnection! @goField(forceResolver: true)
snapshots(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: SnapshotOrder
): SnapshotConnection! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type ControlConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ControlConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [ControlEdge!]!
pageInfo: PageInfo!
}
type ControlEdge {
cursor: CursorKey!
node: Control!
}
type StatementOfApplicability implements Node {
id: ID!
name: String!
sourceId: ID
snapshotId: ID
organization: Organization @goField(forceResolver: true)
owner: Profile! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
applicabilityStatements(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ApplicabilityStatementOrder
): ApplicabilityStatementConnection! @goField(forceResolver: true)
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type ApplicabilityStatementConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ApplicabilityStatementConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [ApplicabilityStatementEdge!]!
pageInfo: PageInfo!
}
type ApplicabilityStatementEdge {
cursor: CursorKey!
node: ApplicabilityStatement!
}
type ApplicabilityStatement implements Node {
id: ID!
statementOfApplicability: StatementOfApplicability! @goField(forceResolver: true)
control: Control! @goField(forceResolver: true)
applicability: Boolean!
justification: String!
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type StatementOfApplicabilityConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.StatementOfApplicabilityConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [StatementOfApplicabilityEdge!]!
pageInfo: PageInfo!
}
type StatementOfApplicabilityEdge {
cursor: CursorKey!
node: StatementOfApplicability!
}
extend type Mutation {
createControl(input: CreateControlInput!): CreateControlPayload!
updateControl(input: UpdateControlInput!): UpdateControlPayload!
deleteControl(input: DeleteControlInput!): DeleteControlPayload!
createControlMeasureMapping(
input: CreateControlMeasureMappingInput!
): CreateControlMeasureMappingPayload!
createControlDocumentMapping(
input: CreateControlDocumentMappingInput!
): CreateControlDocumentMappingPayload!
deleteControlMeasureMapping(
input: DeleteControlMeasureMappingInput!
): DeleteControlMeasureMappingPayload!
deleteControlDocumentMapping(
input: DeleteControlDocumentMappingInput!
): DeleteControlDocumentMappingPayload!
createApplicabilityStatement(
input: CreateApplicabilityStatementInput!
): CreateApplicabilityStatementPayload!
updateApplicabilityStatement(
input: UpdateApplicabilityStatementInput!
): UpdateApplicabilityStatementPayload!
deleteApplicabilityStatement(
input: DeleteApplicabilityStatementInput!
): DeleteApplicabilityStatementPayload!
createControlAuditMapping(
input: CreateControlAuditMappingInput!
): CreateControlAuditMappingPayload
deleteControlAuditMapping(
input: DeleteControlAuditMappingInput!
): DeleteControlAuditMappingPayload
createControlObligationMapping(
input: CreateControlObligationMappingInput!
): CreateControlObligationMappingPayload!
deleteControlObligationMapping(
input: DeleteControlObligationMappingInput!
): DeleteControlObligationMappingPayload!
createControlSnapshotMapping(
input: CreateControlSnapshotMappingInput!
): CreateControlSnapshotMappingPayload!
deleteControlSnapshotMapping(
input: DeleteControlSnapshotMappingInput!
): DeleteControlSnapshotMappingPayload!
createStatementOfApplicability(
input: CreateStatementOfApplicabilityInput!
): CreateStatementOfApplicabilityPayload!
updateStatementOfApplicability(
input: UpdateStatementOfApplicabilityInput!
): UpdateStatementOfApplicabilityPayload!
deleteStatementOfApplicability(
input: DeleteStatementOfApplicabilityInput!
): DeleteStatementOfApplicabilityPayload!
exportStatementOfApplicabilityPDF(
input: ExportStatementOfApplicabilityPDFInput!
): ExportStatementOfApplicabilityPDFPayload!
}
input CreateControlInput {
frameworkId: ID!
sectionTitle: String!
name: String!
description: String
bestPractice: Boolean!
implemented: ControlImplementationState!
notImplementedJustification: String
}
input UpdateControlInput {
id: ID!
sectionTitle: String
name: String
description: String @goField(omittable: true)
bestPractice: Boolean
implemented: ControlImplementationState
notImplementedJustification: String @goField(omittable: true)
}
input DeleteControlInput {
controlId: ID!
}
input CreateControlMeasureMappingInput {
controlId: ID!
measureId: ID!
}
input CreateControlDocumentMappingInput {
controlId: ID!
documentId: ID!
}
input DeleteControlMeasureMappingInput {
controlId: ID!
measureId: ID!
}
input DeleteControlDocumentMappingInput {
controlId: ID!
documentId: ID!
}
input CreateApplicabilityStatementInput {
statementOfApplicabilityId: ID!
controlId: ID!
applicability: Boolean!
justification: String
}
input UpdateApplicabilityStatementInput {
applicabilityStatementId: ID!
applicability: Boolean!
justification: String
}
input DeleteApplicabilityStatementInput {
applicabilityStatementId: ID!
}
input CreateControlAuditMappingInput {
controlId: ID!
auditId: ID!
}
input DeleteControlAuditMappingInput {
controlId: ID!
auditId: ID!
}
input CreateControlObligationMappingInput {
controlId: ID!
obligationId: ID!
}
input DeleteControlObligationMappingInput {
controlId: ID!
obligationId: ID!
}
input CreateControlSnapshotMappingInput {
controlId: ID!
snapshotId: ID!
}
input DeleteControlSnapshotMappingInput {
controlId: ID!
snapshotId: ID!
}
input CreateStatementOfApplicabilityInput {
organizationId: ID!
name: String!
ownerId: ID!
}
input UpdateStatementOfApplicabilityInput {
id: ID!
name: String
ownerId: ID
}
input ApplicabilityStatementInput {
controlId: ID!
applicability: Boolean!
justification: String
}
input DeleteStatementOfApplicabilityInput {
statementOfApplicabilityId: ID!
}
input ExportStatementOfApplicabilityPDFInput {
statementOfApplicabilityId: ID!
}
type CreateControlPayload {
controlEdge: ControlEdge!
}
type UpdateControlPayload {
control: Control!
}
type DeleteControlPayload {
deletedControlId: ID!
}
type CreateControlMeasureMappingPayload {
controlEdge: ControlEdge!
measureEdge: MeasureEdge!
}
type CreateControlDocumentMappingPayload {
controlEdge: ControlEdge!
documentEdge: DocumentEdge!
}
type DeleteControlMeasureMappingPayload {
deletedControlId: ID!
deletedMeasureId: ID!
}
type DeleteControlDocumentMappingPayload {
deletedControlId: ID!
deletedDocumentId: ID!
}
type CreateApplicabilityStatementPayload {
applicabilityStatementEdge: ApplicabilityStatementEdge!
}
type UpdateApplicabilityStatementPayload {
applicabilityStatement: ApplicabilityStatement!
}
type DeleteApplicabilityStatementPayload {
deletedApplicabilityStatementId: ID!
}
type CreateControlAuditMappingPayload {
controlEdge: ControlEdge
auditEdge: AuditEdge
}
type DeleteControlAuditMappingPayload {
deletedControlId: ID
deletedAuditId: ID
}
type CreateControlObligationMappingPayload {
controlEdge: ControlEdge!
obligationEdge: ObligationEdge!
}
type DeleteControlObligationMappingPayload {
deletedControlId: ID!
deletedObligationId: ID!
}
type CreateControlSnapshotMappingPayload {
controlEdge: ControlEdge!
snapshotEdge: SnapshotEdge!
}
type DeleteControlSnapshotMappingPayload {
deletedControlId: ID!
deletedSnapshotId: ID!
}
type CreateStatementOfApplicabilityPayload {
statementOfApplicabilityEdge: StatementOfApplicabilityEdge!
}
type UpdateStatementOfApplicabilityPayload {
statementOfApplicability: StatementOfApplicability!
}
type DeleteStatementOfApplicabilityPayload {
deletedStatementOfApplicabilityId: ID!
}
type ExportStatementOfApplicabilityPDFPayload {
data: String!
}

View File

@@ -0,0 +1,252 @@
extend type Organization {
dataProtectionImpactAssessments(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: DataProtectionImpactAssessmentOrder
filter: DataProtectionImpactAssessmentFilter = { snapshotId: null }
): DataProtectionImpactAssessmentConnection! @goField(forceResolver: true)
transferImpactAssessments(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: TransferImpactAssessmentOrder
filter: TransferImpactAssessmentFilter = { snapshotId: null }
): TransferImpactAssessmentConnection! @goField(forceResolver: true)
}
enum DataProtectionImpactAssessmentResidualRisk
@goModel(
model: "go.probo.inc/probo/pkg/coredata.DataProtectionImpactAssessmentResidualRisk"
) {
LOW
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DataProtectionImpactAssessmentResidualRiskLow"
)
MEDIUM
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DataProtectionImpactAssessmentResidualRiskMedium"
)
HIGH
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DataProtectionImpactAssessmentResidualRiskHigh"
)
}
enum DataProtectionImpactAssessmentOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.DataProtectionImpactAssessmentOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DataProtectionImpactAssessmentOrderFieldCreatedAt"
)
}
enum TransferImpactAssessmentOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.TransferImpactAssessmentOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TransferImpactAssessmentOrderFieldCreatedAt"
)
}
input DataProtectionImpactAssessmentOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.DataProtectionImpactAssessmentOrderBy"
) {
direction: OrderDirection!
field: DataProtectionImpactAssessmentOrderField!
}
input TransferImpactAssessmentOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TransferImpactAssessmentOrderBy"
) {
direction: OrderDirection!
field: TransferImpactAssessmentOrderField!
}
input DataProtectionImpactAssessmentFilter {
snapshotId: ID
}
input TransferImpactAssessmentFilter {
snapshotId: ID
}
type DataProtectionImpactAssessment implements Node {
id: ID!
processingActivity: ProcessingActivity! @goField(forceResolver: true)
organization: Organization! @goField(forceResolver: true)
description: String
necessityAndProportionality: String
potentialRisk: String
mitigations: String
residualRisk: DataProtectionImpactAssessmentResidualRisk
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type TransferImpactAssessment implements Node {
id: ID!
processingActivity: ProcessingActivity! @goField(forceResolver: true)
organization: Organization! @goField(forceResolver: true)
dataSubjects: String
legalMechanism: String
transfer: String
localLawRisk: String
supplementaryMeasures: String
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type DataProtectionImpactAssessmentConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.DataProtectionImpactAssessmentConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [DataProtectionImpactAssessmentEdge!]!
pageInfo: PageInfo!
}
type DataProtectionImpactAssessmentEdge {
cursor: CursorKey!
node: DataProtectionImpactAssessment!
}
type TransferImpactAssessmentConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TransferImpactAssessmentConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [TransferImpactAssessmentEdge!]!
pageInfo: PageInfo!
}
type TransferImpactAssessmentEdge {
cursor: CursorKey!
node: TransferImpactAssessment!
}
extend type Mutation {
createDataProtectionImpactAssessment(
input: CreateDataProtectionImpactAssessmentInput!
): CreateDataProtectionImpactAssessmentPayload!
updateDataProtectionImpactAssessment(
input: UpdateDataProtectionImpactAssessmentInput!
): UpdateDataProtectionImpactAssessmentPayload!
deleteDataProtectionImpactAssessment(
input: DeleteDataProtectionImpactAssessmentInput!
): DeleteDataProtectionImpactAssessmentPayload!
createTransferImpactAssessment(
input: CreateTransferImpactAssessmentInput!
): CreateTransferImpactAssessmentPayload!
updateTransferImpactAssessment(
input: UpdateTransferImpactAssessmentInput!
): UpdateTransferImpactAssessmentPayload!
deleteTransferImpactAssessment(
input: DeleteTransferImpactAssessmentInput!
): DeleteTransferImpactAssessmentPayload!
exportDataProtectionImpactAssessmentsPDF(
input: ExportDataProtectionImpactAssessmentsPDFInput!
): ExportDataProtectionImpactAssessmentsPDFPayload!
exportTransferImpactAssessmentsPDF(
input: ExportTransferImpactAssessmentsPDFInput!
): ExportTransferImpactAssessmentsPDFPayload!
}
input CreateDataProtectionImpactAssessmentInput {
processingActivityId: ID!
description: String
necessityAndProportionality: String
potentialRisk: String
mitigations: String
residualRisk: DataProtectionImpactAssessmentResidualRisk
}
input UpdateDataProtectionImpactAssessmentInput {
id: ID!
description: String @goField(omittable: true)
necessityAndProportionality: String @goField(omittable: true)
potentialRisk: String @goField(omittable: true)
mitigations: String @goField(omittable: true)
residualRisk: DataProtectionImpactAssessmentResidualRisk
}
input DeleteDataProtectionImpactAssessmentInput {
dataProtectionImpactAssessmentId: ID!
}
input CreateTransferImpactAssessmentInput {
processingActivityId: ID!
dataSubjects: String
legalMechanism: String
transfer: String
localLawRisk: String
supplementaryMeasures: String
}
input UpdateTransferImpactAssessmentInput {
id: ID!
dataSubjects: String @goField(omittable: true)
legalMechanism: String @goField(omittable: true)
transfer: String @goField(omittable: true)
localLawRisk: String @goField(omittable: true)
supplementaryMeasures: String @goField(omittable: true)
}
input DeleteTransferImpactAssessmentInput {
transferImpactAssessmentId: ID!
}
input ExportDataProtectionImpactAssessmentsPDFInput {
organizationId: ID!
filter: DataProtectionImpactAssessmentFilter
}
input ExportTransferImpactAssessmentsPDFInput {
organizationId: ID!
filter: TransferImpactAssessmentFilter
}
type CreateDataProtectionImpactAssessmentPayload {
dataProtectionImpactAssessment: DataProtectionImpactAssessment!
}
type UpdateDataProtectionImpactAssessmentPayload {
dataProtectionImpactAssessment: DataProtectionImpactAssessment!
}
type DeleteDataProtectionImpactAssessmentPayload {
deletedDataProtectionImpactAssessmentId: ID!
}
type CreateTransferImpactAssessmentPayload {
transferImpactAssessment: TransferImpactAssessment!
}
type UpdateTransferImpactAssessmentPayload {
transferImpactAssessment: TransferImpactAssessment!
}
type DeleteTransferImpactAssessmentPayload {
deletedTransferImpactAssessmentId: ID!
}
type ExportDataProtectionImpactAssessmentsPDFPayload {
data: String!
}
type ExportTransferImpactAssessmentsPDFPayload {
data: String!
}

View File

@@ -0,0 +1,826 @@
extend type Organization {
documents(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: DocumentOrder
filter: DocumentFilter
): DocumentConnection! @goField(forceResolver: true)
}
extend type Viewer {
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)
}
enum DocumentVersionStatus
@goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentVersionStatus") {
DRAFT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionStatusDraft"
)
PENDING_APPROVAL
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionStatusPendingApproval"
)
PUBLISHED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionStatusPublished"
)
}
enum DocumentStatus
@goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentStatus") {
ACTIVE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentStatusActive")
ARCHIVED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentStatusArchived")
}
enum DocumentType
@goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentType") {
OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeOther")
GOVERNANCE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeGovernance")
POLICY @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePolicy")
PROCEDURE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeProcedure")
PLAN @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePlan")
REGISTER
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeRegister")
RECORD @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeRecord")
REPORT @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeReport")
TEMPLATE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeTemplate")
}
enum DocumentClassification
@goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentClassification") {
PUBLIC
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentClassificationPublic"
)
INTERNAL
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentClassificationInternal"
)
CONFIDENTIAL
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentClassificationConfidential"
)
SECRET
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentClassificationSecret"
)
}
enum DocumentOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentOrderField") {
TITLE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentOrderFieldTitle"
)
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentOrderFieldCreatedAt"
)
UPDATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentOrderFieldUpdatedAt"
)
DOCUMENT_TYPE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentOrderFieldDocumentType"
)
}
enum DocumentVersionOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.DocumentVersionOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionOrderFieldCreatedAt"
)
}
enum DocumentVersionSignatureState
@goModel(
model: "go.probo.inc/probo/pkg/coredata.DocumentVersionSignatureState"
) {
REQUESTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionSignatureStateRequested"
)
SIGNED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionSignatureStateSigned"
)
}
enum DocumentVersionSignatureOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.DocumentVersionSignatureOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionSignatureOrderFieldCreatedAt"
)
SIGNED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionSignatureOrderFieldSignedAt"
)
}
enum DocumentVersionApprovalDecisionState
@goModel(
model: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalDecisionState"
) {
PENDING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalDecisionStatePending"
)
APPROVED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalDecisionStateApproved"
)
REJECTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalDecisionStateRejected"
)
VOIDED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalDecisionStateVoided"
)
}
enum DocumentVersionApprovalDecisionOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalDecisionOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalDecisionOrderFieldCreatedAt"
)
}
enum DocumentVersionApprovalQuorumStatus
@goModel(
model: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalQuorumStatus"
) {
PENDING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalQuorumStatusPending"
)
APPROVED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalQuorumStatusApproved"
)
REJECTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalQuorumStatusRejected"
)
VOIDED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalQuorumStatusVoided"
)
}
enum DocumentVersionApprovalQuorumOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalQuorumOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalQuorumOrderFieldCreatedAt"
)
}
input DocumentOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.DocumentOrderBy"
) {
direction: OrderDirection!
field: DocumentOrderField!
}
input DocumentVersionOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.DocumentVersionOrderBy"
) {
direction: OrderDirection!
field: DocumentVersionOrderField!
}
input DocumentFilter {
query: String
documentTypes: [DocumentType!]
classifications: [DocumentClassification!]
status: [DocumentStatus!]
}
input DocumentVersionFilter {
statuses: [DocumentVersionStatus!]
}
input DocumentVersionSignatureOrder {
field: DocumentVersionSignatureOrderField!
direction: OrderDirection!
}
input DocumentVersionSignatureFilter {
states: [DocumentVersionSignatureState!]
activeContract: Boolean
}
input DocumentVersionApprovalQuorumOrder {
field: DocumentVersionApprovalQuorumOrderField!
direction: OrderDirection!
}
input DocumentVersionApprovalDecisionFilter {
states: [DocumentVersionApprovalDecisionState!]
}
input DocumentVersionApprovalDecisionOrder {
field: DocumentVersionApprovalDecisionOrderField!
direction: OrderDirection!
}
type Document implements Node {
id: ID!
currentPublishedMajor: Int
currentPublishedMinor: Int
trustCenterVisibility: TrustCenterVisibility!
organization: Organization! @goField(forceResolver: true)
versions(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: DocumentVersionOrder
filter: DocumentVersionFilter
): DocumentVersionConnection! @goField(forceResolver: true)
controls(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
defaultApprovers: [Profile!]! @goField(forceResolver: true)
status: DocumentStatus!
archivedAt: Datetime
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type DocumentVersion implements Node {
id: ID!
document: Document! @goField(forceResolver: true)
status: DocumentVersionStatus!
major: Int!
minor: Int!
content: String!
changelog: String!
title: String!
classification: DocumentClassification!
documentType: DocumentType!
approvers(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ProfileOrder
): ProfileConnection! @goField(forceResolver: true)
signatures(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: DocumentVersionSignatureOrder
filter: DocumentVersionSignatureFilter
): DocumentVersionSignatureConnection! @goField(forceResolver: true)
approvalQuorums(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: DocumentVersionApprovalQuorumOrder
): DocumentVersionApprovalQuorumConnection! @goField(forceResolver: true)
signed: Boolean! @goField(forceResolver: true)
publishedAt: Datetime
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type DocumentVersionSignature implements Node {
id: ID!
documentVersion: DocumentVersion! @goField(forceResolver: true)
state: DocumentVersionSignatureState!
signedBy: Profile! @goField(forceResolver: true)
signedAt: Datetime
requestedAt: Datetime!
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type DocumentVersionApprovalDecision implements Node {
id: ID!
quorum: DocumentVersionApprovalQuorum! @goField(forceResolver: true)
documentVersion: DocumentVersion! @goField(forceResolver: true)
approver: Profile! @goField(forceResolver: true)
state: DocumentVersionApprovalDecisionState!
comment: String
decidedAt: Datetime
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type DocumentVersionApprovalQuorum implements Node {
id: ID!
documentVersion: DocumentVersion! @goField(forceResolver: true)
status: DocumentVersionApprovalQuorumStatus!
decisions(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: DocumentVersionApprovalDecisionOrder
filter: DocumentVersionApprovalDecisionFilter
): DocumentVersionApprovalDecisionConnection! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type EmployeeDocument
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.EmployeeDocument"
) {
id: ID!
title: String!
signed: Boolean @goField(forceResolver: true)
approvalState: DocumentVersionApprovalDecisionState @goField(forceResolver: true)
versions(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: DocumentVersionOrder
): EmployeeDocumentVersionConnection! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
}
type EmployeeDocumentVersion
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.EmployeeDocumentVersion"
) {
id: ID!
major: Int!
minor: Int!
status: DocumentVersionStatus!
classification: DocumentClassification!
documentType: DocumentType!
signed: Boolean! @goField(forceResolver: true)
approvalDecision: DocumentVersionApprovalDecision @goField(forceResolver: true)
publishedAt: Datetime
createdAt: Datetime!
updatedAt: Datetime!
}
type DocumentConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.DocumentConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [DocumentEdge!]!
pageInfo: PageInfo!
}
type DocumentEdge {
cursor: CursorKey!
node: Document!
}
type DocumentVersionConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.DocumentVersionConnection"
) {
edges: [DocumentVersionEdge!]!
pageInfo: PageInfo!
totalCount: Int! @goField(forceResolver: true)
}
type DocumentVersionEdge {
cursor: CursorKey!
node: DocumentVersion!
}
type DocumentVersionSignatureConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.DocumentVersionSignatureConnection"
) {
edges: [DocumentVersionSignatureEdge!]!
pageInfo: PageInfo!
totalCount: Int! @goField(forceResolver: true)
}
type DocumentVersionSignatureEdge {
cursor: CursorKey!
node: DocumentVersionSignature!
}
type DocumentVersionApprovalQuorumConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.DocumentVersionApprovalQuorumConnection"
) {
edges: [DocumentVersionApprovalQuorumEdge!]!
pageInfo: PageInfo!
totalCount: Int! @goField(forceResolver: true)
}
type DocumentVersionApprovalQuorumEdge {
cursor: CursorKey!
node: DocumentVersionApprovalQuorum!
}
type DocumentVersionApprovalDecisionConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.DocumentVersionApprovalDecisionConnection"
) {
edges: [DocumentVersionApprovalDecisionEdge!]!
pageInfo: PageInfo!
totalCount: Int! @goField(forceResolver: true)
}
type DocumentVersionApprovalDecisionEdge {
cursor: CursorKey!
node: DocumentVersionApprovalDecision!
}
type EmployeeDocumentConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.EmployeeDocumentConnection"
) {
edges: [EmployeeDocumentEdge!]!
pageInfo: PageInfo!
}
type EmployeeDocumentEdge
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.EmployeeDocumentEdge"
) {
cursor: CursorKey!
node: EmployeeDocument!
}
type EmployeeDocumentVersionConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.EmployeeDocumentVersionConnection"
) {
edges: [EmployeeDocumentVersionEdge!]!
pageInfo: PageInfo!
}
type EmployeeDocumentVersionEdge
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.EmployeeDocumentVersionEdge"
) {
cursor: CursorKey!
node: EmployeeDocumentVersion!
}
extend type Mutation {
createDocument(input: CreateDocumentInput!): CreateDocumentPayload!
updateDocument(input: UpdateDocumentInput!): UpdateDocumentPayload!
deleteDocumentDraft(input: DeleteDocumentDraftInput!): DeleteDocumentDraftPayload!
archiveDocument(input: ArchiveDocumentInput!): ArchiveDocumentPayload!
unarchiveDocument(input: UnarchiveDocumentInput!): UnarchiveDocumentPayload!
deleteDocument(input: DeleteDocumentInput!): DeleteDocumentPayload!
publishMajorDocumentVersion(
input: PublishMajorDocumentVersionInput!
): PublishDocumentVersionPayload!
publishMinorDocumentVersion(
input: PublishMinorDocumentVersionInput!
): PublishDocumentVersionPayload!
bulkPublishMajorDocumentVersions(
input: BulkPublishDocumentVersionsInput!
): BulkPublishDocumentVersionsPayload!
bulkPublishMinorDocumentVersions(
input: BulkPublishDocumentVersionsInput!
): BulkPublishDocumentVersionsPayload!
requestDocumentVersionApproval(
input: RequestDocumentVersionApprovalInput!
): RequestDocumentVersionApprovalPayload!
voidDocumentVersionApproval(
input: VoidDocumentVersionApprovalInput!
): VoidDocumentVersionApprovalPayload!
bulkDeleteDocuments(
input: BulkDeleteDocumentsInput!
): BulkDeleteDocumentsPayload!
bulkArchiveDocuments(
input: BulkArchiveDocumentsInput!
): BulkArchiveDocumentsPayload!
bulkUnarchiveDocuments(
input: BulkUnarchiveDocumentsInput!
): BulkUnarchiveDocumentsPayload!
bulkExportDocuments(
input: BulkExportDocumentsInput!
): BulkExportDocumentsPayload!
generateDocumentChangelog(
input: GenerateDocumentChangelogInput!
): GenerateDocumentChangelogPayload!
requestSignature(input: RequestSignatureInput!): RequestSignaturePayload!
bulkRequestSignatures(
input: BulkRequestSignaturesInput!
): BulkRequestSignaturesPayload!
sendSigningNotifications(
input: SendSigningNotificationsInput!
): SendSigningNotificationsPayload!
cancelSignatureRequest(
input: CancelSignatureRequestInput!
): CancelSignatureRequestPayload!
signDocument(input: SignDocumentInput!): SignDocumentPayload!
approveDocumentVersion(
input: ApproveDocumentVersionInput!
): ApproveDocumentVersionPayload!
rejectDocumentVersion(
input: RejectDocumentVersionInput!
): RejectDocumentVersionPayload!
exportDocumentVersionPDF(
input: ExportDocumentVersionPDFInput!
): ExportDocumentVersionPDFPayload!
exportEmployeeDocumentVersionPDF(
input: ExportEmployeeDocumentVersionPDFInput!
): ExportEmployeeDocumentVersionPDFPayload!
}
input CreateDocumentInput {
organizationId: ID!
title: String!
content: String
documentType: DocumentType!
classification: DocumentClassification!
trustCenterVisibility: TrustCenterVisibility
defaultApproverIds: [ID!]
}
input UpdateDocumentInput {
id: ID!
title: String
content: String
classification: DocumentClassification
documentType: DocumentType
trustCenterVisibility: TrustCenterVisibility
defaultApproverIds: [ID!]
}
input DeleteDocumentDraftInput {
documentId: ID!
}
input ArchiveDocumentInput {
documentId: ID!
}
input UnarchiveDocumentInput {
documentId: ID!
}
input DeleteDocumentInput {
documentId: ID!
}
input ExportDocumentVersionPDFInput {
documentVersionId: ID!
withWatermark: Boolean!
watermarkEmail: EmailAddr
withSignatures: Boolean!
}
input ExportEmployeeDocumentVersionPDFInput {
documentVersionId: ID!
}
input PublishMajorDocumentVersionInput {
documentId: ID!
changelog: String
}
input PublishMinorDocumentVersionInput {
documentId: ID!
changelog: String
}
input BulkPublishDocumentVersionsInput {
documentIds: [ID!]!
changelog: String!
}
input RequestDocumentVersionApprovalInput {
documentId: ID!
approverIds: [ID!]!
changelog: String
}
input VoidDocumentVersionApprovalInput {
documentVersionId: ID!
}
input BulkDeleteDocumentsInput {
documentIds: [ID!]!
}
input BulkArchiveDocumentsInput {
documentIds: [ID!]!
}
input BulkUnarchiveDocumentsInput {
documentIds: [ID!]!
}
input BulkExportDocumentsInput {
documentIds: [ID!]!
withWatermark: Boolean!
watermarkEmail: EmailAddr
withSignatures: Boolean!
}
input GenerateDocumentChangelogInput {
documentId: ID!
}
input RequestSignatureInput {
documentVersionId: ID!
signatoryId: ID!
}
input BulkRequestSignaturesInput {
documentIds: [ID!]!
signatoryIds: [ID!]!
}
input SendSigningNotificationsInput {
organizationId: ID!
}
input CancelSignatureRequestInput {
documentVersionSignatureId: ID!
}
input SignDocumentInput {
documentVersionId: ID!
}
input ApproveDocumentVersionInput {
documentVersionId: ID!
comment: String
}
input RejectDocumentVersionInput {
documentVersionId: ID!
comment: String
}
type CreateDocumentPayload {
documentEdge: DocumentEdge!
documentVersionEdge: DocumentVersionEdge!
}
type UpdateDocumentPayload {
document: Document!
documentVersion: DocumentVersion
documentVersionEdge: DocumentVersionEdge
}
type DeleteDocumentDraftPayload {
document: Document!
}
type ArchiveDocumentPayload {
document: Document!
}
type UnarchiveDocumentPayload {
document: Document!
}
type DeleteDocumentPayload {
deletedDocumentId: ID!
}
type ExportDocumentVersionPDFPayload {
data: String!
}
type ExportEmployeeDocumentVersionPDFPayload {
data: String!
}
type PublishDocumentVersionPayload {
document: Document!
documentVersion: DocumentVersion!
}
type BulkPublishDocumentVersionsPayload {
documentVersions: [DocumentVersion!]!
documents: [Document!]!
}
type RequestDocumentVersionApprovalPayload {
approvalQuorum: DocumentVersionApprovalQuorum!
}
type VoidDocumentVersionApprovalPayload {
approvalQuorum: DocumentVersionApprovalQuorum!
documentVersion: DocumentVersion!
}
type BulkDeleteDocumentsPayload {
deletedDocumentIds: [ID!]!
}
type BulkArchiveDocumentsPayload {
documents: [Document!]!
}
type BulkUnarchiveDocumentsPayload {
documents: [Document!]!
}
type BulkExportDocumentsPayload {
exportJobId: ID!
}
type RequestSignaturePayload {
documentVersionSignatureEdge: DocumentVersionSignatureEdge!
}
type BulkRequestSignaturesPayload {
documentVersionSignatureEdges: [DocumentVersionSignatureEdge!]!
}
type SendSigningNotificationsPayload {
success: Boolean!
}
type CancelSignatureRequestPayload {
deletedDocumentVersionSignatureId: ID!
}
type SignDocumentPayload {
documentVersionSignature: DocumentVersionSignature!
}
type ApproveDocumentVersionPayload {
approvalDecision: DocumentVersionApprovalDecision!
}
type RejectDocumentVersionPayload {
approvalDecision: DocumentVersionApprovalDecision!
}
type GenerateDocumentChangelogPayload {
changelog: String!
}

View File

@@ -0,0 +1,143 @@
enum ElectronicSignatureStatus
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatus"
) {
PENDING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusPending"
)
ACCEPTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusAccepted"
)
PROCESSING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusProcessing"
)
COMPLETED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusCompleted"
)
FAILED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusFailed"
)
}
enum ElectronicSignatureDocumentType
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentType"
) {
NDA
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeNDA"
)
DPA
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeDPA"
)
MSA
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeMSA"
)
SOW
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeSOW"
)
SLA
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeSLA"
)
TOS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeTOS"
)
PRIVACY_POLICY
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypePrivacyPolicy"
)
OTHER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeOther"
)
}
enum ElectronicSignatureEventType
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventType"
) {
DOCUMENT_VIEWED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeDocumentViewed"
)
CONSENT_GIVEN
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeConsentGiven"
)
FULL_NAME_TYPED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeFullNameTyped"
)
SIGNATURE_ACCEPTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeSignatureAccepted"
)
SIGNATURE_COMPLETED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeSignatureCompleted"
)
SEAL_COMPUTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeSealComputed"
)
TIMESTAMP_REQUESTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeTimestampRequested"
)
CERTIFICATE_GENERATED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeCertificateGenerated"
)
PROCESSING_ERROR
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeProcessingError"
)
}
enum ElectronicSignatureEventSource
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventSource"
) {
CLIENT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventSourceClient"
)
SERVER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventSourceServer"
)
}
type ElectronicSignature implements Node {
id: ID!
status: ElectronicSignatureStatus!
documentType: ElectronicSignatureDocumentType!
consentText: String!
lastError: String
signedAt: Datetime
certificateFileUrl: String @goField(forceResolver: true)
events: [ElectronicSignatureEvent!]! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
}
type ElectronicSignatureEvent {
id: ID!
eventType: ElectronicSignatureEventType!
eventSource: ElectronicSignatureEventSource!
actorEmail: String!
actorIpAddress: String!
actorUserAgent: String!
occurredAt: Datetime!
createdAt: Datetime!
}

View File

@@ -0,0 +1,92 @@
extend type Organization {
evidences(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: EvidenceOrder
): EvidenceConnection! @goField(forceResolver: true)
}
enum EvidenceState
@goModel(model: "go.probo.inc/probo/pkg/coredata.EvidenceState") {
FULFILLED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.EvidenceStateFulfilled")
REQUESTED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.EvidenceStateRequested")
}
enum EvidenceType
@goModel(model: "go.probo.inc/probo/pkg/coredata.EvidenceType") {
FILE @goEnum(value: "go.probo.inc/probo/pkg/coredata.EvidenceTypeFile")
LINK @goEnum(value: "go.probo.inc/probo/pkg/coredata.EvidenceTypeLink")
}
enum EvidenceOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.EvidenceOrderField") {
CREATED_AT
}
input EvidenceOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.EvidenceOrderBy"
) {
direction: OrderDirection!
field: EvidenceOrderField!
}
type Evidence implements Node {
id: ID!
size: Int!
state: EvidenceState!
type: EvidenceType!
file: File @goField(forceResolver: true)
url: String
description: String
task: Task @goField(forceResolver: true)
measure: Measure! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type EvidenceConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.EvidenceConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [EvidenceEdge!]!
pageInfo: PageInfo!
}
type EvidenceEdge {
cursor: CursorKey!
node: Evidence!
}
extend type Mutation {
deleteEvidence(input: DeleteEvidenceInput!): DeleteEvidencePayload!
uploadMeasureEvidence(
input: UploadMeasureEvidenceInput!
): UploadMeasureEvidencePayload!
}
input DeleteEvidenceInput {
evidenceId: ID!
}
input UploadMeasureEvidenceInput {
measureId: ID!
file: Upload!
}
type DeleteEvidencePayload {
deletedEvidenceId: ID!
}
type UploadMeasureEvidencePayload {
evidenceEdge: EvidenceEdge!
}

View File

@@ -0,0 +1,116 @@
extend type Organization {
frameworks(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: FrameworkOrder
): FrameworkConnection! @goField(forceResolver: true)
}
enum FrameworkOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.FrameworkOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.FrameworkOrderFieldCreatedAt"
)
}
input FrameworkOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.FrameworkOrderBy"
) {
direction: OrderDirection!
field: FrameworkOrderField!
}
type Framework implements Node {
id: ID!
name: String!
description: String
organization: Organization! @goField(forceResolver: true)
controls(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
lightLogoURL: String @goField(forceResolver: true)
darkLogoURL: String @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type FrameworkConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.FrameworkConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [FrameworkEdge!]!
pageInfo: PageInfo!
}
type FrameworkEdge {
cursor: CursorKey!
node: Framework!
}
extend type Mutation {
createFramework(input: CreateFrameworkInput!): CreateFrameworkPayload!
updateFramework(input: UpdateFrameworkInput!): UpdateFrameworkPayload!
importFramework(input: ImportFrameworkInput!): ImportFrameworkPayload!
deleteFramework(input: DeleteFrameworkInput!): DeleteFrameworkPayload!
exportFramework(input: ExportFrameworkInput!): ExportFrameworkPayload!
}
input CreateFrameworkInput {
organizationId: ID!
name: String!
description: String
}
input UpdateFrameworkInput {
id: ID!
name: String
description: String @goField(omittable: true)
}
input ImportFrameworkInput {
organizationId: ID!
file: Upload!
}
input DeleteFrameworkInput {
frameworkId: ID!
}
input ExportFrameworkInput {
frameworkId: ID!
}
type CreateFrameworkPayload {
frameworkEdge: FrameworkEdge!
}
type UpdateFrameworkPayload {
framework: Framework!
}
type ImportFrameworkPayload {
frameworkEdge: FrameworkEdge!
}
type DeleteFrameworkPayload {
deletedFrameworkId: ID!
}
type ExportFrameworkPayload {
exportJobId: ID!
}

View File

@@ -0,0 +1,188 @@
enum MailingListSubscriberStatus
@goModel(
model: "go.probo.inc/probo/pkg/coredata.MailingListSubscriberStatus"
) {
PENDING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MailingListSubscriberStatusPending"
)
CONFIRMED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MailingListSubscriberStatusConfirmed"
)
}
enum MailingListUpdateStatus
@goModel(
model: "go.probo.inc/probo/pkg/coredata.MailingListUpdateStatus"
) {
DRAFT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MailingListUpdateStatusDraft"
)
ENQUEUED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MailingListUpdateStatusEnqueued"
)
PROCESSING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MailingListUpdateStatusProcessing"
)
SENT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MailingListUpdateStatusSent"
)
}
type MailingList implements Node {
id: ID!
replyTo: EmailAddr
subscribers(
first: Int
after: CursorKey
last: Int
before: CursorKey
): MailingListSubscriberConnection! @goField(forceResolver: true)
updates(
first: Int
after: CursorKey
last: Int
before: CursorKey
): MailingListUpdateConnection! @goField(forceResolver: true)
}
type MailingListSubscriber implements Node {
id: ID!
fullName: String!
email: EmailAddr!
status: MailingListSubscriberStatus!
createdAt: Datetime!
updatedAt: Datetime!
}
type MailingListSubscriberConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.MailingListSubscriberConnection"
) {
edges: [MailingListSubscriberEdge!]!
pageInfo: PageInfo!
totalCount: Int! @goField(forceResolver: true)
}
type MailingListSubscriberEdge {
cursor: CursorKey!
node: MailingListSubscriber!
}
type MailingListUpdate implements Node {
id: ID!
title: String!
body: String!
status: MailingListUpdateStatus!
createdAt: Datetime!
updatedAt: Datetime!
}
type MailingListUpdateConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.MailingListUpdateConnection"
) {
edges: [MailingListUpdateEdge!]!
pageInfo: PageInfo!
totalCount: Int! @goField(forceResolver: true)
}
type MailingListUpdateEdge {
cursor: CursorKey!
node: MailingListUpdate!
}
extend type Mutation {
createMailingListUpdate(
input: CreateMailingListUpdateInput!
): CreateMailingListUpdatePayload!
updateMailingListUpdate(
input: UpdateMailingListUpdateInput!
): UpdateMailingListUpdatePayload!
sendMailingListUpdate(
input: SendMailingListUpdateInput!
): SendMailingListUpdatePayload!
deleteMailingListUpdate(
input: DeleteMailingListUpdateInput!
): DeleteMailingListUpdatePayload!
updateMailingList(
input: UpdateMailingListInput!
): UpdateMailingListPayload!
createMailingListSubscriber(
input: CreateMailingListSubscriberInput!
): CreateMailingListSubscriberPayload!
deleteMailingListSubscriber(
input: DeleteMailingListSubscriberInput!
): DeleteMailingListSubscriberPayload!
}
input UpdateMailingListInput {
id: ID!
replyTo: EmailAddr
}
type UpdateMailingListPayload {
mailingList: MailingList!
}
input CreateMailingListUpdateInput {
mailingListId: ID!
title: String!
body: String!
}
input UpdateMailingListUpdateInput {
id: ID!
title: String
body: String
}
input SendMailingListUpdateInput {
id: ID!
}
input DeleteMailingListUpdateInput {
id: ID!
}
input CreateMailingListSubscriberInput {
mailingListId: ID!
fullName: String!
email: EmailAddr!
confirmed: Boolean
}
input DeleteMailingListSubscriberInput {
id: ID!
}
type CreateMailingListUpdatePayload {
mailingListUpdate: MailingListUpdate!
}
type UpdateMailingListUpdatePayload {
mailingListUpdate: MailingListUpdate!
}
type SendMailingListUpdatePayload {
mailingListUpdate: MailingListUpdate!
}
type DeleteMailingListUpdatePayload {
deletedMailingListUpdateId: ID!
}
type CreateMailingListSubscriberPayload {
mailingListSubscriberEdge: MailingListSubscriberEdge!
}
type DeleteMailingListSubscriberPayload {
deletedMailingListSubscriberId: ID!
}

View File

@@ -0,0 +1,200 @@
extend type Organization {
measures(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: MeasureOrder
filter: MeasureFilter
): MeasureConnection! @goField(forceResolver: true)
}
enum MeasureState
@goModel(model: "go.probo.inc/probo/pkg/coredata.MeasureState") {
NOT_STARTED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.MeasureStateNotStarted")
IN_PROGRESS
@goEnum(value: "go.probo.inc/probo/pkg/coredata.MeasureStateInProgress")
NOT_APPLICABLE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MeasureStateNotApplicable"
)
IMPLEMENTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MeasureStateImplemented"
)
UNKNOWN
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MeasureStateUnknown"
)
NOT_IMPLEMENTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MeasureStateNotImplemented"
)
}
enum MeasureOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.MeasureOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MeasureOrderFieldCreatedAt"
)
NAME @goEnum(value: "go.probo.inc/probo/pkg/coredata.MeasureOrderFieldName")
}
input MeasureOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.MeasureOrderBy"
) {
direction: OrderDirection!
field: MeasureOrderField!
}
input MeasureFilter {
query: String
state: MeasureState
category: String
}
type Measure implements Node {
id: ID!
category: String!
name: String!
description: String
state: MeasureState!
evidences(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: EvidenceOrder
): EvidenceConnection! @goField(forceResolver: true)
tasks(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: TaskOrder
): TaskConnection! @goField(forceResolver: true)
risks(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: RiskOrder
filter: RiskFilter
): RiskConnection! @goField(forceResolver: true)
controls(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
documents(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: DocumentOrder
filter: DocumentFilter
): DocumentConnection! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type MeasureConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.MeasureConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [MeasureEdge!]!
pageInfo: PageInfo!
}
type MeasureEdge {
cursor: CursorKey!
node: Measure!
}
extend type Mutation {
createMeasure(input: CreateMeasureInput!): CreateMeasurePayload!
updateMeasure(input: UpdateMeasureInput!): UpdateMeasurePayload!
importMeasure(input: ImportMeasureInput!): ImportMeasurePayload!
deleteMeasure(input: DeleteMeasureInput!): DeleteMeasurePayload!
createMeasureDocumentMapping(
input: CreateMeasureDocumentMappingInput!
): CreateMeasureDocumentMappingPayload!
deleteMeasureDocumentMapping(
input: DeleteMeasureDocumentMappingInput!
): DeleteMeasureDocumentMappingPayload!
}
input CreateMeasureInput {
organizationId: ID!
name: String!
description: String
category: String!
}
input UpdateMeasureInput {
id: ID!
name: String
description: String @goField(omittable: true)
category: String
state: MeasureState
}
input ImportMeasureInput {
organizationId: ID!
file: Upload!
}
input DeleteMeasureInput {
measureId: ID!
}
input CreateMeasureDocumentMappingInput {
measureId: ID!
documentId: ID!
}
input DeleteMeasureDocumentMappingInput {
measureId: ID!
documentId: ID!
}
type CreateMeasurePayload {
measureEdge: MeasureEdge!
}
type UpdateMeasurePayload {
measure: Measure!
}
type ImportMeasurePayload {
measureEdges: [MeasureEdge!]!
}
type DeleteMeasurePayload {
deletedMeasureId: ID!
}
type CreateMeasureDocumentMappingPayload {
measureEdge: MeasureEdge!
documentEdge: DocumentEdge!
}
type DeleteMeasureDocumentMappingPayload {
deletedMeasureId: ID!
deletedDocumentId: ID!
}

View File

@@ -0,0 +1,92 @@
extend type Organization {
meetings(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: MeetingOrder
): MeetingConnection! @goField(forceResolver: true)
}
enum MeetingOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.MeetingOrderField") {
DATE @goEnum(value: "go.probo.inc/probo/pkg/coredata.MeetingOrderFieldDate")
NAME @goEnum(value: "go.probo.inc/probo/pkg/coredata.MeetingOrderFieldName")
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MeetingOrderFieldCreatedAt"
)
}
input MeetingOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.MeetingOrderBy"
) {
direction: OrderDirection!
field: MeetingOrderField!
}
type Meeting implements Node {
id: ID!
name: String!
date: Datetime!
minutes: String
attendees: [Profile!]! @goField(forceResolver: true)
organization: Organization! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type MeetingConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.MeetingConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [MeetingEdge!]!
pageInfo: PageInfo!
}
type MeetingEdge {
cursor: CursorKey!
node: Meeting!
}
extend type Mutation {
createMeeting(input: CreateMeetingInput!): CreateMeetingPayload!
updateMeeting(input: UpdateMeetingInput!): UpdateMeetingPayload!
deleteMeeting(input: DeleteMeetingInput!): DeleteMeetingPayload!
}
input CreateMeetingInput {
organizationId: ID!
name: String!
date: Datetime!
attendeeIds: [ID!]
minutes: String
}
input UpdateMeetingInput {
meetingId: ID!
name: String
date: Datetime
attendeeIds: [ID!]
minutes: String @goField(omittable: true)
}
input DeleteMeetingInput {
meetingId: ID!
}
type CreateMeetingPayload {
meetingEdge: MeetingEdge!
}
type UpdateMeetingPayload {
meeting: Meeting!
}
type DeleteMeetingPayload {
deletedMeetingId: ID!
}

View File

@@ -0,0 +1,152 @@
extend type Organization {
obligations(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ObligationOrder
filter: ObligationFilter = { snapshotId: null }
): ObligationConnection! @goField(forceResolver: true)
}
enum ObligationStatus
@goModel(model: "go.probo.inc/probo/pkg/coredata.ObligationStatus") {
NON_COMPLIANT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ObligationStatusNonCompliant"
)
PARTIALLY_COMPLIANT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ObligationStatusPartiallyCompliant"
)
COMPLIANT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ObligationStatusCompliant"
)
}
enum ObligationType
@goModel(model: "go.probo.inc/probo/pkg/coredata.ObligationType") {
LEGAL @goEnum(value: "go.probo.inc/probo/pkg/coredata.ObligationTypeLegal")
CONTRACTUAL
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ObligationTypeContractual"
)
}
enum ObligationOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.ObligationOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ObligationOrderFieldCreatedAt"
)
LAST_REVIEW_DATE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ObligationOrderFieldLastReviewDate"
)
DUE_DATE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ObligationOrderFieldDueDate"
)
STATUS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ObligationOrderFieldStatus"
)
}
input ObligationOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ObligationOrderBy"
) {
direction: OrderDirection!
field: ObligationOrderField!
}
input ObligationFilter {
snapshotId: ID
}
type Obligation implements Node {
id: ID!
snapshotId: ID
sourceId: ID
organization: Organization! @goField(forceResolver: true)
area: String
source: String
requirement: String
actionsToBeImplemented: String
regulator: String
owner: Profile! @goField(forceResolver: true)
lastReviewDate: Datetime
dueDate: Datetime
status: ObligationStatus!
type: ObligationType!
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type ObligationConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ObligationConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [ObligationEdge!]!
pageInfo: PageInfo!
}
type ObligationEdge {
cursor: CursorKey!
node: Obligation!
}
extend type Mutation {
createObligation(input: CreateObligationInput!): CreateObligationPayload!
updateObligation(input: UpdateObligationInput!): UpdateObligationPayload!
deleteObligation(input: DeleteObligationInput!): DeleteObligationPayload!
}
input CreateObligationInput {
organizationId: ID!
area: String
source: String
requirement: String
actionsToBeImplemented: String
regulator: String
ownerId: ID!
lastReviewDate: Datetime
dueDate: Datetime
status: ObligationStatus!
type: ObligationType!
}
input UpdateObligationInput {
id: ID!
area: String @goField(omittable: true)
source: String @goField(omittable: true)
requirement: String @goField(omittable: true)
actionsToBeImplemented: String @goField(omittable: true)
regulator: String @goField(omittable: true)
ownerId: ID
lastReviewDate: Datetime @goField(omittable: true)
dueDate: Datetime @goField(omittable: true)
status: ObligationStatus
type: ObligationType
}
input DeleteObligationInput {
obligationId: ID!
}
type CreateObligationPayload {
obligationEdge: ObligationEdge!
}
type UpdateObligationPayload {
obligation: Obligation!
}
type DeleteObligationPayload {
deletedObligationId: ID!
}

View File

@@ -0,0 +1,83 @@
type OrganizationContext {
organizationId: ID!
product: String
architecture: String
team: String
processes: String
customers: String
}
enum ProfileState
@goModel(model: "go.probo.inc/probo/pkg/coredata.ProfileState") {
ACTIVE @goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStateActive")
INACTIVE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ProfileStateInactive")
}
type Profile implements Node {
id: ID!
fullName: String!
emailAddress: EmailAddr!
state: ProfileState!
additionalEmailAddresses: [EmailAddr!]!
kind: String
position: String
contractStartDate: Datetime
contractEndDate: Datetime
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
enum ProfileOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.MembershipProfileOrderField") {
FULL_NAME
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MembershipProfileOrderFieldFullName"
)
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MembershipProfileOrderFieldCreatedAt"
)
KIND @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipProfileOrderFieldKind")
}
input ProfileOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ProfileOrderBy"
) {
direction: OrderDirection!
field: ProfileOrderField!
}
input ProfileFilter {
excludeContractEnded: Boolean
}
type ProfileConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ProfileConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [ProfileEdge!]!
pageInfo: PageInfo!
}
type ProfileEdge {
cursor: CursorKey!
node: Profile!
}
input UpdateOrganizationContextInput {
organizationId: ID!
product: String @goField(omittable: true)
architecture: String @goField(omittable: true)
team: String @goField(omittable: true)
processes: String @goField(omittable: true)
customers: String @goField(omittable: true)
}
type UpdateOrganizationContextPayload {
context: OrganizationContext!
}

View File

@@ -0,0 +1,298 @@
extend type Organization {
processingActivities(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ProcessingActivityOrder
filter: ProcessingActivityFilter = { snapshotId: null }
): ProcessingActivityConnection! @goField(forceResolver: true)
}
enum ProcessingActivitySpecialOrCriminalDatum
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ProcessingActivitySpecialOrCriminalDatum"
) {
YES
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivitySpecialOrCriminalDatumYes"
)
NO
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivitySpecialOrCriminalDatumNo"
)
POSSIBLE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivitySpecialOrCriminalDatumPossible"
)
}
enum ProcessingActivityLawfulBasis
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ProcessingActivityLawfulBasis"
) {
LEGITIMATE_INTEREST
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityLawfulBasisLegitimateInterest"
)
CONSENT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityLawfulBasisConsent"
)
CONTRACTUAL_NECESSITY
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityLawfulBasisContractualNecessity"
)
LEGAL_OBLIGATION
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityLawfulBasisLegalObligation"
)
VITAL_INTERESTS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityLawfulBasisVitalInterests"
)
PUBLIC_TASK
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityLawfulBasisPublicTask"
)
}
enum ProcessingActivityTransferSafeguard
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ProcessingActivityTransferSafeguard"
) {
STANDARD_CONTRACTUAL_CLAUSES
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityTransferSafeguardStandardContractualClauses"
)
BINDING_CORPORATE_RULES
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityTransferSafeguardBindingCorporateRules"
)
ADEQUACY_DECISION
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityTransferSafeguardAdequacyDecision"
)
DEROGATIONS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityTransferSafeguardDerogations"
)
CODES_OF_CONDUCT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityTransferSafeguardCodesOfConduct"
)
CERTIFICATION_MECHANISMS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityTransferSafeguardCertificationMechanisms"
)
}
enum ProcessingActivityDataProtectionImpactAssessment
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ProcessingActivityDataProtectionImpactAssessment"
) {
NEEDED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityDataProtectionImpactAssessmentNeeded"
)
NOT_NEEDED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityDataProtectionImpactAssessmentNotNeeded"
)
}
enum ProcessingActivityTransferImpactAssessment
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ProcessingActivityTransferImpactAssessment"
) {
NEEDED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityTransferImpactAssessmentNeeded"
)
NOT_NEEDED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityTransferImpactAssessmentNotNeeded"
)
}
enum ProcessingActivityRole
@goModel(model: "go.probo.inc/probo/pkg/coredata.ProcessingActivityRole") {
CONTROLLER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityRoleController"
)
PROCESSOR
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityRoleProcessor"
)
}
enum ProcessingActivityOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ProcessingActivityOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityOrderFieldCreatedAt"
)
NAME
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityOrderFieldName"
)
}
input ProcessingActivityOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ProcessingActivityOrderBy"
) {
direction: OrderDirection!
field: ProcessingActivityOrderField!
}
input ProcessingActivityFilter {
snapshotId: ID
}
type ProcessingActivity implements Node {
id: ID!
snapshotId: ID
sourceId: ID
organization: Organization! @goField(forceResolver: true)
name: String!
purpose: String
dataSubjectCategory: String
personalDataCategory: String
specialOrCriminalData: ProcessingActivitySpecialOrCriminalDatum!
consentEvidenceLink: String
lawfulBasis: ProcessingActivityLawfulBasis!
recipients: String
location: String
internationalTransfers: Boolean!
transferSafeguards: ProcessingActivityTransferSafeguard
retentionPeriod: String
securityMeasures: String
dataProtectionImpactAssessmentNeeded: ProcessingActivityDataProtectionImpactAssessment!
transferImpactAssessmentNeeded: ProcessingActivityTransferImpactAssessment!
lastReviewDate: Datetime
nextReviewDate: Datetime
role: ProcessingActivityRole!
dataProtectionOfficer: Profile @goField(forceResolver: true)
vendors(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: VendorOrder
): VendorConnection! @goField(forceResolver: true)
dataProtectionImpactAssessment: DataProtectionImpactAssessment
@goField(forceResolver: true)
transferImpactAssessment: TransferImpactAssessment
@goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type ProcessingActivityConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ProcessingActivityConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [ProcessingActivityEdge!]!
pageInfo: PageInfo!
}
type ProcessingActivityEdge {
cursor: CursorKey!
node: ProcessingActivity!
}
extend type Mutation {
createProcessingActivity(
input: CreateProcessingActivityInput!
): CreateProcessingActivityPayload!
updateProcessingActivity(
input: UpdateProcessingActivityInput!
): UpdateProcessingActivityPayload!
deleteProcessingActivity(
input: DeleteProcessingActivityInput!
): DeleteProcessingActivityPayload!
exportProcessingActivitiesPDF(
input: ExportProcessingActivitiesPDFInput!
): ExportProcessingActivitiesPDFPayload!
}
input CreateProcessingActivityInput {
organizationId: ID!
name: String!
purpose: String
dataSubjectCategory: String
personalDataCategory: String
specialOrCriminalData: ProcessingActivitySpecialOrCriminalDatum!
consentEvidenceLink: String
lawfulBasis: ProcessingActivityLawfulBasis!
recipients: String
location: String
internationalTransfers: Boolean!
transferSafeguards: ProcessingActivityTransferSafeguard
retentionPeriod: String
securityMeasures: String
dataProtectionImpactAssessmentNeeded: ProcessingActivityDataProtectionImpactAssessment!
transferImpactAssessmentNeeded: ProcessingActivityTransferImpactAssessment!
lastReviewDate: Datetime
nextReviewDate: Datetime
role: ProcessingActivityRole!
dataProtectionOfficerId: ID
vendorIds: [ID!]
}
input UpdateProcessingActivityInput {
id: ID!
name: String
purpose: String @goField(omittable: true)
dataSubjectCategory: String @goField(omittable: true)
personalDataCategory: String @goField(omittable: true)
specialOrCriminalData: ProcessingActivitySpecialOrCriminalDatum
consentEvidenceLink: String
lawfulBasis: ProcessingActivityLawfulBasis
recipients: String @goField(omittable: true)
location: String @goField(omittable: true)
internationalTransfers: Boolean
transferSafeguards: ProcessingActivityTransferSafeguard
@goField(omittable: true)
retentionPeriod: String @goField(omittable: true)
securityMeasures: String @goField(omittable: true)
dataProtectionImpactAssessmentNeeded: ProcessingActivityDataProtectionImpactAssessment
transferImpactAssessmentNeeded: ProcessingActivityTransferImpactAssessment
lastReviewDate: Datetime @goField(omittable: true)
nextReviewDate: Datetime @goField(omittable: true)
role: ProcessingActivityRole
dataProtectionOfficerId: ID @goField(omittable: true)
vendorIds: [ID!]
}
input DeleteProcessingActivityInput {
processingActivityId: ID!
}
input ExportProcessingActivitiesPDFInput {
organizationId: ID!
filter: ProcessingActivityFilter
}
type CreateProcessingActivityPayload {
processingActivityEdge: ProcessingActivityEdge!
}
type UpdateProcessingActivityPayload {
processingActivity: ProcessingActivity!
}
type DeleteProcessingActivityPayload {
deletedProcessingActivityId: ID!
}
type ExportProcessingActivitiesPDFPayload {
data: String!
}

View File

@@ -0,0 +1,145 @@
extend type Organization {
rightsRequests(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: RightsRequestOrder
): RightsRequestConnection! @goField(forceResolver: true)
}
enum RightsRequestType
@goModel(model: "go.probo.inc/probo/pkg/coredata.RightsRequestType") {
ACCESS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypeAccess"
)
DELETION
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypeDeletion"
)
PORTABILITY
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypePortability"
)
}
enum RightsRequestState
@goModel(model: "go.probo.inc/probo/pkg/coredata.RightsRequestState") {
TODO
@goEnum(value: "go.probo.inc/probo/pkg/coredata.RightsRequestStateTodo")
IN_PROGRESS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.RightsRequestStateInProgress"
)
DONE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.RightsRequestStateDone")
}
enum RightsRequestOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.RightsRequestOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.RightsRequestOrderFieldCreatedAt"
)
DEADLINE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.RightsRequestOrderFieldDeadline"
)
STATE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.RightsRequestOrderFieldState"
)
TYPE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.RightsRequestOrderFieldType"
)
}
input RightsRequestOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.RightsRequestOrderBy"
) {
direction: OrderDirection!
field: RightsRequestOrderField!
}
type RightsRequest implements Node {
id: ID!
organization: Organization! @goField(forceResolver: true)
requestType: RightsRequestType!
requestState: RightsRequestState!
dataSubject: String
contact: String
details: String
deadline: Datetime
actionTaken: String
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type RightsRequestConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.RightsRequestConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [RightsRequestEdge!]!
pageInfo: PageInfo!
}
type RightsRequestEdge {
cursor: CursorKey!
node: RightsRequest!
}
extend type Mutation {
createRightsRequest(
input: CreateRightsRequestInput!
): CreateRightsRequestPayload!
updateRightsRequest(
input: UpdateRightsRequestInput!
): UpdateRightsRequestPayload!
deleteRightsRequest(
input: DeleteRightsRequestInput!
): DeleteRightsRequestPayload!
}
input CreateRightsRequestInput {
organizationId: ID!
requestType: RightsRequestType!
requestState: RightsRequestState!
dataSubject: String
contact: String
details: String
deadline: Datetime
actionTaken: String
}
input UpdateRightsRequestInput {
id: ID!
requestType: RightsRequestType
requestState: RightsRequestState
dataSubject: String @goField(omittable: true)
contact: String @goField(omittable: true)
details: String @goField(omittable: true)
deadline: Datetime @goField(omittable: true)
actionTaken: String @goField(omittable: true)
}
input DeleteRightsRequestInput {
rightsRequestId: ID!
}
type CreateRightsRequestPayload {
rightsRequestEdge: RightsRequestEdge!
}
type UpdateRightsRequestPayload {
rightsRequest: RightsRequest!
}
type DeleteRightsRequestPayload {
deletedRightsRequestId: ID!
}

View File

@@ -0,0 +1,270 @@
extend type Organization {
risks(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: RiskOrder
filter: RiskFilter = { snapshotId: null }
): RiskConnection! @goField(forceResolver: true)
}
enum RiskTreatment
@goModel(model: "go.probo.inc/probo/pkg/coredata.RiskTreatment") {
MITIGATED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.RiskTreatmentMitigated")
ACCEPTED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.RiskTreatmentAccepted")
AVOIDED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.RiskTreatmentAvoided")
TRANSFERRED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.RiskTreatmentTransferred"
)
}
enum RiskOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.RiskOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.RiskOrderFieldCreatedAt"
)
UPDATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.RiskOrderFieldUpdatedAt"
)
NAME @goEnum(value: "go.probo.inc/probo/pkg/coredata.RiskOrderFieldName")
CATEGORY
@goEnum(value: "go.probo.inc/probo/pkg/coredata.RiskOrderFieldCategory")
TREATMENT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.RiskOrderFieldTreatment"
)
INHERENT_RISK_SCORE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.RiskOrderFieldInherentRiskScore"
)
RESIDUAL_RISK_SCORE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.RiskOrderFieldResidualRiskScore"
)
OWNER_FULL_NAME
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.RiskOrderFieldOwnerFullName"
)
}
input RiskOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.RiskOrderBy"
) {
direction: OrderDirection!
field: RiskOrderField!
}
input RiskFilter {
query: String
snapshotId: ID
}
type Risk implements Node {
id: ID!
snapshotId: ID
name: String!
description: String
category: String!
treatment: RiskTreatment!
inherentLikelihood: Int!
inherentImpact: Int!
inherentRiskScore: Int!
residualLikelihood: Int!
residualImpact: Int!
residualRiskScore: Int!
note: String!
owner: Profile @goField(forceResolver: true)
organization: Organization! @goField(forceResolver: true)
measures(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: MeasureOrder
filter: MeasureFilter
): MeasureConnection! @goField(forceResolver: true)
documents(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: DocumentOrder
filter: DocumentFilter
): DocumentConnection! @goField(forceResolver: true)
controls(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
obligations(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ObligationOrder
filter: ObligationFilter
): ObligationConnection! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type RiskConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.RiskConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [RiskEdge!]!
pageInfo: PageInfo!
}
type RiskEdge {
cursor: CursorKey!
node: Risk!
}
extend type Mutation {
createRisk(input: CreateRiskInput!): CreateRiskPayload!
updateRisk(input: UpdateRiskInput!): UpdateRiskPayload!
deleteRisk(input: DeleteRiskInput!): DeleteRiskPayload!
createRiskMeasureMapping(
input: CreateRiskMeasureMappingInput!
): CreateRiskMeasureMappingPayload!
deleteRiskMeasureMapping(
input: DeleteRiskMeasureMappingInput!
): DeleteRiskMeasureMappingPayload!
createRiskDocumentMapping(
input: CreateRiskDocumentMappingInput!
): CreateRiskDocumentMappingPayload!
deleteRiskDocumentMapping(
input: DeleteRiskDocumentMappingInput!
): DeleteRiskDocumentMappingPayload!
createRiskObligationMapping(
input: CreateRiskObligationMappingInput!
): CreateRiskObligationMappingPayload!
deleteRiskObligationMapping(
input: DeleteRiskObligationMappingInput!
): DeleteRiskObligationMappingPayload!
}
input CreateRiskInput {
organizationId: ID!
name: String!
description: String
category: String!
ownerId: ID
treatment: RiskTreatment!
inherentLikelihood: Int!
inherentImpact: Int!
residualLikelihood: Int
residualImpact: Int
note: String
}
input UpdateRiskInput {
id: ID!
name: String
description: String @goField(omittable: true)
category: String
ownerId: ID @goField(omittable: true)
treatment: RiskTreatment
inherentLikelihood: Int
inherentImpact: Int
residualLikelihood: Int
residualImpact: Int
note: String
}
input DeleteRiskInput {
riskId: ID!
}
input CreateRiskMeasureMappingInput {
riskId: ID!
measureId: ID!
}
input DeleteRiskMeasureMappingInput {
riskId: ID!
measureId: ID!
}
input CreateRiskDocumentMappingInput {
riskId: ID!
documentId: ID!
}
input DeleteRiskDocumentMappingInput {
riskId: ID!
documentId: ID!
}
input CreateRiskObligationMappingInput {
riskId: ID!
obligationId: ID!
}
input DeleteRiskObligationMappingInput {
riskId: ID!
obligationId: ID!
}
type CreateRiskPayload {
riskEdge: RiskEdge!
}
type UpdateRiskPayload {
risk: Risk!
}
type DeleteRiskPayload {
deletedRiskId: ID!
}
type CreateRiskMeasureMappingPayload {
riskEdge: RiskEdge!
measureEdge: MeasureEdge!
}
type DeleteRiskMeasureMappingPayload {
deletedMeasureId: ID!
deletedRiskId: ID!
}
type CreateRiskDocumentMappingPayload {
riskEdge: RiskEdge!
documentEdge: DocumentEdge!
}
type DeleteRiskDocumentMappingPayload {
deletedRiskId: ID!
deletedDocumentId: ID!
}
type CreateRiskObligationMappingPayload {
riskEdge: RiskEdge!
obligationEdge: ObligationEdge!
}
type DeleteRiskObligationMappingPayload {
deletedRiskId: ID!
deletedObligationId: ID!
}

View File

@@ -0,0 +1,113 @@
extend type Organization {
snapshots(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: SnapshotOrder
): SnapshotConnection! @goField(forceResolver: true)
}
enum SnapshotsType
@goModel(model: "go.probo.inc/probo/pkg/coredata.SnapshotsType") {
RISKS @goEnum(value: "go.probo.inc/probo/pkg/coredata.SnapshotsTypeRisks")
VENDORS
@goEnum(value: "go.probo.inc/probo/pkg/coredata.SnapshotsTypeVendors")
ASSETS @goEnum(value: "go.probo.inc/probo/pkg/coredata.SnapshotsTypeAssets")
DATA @goEnum(value: "go.probo.inc/probo/pkg/coredata.SnapshotsTypeData")
FINDINGS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.SnapshotsTypeFindings"
)
OBLIGATIONS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.SnapshotsTypeObligations"
)
PROCESSING_ACTIVITIES
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.SnapshotsTypeProcessingActivities"
)
STATEMENTS_OF_APPLICABILITY
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.SnapshotsTypeStatementsOfApplicability"
)
}
enum SnapshotOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.SnapshotOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.SnapshotOrderFieldCreatedAt"
)
NAME
@goEnum(value: "go.probo.inc/probo/pkg/coredata.SnapshotOrderFieldName")
TYPE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.SnapshotOrderFieldType")
}
input SnapshotOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.SnapshotOrderBy"
) {
direction: OrderDirection!
field: SnapshotOrderField!
}
type Snapshot implements Node {
id: ID!
organization: Organization! @goField(forceResolver: true)
name: String!
description: String
type: SnapshotsType!
controls(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
createdAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type SnapshotConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.SnapshotConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [SnapshotEdge!]!
pageInfo: PageInfo!
}
type SnapshotEdge {
cursor: CursorKey!
node: Snapshot!
}
extend type Mutation {
createSnapshot(input: CreateSnapshotInput!): CreateSnapshotPayload!
deleteSnapshot(input: DeleteSnapshotInput!): DeleteSnapshotPayload!
}
input CreateSnapshotInput {
organizationId: ID!
name: String!
description: String
type: SnapshotsType!
}
input DeleteSnapshotInput {
snapshotId: ID!
}
type CreateSnapshotPayload {
snapshotEdge: SnapshotEdge!
}
type DeleteSnapshotPayload {
deletedSnapshotId: ID!
}

View File

@@ -0,0 +1,137 @@
extend type Organization {
tasks(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: TaskOrder
): TaskConnection! @goField(forceResolver: true)
}
enum TaskState @goModel(model: "go.probo.inc/probo/pkg/coredata.TaskState") {
TODO @goEnum(value: "go.probo.inc/probo/pkg/coredata.TaskStateTodo")
IN_PROGRESS @goEnum(value: "go.probo.inc/probo/pkg/coredata.TaskStateInProgress")
DONE @goEnum(value: "go.probo.inc/probo/pkg/coredata.TaskStateDone")
}
enum TaskPriority
@goModel(model: "go.probo.inc/probo/pkg/coredata.TaskPriority") {
URGENT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TaskPriorityUrgent"
)
HIGH
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TaskPriorityHigh"
)
MEDIUM
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TaskPriorityMedium"
)
LOW
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TaskPriorityLow"
)
}
enum TaskOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.TaskOrderField") {
PRIORITY_RANK
CREATED_AT
}
input TaskOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TaskOrderBy"
) {
direction: OrderDirection!
field: TaskOrderField!
}
type Task implements Node {
id: ID!
name: String!
description: String
state: TaskState!
priority: TaskPriority!
rank: Int!
timeEstimate: Duration
deadline: Datetime
assignedTo: Profile @goField(forceResolver: true)
organization: Organization! @goField(forceResolver: true)
measure: Measure @goField(forceResolver: true)
evidences(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: EvidenceOrder
): EvidenceConnection! @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type TaskConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TaskConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [TaskEdge!]!
pageInfo: PageInfo!
}
type TaskEdge {
cursor: CursorKey!
node: Task!
}
extend type Mutation {
createTask(input: CreateTaskInput!): CreateTaskPayload!
updateTask(input: UpdateTaskInput!): UpdateTaskPayload!
deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
}
input CreateTaskInput {
organizationId: ID!
measureId: ID
name: String!
description: String
priority: TaskPriority!
timeEstimate: Duration
assignedToId: ID
deadline: Datetime
}
input UpdateTaskInput {
taskId: ID!
name: String
description: String @goField(omittable: true)
state: TaskState
priority: TaskPriority
rank: Int
timeEstimate: Duration @goField(omittable: true)
deadline: Datetime @goField(omittable: true)
assignedToId: ID @goField(omittable: true)
measureId: ID @goField(omittable: true)
}
input DeleteTaskInput {
taskId: ID!
}
type CreateTaskPayload {
taskEdge: TaskEdge!
}
type UpdateTaskPayload {
task: Task!
}
type DeleteTaskPayload {
deletedTaskId: ID!
}

View File

@@ -0,0 +1,771 @@
extend type Organization {
trustCenter: TrustCenter @goField(forceResolver: true)
customDomain: CustomDomain @goField(forceResolver: true)
trustCenterFiles(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: TrustCenterFileOrder
): TrustCenterFileConnection! @goField(forceResolver: true)
}
enum TrustCenterVisibility
@goModel(model: "go.probo.inc/probo/pkg/coredata.TrustCenterVisibility") {
NONE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterVisibilityNone"
)
PRIVATE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterVisibilityPrivate"
)
PUBLIC
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterVisibilityPublic"
)
}
enum SearchEngineIndexing
@goModel(
model: "go.probo.inc/probo/pkg/coredata.SearchEngineIndexing"
) {
INDEXABLE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.SearchEngineIndexingIndexable"
)
NOT_INDEXABLE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.SearchEngineIndexingNotIndexable"
)
}
enum TrustCenterDocumentAccessStatus
@goModel(
model: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatus"
) {
REQUESTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusRequested"
)
GRANTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusGranted"
)
REJECTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusRejected"
)
REVOKED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusRevoked"
)
}
enum TrustCenterAccessOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.TrustCenterAccessOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterAccessOrderFieldCreatedAt"
)
}
enum TrustCenterAccessState
@goModel(
model: "go.probo.inc/probo/pkg/coredata.TrustCenterAccessState"
) {
ACTIVE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterAccessStateActive"
)
INACTIVE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterAccessStateInactive"
)
}
enum TrustCenterDocumentAccessOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessOrderFieldCreatedAt"
)
}
enum TrustCenterReferenceOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.TrustCenterReferenceOrderField"
) {
RANK
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterReferenceOrderFieldRank"
)
NAME
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterReferenceOrderFieldName"
)
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterReferenceOrderFieldCreatedAt"
)
UPDATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterReferenceOrderFieldUpdatedAt"
)
}
enum ComplianceExternalURLOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ComplianceExternalURLOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ComplianceExternalURLOrderFieldCreatedAt"
)
RANK
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ComplianceExternalURLOrderFieldRank"
)
}
enum ComplianceFrameworkOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ComplianceFrameworkOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ComplianceFrameworkOrderFieldCreatedAt"
)
RANK
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ComplianceFrameworkOrderFieldRank"
)
}
enum ComplianceFrameworkVisibility
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ComplianceFrameworkVisibility"
) {
NONE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ComplianceFrameworkVisibilityNone"
)
PUBLIC
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ComplianceFrameworkVisibilityPublic"
)
}
enum TrustCenterFileOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.TrustCenterFileOrderField"
) {
NAME
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterFileOrderFieldName"
)
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterFileOrderFieldCreatedAt"
)
UPDATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterFileOrderFieldUpdatedAt"
)
}
enum SSLStatus
@goModel(model: "go.probo.inc/probo/pkg/coredata.CustomDomainSSLStatus") {
PENDING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.CustomDomainSSLStatusPending"
)
PROVISIONING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.CustomDomainSSLStatusProvisioning"
)
ACTIVE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.CustomDomainSSLStatusActive"
)
RENEWING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.CustomDomainSSLStatusRenewing"
)
EXPIRED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.CustomDomainSSLStatusExpired"
)
FAILED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.CustomDomainSSLStatusFailed"
)
}
input TrustCenterAccessOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TrustCenterAccessOrderBy"
) {
direction: OrderDirection!
field: TrustCenterAccessOrderField!
}
input TrustCenterDocumentAccessOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TrustCenterDocumentAccessOrderBy"
) {
direction: OrderDirection!
field: TrustCenterDocumentAccessOrderField!
}
input TrustCenterReferenceOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TrustCenterReferenceOrderBy"
) {
direction: OrderDirection!
field: TrustCenterReferenceOrderField!
}
input TrustCenterFileOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TrustCenterFileOrderBy"
) {
direction: OrderDirection!
field: TrustCenterFileOrderField!
}
input ComplianceExternalURLOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ComplianceExternalURLOrderBy"
) {
direction: OrderDirection!
field: ComplianceExternalURLOrderField!
}
input ComplianceFrameworkOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ComplianceFrameworkOrderBy"
) {
direction: OrderDirection!
field: ComplianceFrameworkOrderField!
}
type TrustCenter implements Node
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TrustCenter"
) {
id: ID!
active: Boolean!
searchEngineIndexing: SearchEngineIndexing!
logoFileUrl: String @goField(forceResolver: true)
darkLogoFileUrl: String @goField(forceResolver: true)
ndaFileName: String
ndaFileUrl: String @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
organization: Organization! @goField(forceResolver: true)
accesses(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: TrustCenterAccessOrder
): TrustCenterAccessConnection! @goField(forceResolver: true)
references(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: TrustCenterReferenceOrder
): TrustCenterReferenceConnection! @goField(forceResolver: true)
complianceFrameworks(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ComplianceFrameworkOrder
): ComplianceFrameworkConnection! @goField(forceResolver: true)
externalUrls(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: ComplianceExternalURLOrder
): ComplianceExternalURLConnection! @goField(forceResolver: true)
mailingList: MailingList @goField(forceResolver: true)
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type TrustCenterConnection {
edges: [TrustCenterEdge!]!
pageInfo: PageInfo!
}
type TrustCenterEdge {
cursor: CursorKey!
node: TrustCenter!
}
type TrustCenterAccess implements Node {
id: ID!
ndaSignature: ElectronicSignature @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
pendingRequestCount: Int! @goField(forceResolver: true)
activeCount: Int! @goField(forceResolver: true)
organizationId: ID!
identityID: ID!
profile: Profile! @goField(forceResolver: true)
availableDocumentAccesses(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: TrustCenterDocumentAccessOrder
): TrustCenterDocumentAccessConnection! @goField(forceResolver: true)
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type TrustCenterDocumentAccess
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TrustCenterDocumentAccess"
) {
id: ID!
status: TrustCenterDocumentAccessStatus!
document: Document @goField(forceResolver: true)
report: Report @goField(forceResolver: true)
trustCenterFile: TrustCenterFile @goField(forceResolver: true)
}
type TrustCenterDocumentAccessConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TrustCenterDocumentAccessConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [TrustCenterDocumentAccessEdge!]!
pageInfo: PageInfo!
}
type TrustCenterDocumentAccessEdge {
cursor: CursorKey!
node: TrustCenterDocumentAccess!
}
type TrustCenterAccessConnection {
edges: [TrustCenterAccessEdge!]!
pageInfo: PageInfo!
}
type TrustCenterAccessEdge {
cursor: CursorKey!
node: TrustCenterAccess!
}
type TrustCenterReference implements Node {
id: ID!
name: String!
description: String
websiteUrl: String!
logoUrl: String! @goField(forceResolver: true)
rank: Int!
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type TrustCenterReferenceConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TrustCenterReferenceConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [TrustCenterReferenceEdge!]!
pageInfo: PageInfo!
}
type TrustCenterReferenceEdge {
cursor: CursorKey!
node: TrustCenterReference!
}
type ComplianceFramework implements Node
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ComplianceFramework"
) {
id: ID!
framework: Framework! @goField(forceResolver: true)
rank: Int!
visibility: ComplianceFrameworkVisibility!
createdAt: Datetime!
updatedAt: Datetime!
}
type ComplianceFrameworkConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ComplianceFrameworkConnection"
) {
edges: [ComplianceFrameworkEdge!]!
pageInfo: PageInfo!
}
type ComplianceFrameworkEdge {
cursor: CursorKey!
node: ComplianceFramework!
}
type ComplianceExternalURL implements Node {
id: ID!
name: String!
url: String!
rank: Int!
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type ComplianceExternalURLConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.ComplianceExternalURLConnection"
) {
edges: [ComplianceExternalURLEdge!]!
pageInfo: PageInfo!
}
type ComplianceExternalURLEdge {
cursor: CursorKey!
node: ComplianceExternalURL!
}
type TrustCenterFile implements Node {
id: ID!
name: String!
category: String!
fileUrl: String! @goField(forceResolver: true)
trustCenterVisibility: TrustCenterVisibility!
createdAt: Datetime!
updatedAt: Datetime!
organization: Organization! @goField(forceResolver: true)
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type TrustCenterFileConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TrustCenterFileConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [TrustCenterFileEdge!]!
pageInfo: PageInfo!
}
type TrustCenterFileEdge {
cursor: CursorKey!
node: TrustCenterFile!
}
type CustomDomain implements Node {
id: ID!
organization: Organization!
domain: String!
sslStatus: SSLStatus!
sslExpiresAt: Datetime
dnsRecords: [DNSRecordInstruction!]!
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type DNSRecordInstruction {
type: String!
name: String!
value: String!
ttl: Int!
purpose: String!
}
extend type Mutation {
updateTrustCenter(input: UpdateTrustCenterInput!): UpdateTrustCenterPayload!
uploadTrustCenterNDA(
input: UploadTrustCenterNDAInput!
): UploadTrustCenterNDAPayload!
deleteTrustCenterNDA(
input: DeleteTrustCenterNDAInput!
): DeleteTrustCenterNDAPayload!
updateTrustCenterBrand(
input: UpdateTrustCenterBrandInput!
): UpdateTrustCenterBrandPayload!
updateTrustCenterAccess(
input: UpdateTrustCenterAccessInput!
): UpdateTrustCenterAccessPayload!
deleteTrustCenterAccess(
input: DeleteTrustCenterAccessInput!
): DeleteTrustCenterAccessPayload!
createTrustCenterReference(
input: CreateTrustCenterReferenceInput!
): CreateTrustCenterReferencePayload!
updateTrustCenterReference(
input: UpdateTrustCenterReferenceInput!
): UpdateTrustCenterReferencePayload!
deleteTrustCenterReference(
input: DeleteTrustCenterReferenceInput!
): DeleteTrustCenterReferencePayload!
createComplianceFramework(
input: CreateComplianceFrameworkInput!
): CreateComplianceFrameworkPayload!
updateComplianceFramework(
input: UpdateComplianceFrameworkInput!
): UpdateComplianceFrameworkPayload!
deleteComplianceFramework(
input: DeleteComplianceFrameworkInput!
): DeleteComplianceFrameworkPayload!
createComplianceExternalURL(
input: CreateComplianceExternalURLInput!
): CreateComplianceExternalURLPayload!
updateComplianceExternalURL(
input: UpdateComplianceExternalURLInput!
): UpdateComplianceExternalURLPayload!
deleteComplianceExternalURL(
input: DeleteComplianceExternalURLInput!
): DeleteComplianceExternalURLPayload!
createTrustCenterFile(
input: CreateTrustCenterFileInput!
): CreateTrustCenterFilePayload!
updateTrustCenterFile(
input: UpdateTrustCenterFileInput!
): UpdateTrustCenterFilePayload!
getTrustCenterFile(
input: GetTrustCenterFileInput!
): GetTrustCenterFilePayload!
deleteTrustCenterFile(
input: DeleteTrustCenterFileInput!
): DeleteTrustCenterFilePayload!
createCustomDomain(
input: CreateCustomDomainInput!
): CreateCustomDomainPayload!
deleteCustomDomain(
input: DeleteCustomDomainInput!
): DeleteCustomDomainPayload!
}
input UpdateTrustCenterInput {
trustCenterId: ID!
active: Boolean
searchEngineIndexing: SearchEngineIndexing
}
input UploadTrustCenterNDAInput {
trustCenterId: ID!
fileName: String!
file: Upload!
}
input DeleteTrustCenterNDAInput {
trustCenterId: ID!
}
input UpdateTrustCenterBrandInput {
trustCenterId: ID!
logoFile: Upload @goField(omittable: true)
darkLogoFile: Upload @goField(omittable: true)
}
input TrustCenterDocumentAccessInput {
id: ID!
status: TrustCenterDocumentAccessStatus!
}
input UpdateTrustCenterAccessInput {
id: ID!
name: String
state: TrustCenterAccessState
documents: [TrustCenterDocumentAccessInput!]
reports: [TrustCenterDocumentAccessInput!]
trustCenterFiles: [TrustCenterDocumentAccessInput!]
}
input DeleteTrustCenterAccessInput {
id: ID!
}
input CreateTrustCenterReferenceInput {
trustCenterId: ID!
name: String!
description: String
websiteUrl: String!
logoFile: Upload!
}
input UpdateTrustCenterReferenceInput {
id: ID!
name: String
description: String @goField(omittable: true)
websiteUrl: String
logoFile: Upload
rank: Int
}
input DeleteTrustCenterReferenceInput {
id: ID!
}
input CreateComplianceFrameworkInput {
trustCenterId: ID!
frameworkId: ID!
}
input UpdateComplianceFrameworkInput {
id: ID!
rank: Int!
}
input DeleteComplianceFrameworkInput {
id: ID!
}
input CreateComplianceExternalURLInput {
trustCenterId: ID!
name: String!
url: String!
}
input UpdateComplianceExternalURLInput {
id: ID!
name: String!
url: String!
rank: Int
}
input DeleteComplianceExternalURLInput {
id: ID!
}
input CreateTrustCenterFileInput {
organizationId: ID!
name: String!
category: String!
file: Upload!
trustCenterVisibility: TrustCenterVisibility!
}
input UpdateTrustCenterFileInput {
id: ID!
name: String
category: String
trustCenterVisibility: TrustCenterVisibility
}
input GetTrustCenterFileInput {
id: ID!
}
input DeleteTrustCenterFileInput {
id: ID!
}
input CreateCustomDomainInput {
organizationId: ID!
domain: String!
}
input DeleteCustomDomainInput {
organizationId: ID!
}
type UpdateTrustCenterPayload {
trustCenter: TrustCenter!
}
type UploadTrustCenterNDAPayload {
trustCenter: TrustCenter!
}
type DeleteTrustCenterNDAPayload {
trustCenter: TrustCenter!
}
type UpdateTrustCenterBrandPayload {
trustCenter: TrustCenter!
}
type UpdateTrustCenterAccessPayload {
trustCenterAccess: TrustCenterAccess!
}
type DeleteTrustCenterAccessPayload {
deletedTrustCenterAccessId: ID!
}
type CreateTrustCenterReferencePayload {
trustCenterReferenceEdge: TrustCenterReferenceEdge!
}
type UpdateTrustCenterReferencePayload {
trustCenterReference: TrustCenterReference!
}
type DeleteTrustCenterReferencePayload {
deletedTrustCenterReferenceId: ID!
}
type CreateComplianceFrameworkPayload {
complianceFrameworkEdge: ComplianceFrameworkEdge!
}
type UpdateComplianceFrameworkPayload {
complianceFramework: ComplianceFramework!
}
type DeleteComplianceFrameworkPayload {
deletedComplianceFrameworkId: ID!
}
type CreateComplianceExternalURLPayload {
complianceExternalUrlEdge: ComplianceExternalURLEdge!
}
type UpdateComplianceExternalURLPayload {
complianceExternalUrl: ComplianceExternalURL!
}
type DeleteComplianceExternalURLPayload {
deletedComplianceExternalUrlId: ID!
}
type CreateTrustCenterFilePayload {
trustCenterFileEdge: TrustCenterFileEdge!
}
type UpdateTrustCenterFilePayload {
trustCenterFile: TrustCenterFile!
}
type GetTrustCenterFilePayload {
trustCenterFile: TrustCenterFile!
}
type DeleteTrustCenterFilePayload {
deletedTrustCenterFileId: ID!
}
type CreateCustomDomainPayload {
customDomain: CustomDomain!
}
type DeleteCustomDomainPayload {
deletedCustomDomainId: ID!
}

View File

@@ -0,0 +1,704 @@
extend type Organization {
vendors(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: VendorOrder
filter: VendorFilter = { snapshotId: null }
): VendorConnection! @goField(forceResolver: true)
}
enum VendorCategory
@goModel(model: "go.probo.inc/probo/pkg/coredata.VendorCategory") {
ANALYTICS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryAnalytics"
)
CLOUD_MONITORING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCloudMonitoring"
)
CLOUD_PROVIDER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCloudProvider"
)
COLLABORATION
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCollaboration"
)
CUSTOMER_SUPPORT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCustomerSupport"
)
DATA_STORAGE_AND_PROCESSING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryDataStorageAndProcessing"
)
DOCUMENT_MANAGEMENT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryDocumentManagement"
)
EMPLOYEE_MANAGEMENT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEmployeeManagement"
)
ENGINEERING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEngineering"
)
FINANCE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryFinance")
IDENTITY_PROVIDER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIdentityProvider"
)
IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIT")
MARKETING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryMarketing"
)
OFFICE_OPERATIONS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOfficeOperations"
)
OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOther")
PASSWORD_MANAGEMENT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryPasswordManagement"
)
PRODUCT_AND_DESIGN
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryProductAndDesign"
)
PROFESSIONAL_SERVICES
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryProfessionalServices"
)
RECRUITING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryRecruiting"
)
SALES @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySales")
SECURITY
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySecurity")
VERSION_CONTROL
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryVersionControl"
)
}
enum DataSensitivity
@goModel(model: "go.probo.inc/probo/pkg/coredata.DataSensitivity") {
NONE @goEnum(value: "go.probo.inc/probo/pkg/coredata.DataSensitivityNone")
LOW @goEnum(value: "go.probo.inc/probo/pkg/coredata.DataSensitivityLow")
MEDIUM
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DataSensitivityMedium")
HIGH @goEnum(value: "go.probo.inc/probo/pkg/coredata.DataSensitivityHigh")
CRITICAL
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.DataSensitivityCritical"
)
}
enum BusinessImpact
@goModel(model: "go.probo.inc/probo/pkg/coredata.BusinessImpact") {
LOW @goEnum(value: "go.probo.inc/probo/pkg/coredata.BusinessImpactLow")
MEDIUM
@goEnum(value: "go.probo.inc/probo/pkg/coredata.BusinessImpactMedium")
HIGH @goEnum(value: "go.probo.inc/probo/pkg/coredata.BusinessImpactHigh")
CRITICAL
@goEnum(value: "go.probo.inc/probo/pkg/coredata.BusinessImpactCritical")
}
enum VendorOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.VendorOrderField") {
NAME @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorOrderFieldName")
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorOrderFieldCreatedAt"
)
UPDATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorOrderFieldUpdatedAt"
)
}
enum VendorComplianceReportOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.VendorComplianceReportOrderField"
) {
REPORT_DATE
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorComplianceReportOrderFieldReportDate"
)
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorComplianceReportOrderFieldCreatedAt"
)
}
enum VendorContactOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.VendorContactOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorContactOrderFieldCreatedAt"
)
FULL_NAME
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorContactOrderFieldFullName"
)
EMAIL
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorContactOrderFieldEmail"
)
}
enum VendorServiceOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.VendorServiceOrderField") {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorServiceOrderFieldCreatedAt"
)
NAME
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorServiceOrderFieldName"
)
}
enum VendorRiskAssessmentOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.VendorRiskAssessmentOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorRiskAssessmentOrderFieldCreatedAt"
)
EXPIRES_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorRiskAssessmentOrderFieldExpiresAt"
)
}
input VendorOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.VendorOrderBy"
) {
direction: OrderDirection!
field: VendorOrderField!
}
input VendorComplianceReportOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.VendorComplianceReportOrderBy"
) {
direction: OrderDirection!
field: VendorComplianceReportOrderField!
}
input VendorContactOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.VendorContactOrderBy"
) {
direction: OrderDirection!
field: VendorContactOrderField!
}
input VendorServiceOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.VendorServiceOrderBy"
) {
direction: OrderDirection!
field: VendorServiceOrderField!
}
input VendorRiskAssessmentOrder {
field: VendorRiskAssessmentOrderField!
direction: OrderDirection!
}
input VendorFilter {
snapshotId: ID
}
type Vendor implements Node {
id: ID!
snapshotId: ID
name: String!
category: VendorCategory!
description: String
organization: Organization! @goField(forceResolver: true)
complianceReports(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: VendorComplianceReportOrder
): VendorComplianceReportConnection! @goField(forceResolver: true)
businessAssociateAgreement: VendorBusinessAssociateAgreement
@goField(forceResolver: true)
dataPrivacyAgreement: VendorDataPrivacyAgreement
@goField(forceResolver: true)
contacts(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: VendorContactOrder
): VendorContactConnection! @goField(forceResolver: true)
services(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: VendorServiceOrder
): VendorServiceConnection! @goField(forceResolver: true)
riskAssessments(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: VendorRiskAssessmentOrder
): VendorRiskAssessmentConnection! @goField(forceResolver: true)
businessOwner: Profile @goField(forceResolver: true)
securityOwner: Profile @goField(forceResolver: true)
statusPageUrl: String
termsOfServiceUrl: String
privacyPolicyUrl: String
serviceLevelAgreementUrl: String
dataProcessingAgreementUrl: String
businessAssociateAgreementUrl: String
subprocessorsListUrl: String
certifications: [String!]!
countries: [CountryCode!]!
securityPageUrl: String
trustPageUrl: String
headquarterAddress: String
legalName: String
websiteUrl: String
showOnTrustCenter: Boolean!
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type VendorComplianceReport implements Node {
id: ID!
vendor: Vendor! @goField(forceResolver: true)
reportDate: Datetime!
validUntil: Datetime
reportName: String!
file: File @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type VendorBusinessAssociateAgreement implements Node {
id: ID!
vendor: Vendor! @goField(forceResolver: true)
validFrom: Datetime
validUntil: Datetime
fileName: String!
fileUrl: String! @goField(forceResolver: true)
fileSize: BigInt!
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type VendorContact implements Node {
id: ID!
vendor: Vendor! @goField(forceResolver: true)
fullName: String
email: EmailAddr
phone: String
role: String
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type VendorService implements Node {
id: ID!
vendor: Vendor! @goField(forceResolver: true)
name: String!
description: String
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type VendorDataPrivacyAgreement implements Node {
id: ID!
vendor: Vendor! @goField(forceResolver: true)
validFrom: Datetime
validUntil: Datetime
fileName: String!
fileUrl: String! @goField(forceResolver: true)
fileSize: BigInt!
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type VendorRiskAssessment implements Node {
id: ID!
vendor: Vendor! @goField(forceResolver: true)
expiresAt: Datetime!
dataSensitivity: DataSensitivity!
businessImpact: BusinessImpact!
notes: String
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type VendorConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.VendorConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [VendorEdge!]!
pageInfo: PageInfo!
}
type VendorEdge {
cursor: CursorKey!
node: Vendor!
}
type VendorComplianceReportConnection {
edges: [VendorComplianceReportEdge!]!
pageInfo: PageInfo!
}
type VendorComplianceReportEdge {
cursor: CursorKey!
node: VendorComplianceReport!
}
type VendorContactConnection {
edges: [VendorContactEdge!]!
pageInfo: PageInfo!
}
type VendorContactEdge {
cursor: CursorKey!
node: VendorContact!
}
type VendorServiceConnection {
edges: [VendorServiceEdge!]!
pageInfo: PageInfo!
}
type VendorServiceEdge {
cursor: CursorKey!
node: VendorService!
}
type VendorRiskAssessmentConnection {
edges: [VendorRiskAssessmentEdge!]!
pageInfo: PageInfo!
}
type VendorRiskAssessmentEdge {
cursor: CursorKey!
node: VendorRiskAssessment!
}
extend type Mutation {
createVendor(input: CreateVendorInput!): CreateVendorPayload!
updateVendor(input: UpdateVendorInput!): UpdateVendorPayload!
deleteVendor(input: DeleteVendorInput!): DeleteVendorPayload!
createVendorContact(
input: CreateVendorContactInput!
): CreateVendorContactPayload!
updateVendorContact(
input: UpdateVendorContactInput!
): UpdateVendorContactPayload!
deleteVendorContact(
input: DeleteVendorContactInput!
): DeleteVendorContactPayload!
createVendorService(
input: CreateVendorServiceInput!
): CreateVendorServicePayload!
updateVendorService(
input: UpdateVendorServiceInput!
): UpdateVendorServicePayload!
deleteVendorService(
input: DeleteVendorServiceInput!
): DeleteVendorServicePayload!
uploadVendorComplianceReport(
input: UploadVendorComplianceReportInput!
): UploadVendorComplianceReportPayload!
deleteVendorComplianceReport(
input: DeleteVendorComplianceReportInput!
): DeleteVendorComplianceReportPayload!
uploadVendorBusinessAssociateAgreement(
input: UploadVendorBusinessAssociateAgreementInput!
): UploadVendorBusinessAssociateAgreementPayload!
updateVendorBusinessAssociateAgreement(
input: UpdateVendorBusinessAssociateAgreementInput!
): UpdateVendorBusinessAssociateAgreementPayload!
deleteVendorBusinessAssociateAgreement(
input: DeleteVendorBusinessAssociateAgreementInput!
): DeleteVendorBusinessAssociateAgreementPayload!
uploadVendorDataPrivacyAgreement(
input: UploadVendorDataPrivacyAgreementInput!
): UploadVendorDataPrivacyAgreementPayload!
updateVendorDataPrivacyAgreement(
input: UpdateVendorDataPrivacyAgreementInput!
): UpdateVendorDataPrivacyAgreementPayload!
deleteVendorDataPrivacyAgreement(
input: DeleteVendorDataPrivacyAgreementInput!
): DeleteVendorDataPrivacyAgreementPayload!
createVendorRiskAssessment(
input: CreateVendorRiskAssessmentInput!
): CreateVendorRiskAssessmentPayload!
assessVendor(input: AssessVendorInput!): AssessVendorPayload!
}
input CreateVendorInput {
organizationId: ID!
name: String!
description: String
headquarterAddress: String
legalName: String
websiteUrl: String
privacyPolicyUrl: String
category: VendorCategory
serviceLevelAgreementUrl: String
dataProcessingAgreementUrl: String
businessAssociateAgreementUrl: String
subprocessorsListUrl: String
certifications: [String!]
countries: [CountryCode!]
securityPageUrl: String
trustPageUrl: String
statusPageUrl: String
termsOfServiceUrl: String
businessOwnerId: ID
securityOwnerId: ID
}
input UpdateVendorInput {
id: ID!
name: String
description: String @goField(omittable: true)
statusPageUrl: String @goField(omittable: true)
termsOfServiceUrl: String @goField(omittable: true)
privacyPolicyUrl: String @goField(omittable: true)
serviceLevelAgreementUrl: String @goField(omittable: true)
dataProcessingAgreementUrl: String @goField(omittable: true)
businessAssociateAgreementUrl: String @goField(omittable: true)
subprocessorsListUrl: String @goField(omittable: true)
websiteUrl: String @goField(omittable: true)
legalName: String @goField(omittable: true)
headquarterAddress: String @goField(omittable: true)
category: VendorCategory
certifications: [String!]
countries: [CountryCode!]
securityPageUrl: String @goField(omittable: true)
trustPageUrl: String @goField(omittable: true)
businessOwnerId: ID @goField(omittable: true)
securityOwnerId: ID @goField(omittable: true)
showOnTrustCenter: Boolean
}
input DeleteVendorInput {
vendorId: ID!
}
input CreateVendorContactInput {
vendorId: ID!
fullName: String
email: EmailAddr
phone: String
role: String
}
input UpdateVendorContactInput {
id: ID!
fullName: String @goField(omittable: true)
email: EmailAddr @goField(omittable: true)
phone: String @goField(omittable: true)
role: String @goField(omittable: true)
}
input DeleteVendorContactInput {
vendorContactId: ID!
}
input CreateVendorServiceInput {
vendorId: ID!
name: String!
description: String
url: String
type: String
}
input UpdateVendorServiceInput {
id: ID!
name: String
description: String @goField(omittable: true)
url: String
type: String
}
input DeleteVendorServiceInput {
vendorServiceId: ID!
}
input UploadVendorComplianceReportInput {
vendorId: ID!
reportDate: Datetime!
validUntil: Datetime
reportName: String!
file: Upload!
}
input DeleteVendorComplianceReportInput {
reportId: ID!
}
input UploadVendorBusinessAssociateAgreementInput {
vendorId: ID!
validFrom: Datetime
validUntil: Datetime
fileName: String!
file: Upload!
}
input UpdateVendorBusinessAssociateAgreementInput {
vendorId: ID!
validFrom: Datetime @goField(omittable: true)
validUntil: Datetime @goField(omittable: true)
}
input DeleteVendorBusinessAssociateAgreementInput {
vendorId: ID!
}
input UploadVendorDataPrivacyAgreementInput {
vendorId: ID!
validFrom: Datetime
validUntil: Datetime
fileName: String!
file: Upload!
}
input UpdateVendorDataPrivacyAgreementInput {
vendorId: ID!
validFrom: Datetime @goField(omittable: true)
validUntil: Datetime @goField(omittable: true)
}
input DeleteVendorDataPrivacyAgreementInput {
vendorId: ID!
}
input CreateVendorRiskAssessmentInput {
vendorId: ID!
expiresAt: Datetime!
dataSensitivity: DataSensitivity!
businessImpact: BusinessImpact!
notes: String
}
input AssessVendorInput {
id: ID!
websiteUrl: String!
}
type CreateVendorPayload {
vendorEdge: VendorEdge!
}
type UpdateVendorPayload {
vendor: Vendor!
}
type DeleteVendorPayload {
deletedVendorId: ID!
}
type CreateVendorContactPayload {
vendorContactEdge: VendorContactEdge!
}
type UpdateVendorContactPayload {
vendorContact: VendorContact!
}
type DeleteVendorContactPayload {
deletedVendorContactId: ID!
}
type CreateVendorServicePayload {
vendorServiceEdge: VendorServiceEdge!
}
type UpdateVendorServicePayload {
vendorService: VendorService!
}
type DeleteVendorServicePayload {
deletedVendorServiceId: ID!
}
type UploadVendorComplianceReportPayload {
vendorComplianceReportEdge: VendorComplianceReportEdge!
}
type DeleteVendorComplianceReportPayload {
deletedVendorComplianceReportId: ID!
}
type UploadVendorBusinessAssociateAgreementPayload {
vendorBusinessAssociateAgreement: VendorBusinessAssociateAgreement!
}
type UpdateVendorBusinessAssociateAgreementPayload {
vendorBusinessAssociateAgreement: VendorBusinessAssociateAgreement!
}
type DeleteVendorBusinessAssociateAgreementPayload {
deletedVendorId: ID!
}
type UploadVendorDataPrivacyAgreementPayload {
vendorDataPrivacyAgreement: VendorDataPrivacyAgreement!
}
type UpdateVendorDataPrivacyAgreementPayload {
vendorDataPrivacyAgreement: VendorDataPrivacyAgreement!
}
type DeleteVendorDataPrivacyAgreementPayload {
deletedVendorId: ID!
}
type CreateVendorRiskAssessmentPayload {
vendorRiskAssessmentEdge: VendorRiskAssessmentEdge!
}
type AssessVendorPayload {
vendor: Vendor!
}

View File

@@ -0,0 +1,179 @@
extend type Organization {
webhookSubscriptions(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: WebhookSubscriptionOrder
): WebhookSubscriptionConnection! @goField(forceResolver: true)
}
enum WebhookEventType
@goModel(model: "go.probo.inc/probo/pkg/coredata.WebhookEventType") {
MEETING_CREATED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeMeetingCreated")
MEETING_UPDATED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeMeetingUpdated")
MEETING_DELETED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeMeetingDeleted")
VENDOR_CREATED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeVendorCreated")
VENDOR_UPDATED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeVendorUpdated")
VENDOR_DELETED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeVendorDeleted")
USER_CREATED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeUserCreated")
USER_UPDATED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeUserUpdated")
USER_DELETED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeUserDeleted")
OBLIGATION_CREATED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeObligationCreated")
OBLIGATION_UPDATED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeObligationUpdated")
OBLIGATION_DELETED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventTypeObligationDeleted")
}
enum WebhookEventStatus
@goModel(model: "go.probo.inc/probo/pkg/coredata.WebhookEventStatus") {
PENDING
@goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventStatusPending")
SUCCEEDED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventStatusSucceeded")
FAILED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.WebhookEventStatusFailed")
}
enum WebhookSubscriptionOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.WebhookSubscriptionOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.WebhookSubscriptionOrderFieldCreatedAt"
)
}
enum WebhookEventOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.WebhookEventOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.WebhookEventOrderFieldCreatedAt"
)
}
input WebhookSubscriptionOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.WebhookSubscriptionOrderBy"
) {
direction: OrderDirection!
field: WebhookSubscriptionOrderField!
}
input WebhookEventOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.WebhookEventOrderBy"
) {
field: WebhookEventOrderField!
direction: OrderDirection!
}
type WebhookSubscription implements Node {
id: ID!
organization: Organization @goField(forceResolver: true)
endpointUrl: String!
signingSecret: String! @goField(forceResolver: true)
selectedEvents: [WebhookEventType!]!
createdAt: Datetime!
updatedAt: Datetime!
events(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: WebhookEventOrder
): WebhookEventConnection! @goField(forceResolver: true)
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type WebhookEvent implements Node {
id: ID!
webhookSubscriptionId: ID!
status: WebhookEventStatus!
response: String
createdAt: Datetime!
}
type WebhookSubscriptionConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.WebhookSubscriptionConnection"
) {
edges: [WebhookSubscriptionEdge!]!
pageInfo: PageInfo!
totalCount: Int! @goField(forceResolver: true)
}
type WebhookSubscriptionEdge {
cursor: CursorKey!
node: WebhookSubscription!
}
type WebhookEventConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.WebhookEventConnection"
) {
edges: [WebhookEventEdge!]!
pageInfo: PageInfo!
totalCount: Int! @goField(forceResolver: true)
}
type WebhookEventEdge {
cursor: CursorKey!
node: WebhookEvent!
}
extend type Mutation {
createWebhookSubscription(
input: CreateWebhookSubscriptionInput!
): CreateWebhookSubscriptionPayload!
updateWebhookSubscription(
input: UpdateWebhookSubscriptionInput!
): UpdateWebhookSubscriptionPayload!
deleteWebhookSubscription(
input: DeleteWebhookSubscriptionInput!
): DeleteWebhookSubscriptionPayload!
}
input CreateWebhookSubscriptionInput {
organizationId: ID!
endpointUrl: String!
selectedEvents: [WebhookEventType!]!
}
input UpdateWebhookSubscriptionInput {
id: ID!
endpointUrl: String
selectedEvents: [WebhookEventType!]
}
input DeleteWebhookSubscriptionInput {
webhookSubscriptionId: ID!
}
type CreateWebhookSubscriptionPayload {
webhookSubscriptionEdge: WebhookSubscriptionEdge!
}
type UpdateWebhookSubscriptionPayload {
webhookSubscription: WebhookSubscription!
}
type DeleteWebhookSubscriptionPayload {
deletedWebhookSubscriptionId: ID!
}

View File

@@ -0,0 +1,287 @@
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"
"fmt"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/mailman"
"go.probo.inc/probo/pkg/page"
"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"
"go.probo.inc/probo/pkg/validator"
)
// Subscribers is the resolver for the subscribers field on MailingList.
func (r *mailingListResolver) Subscribers(ctx context.Context, obj *types.MailingList, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.MailingListSubscriberConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionMailingListSubscriberList); err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.MailingListSubscriberOrderField]{
Field: coredata.MailingListSubscriberOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
result, err := r.mailman.ListSubscribers(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list mailing list subscribers", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMailingListSubscriberConnection(result, r, obj.ID), nil
}
// Updates is the resolver for the updates field on MailingList.
func (r *mailingListResolver) Updates(ctx context.Context, obj *types.MailingList, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.MailingListUpdateConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionMailingListUpdateList); err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.MailingListUpdateOrderField]{
Field: coredata.MailingListUpdateOrderFieldUpdatedAt,
Direction: page.OrderDirectionDesc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
result, err := r.mailman.ListMailingListUpdates(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list mailing list updates", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMailingListUpdateConnection(result, r, obj.ID), nil
}
// TotalCount is the resolver for the totalCount field.
func (r *mailingListSubscriberConnectionResolver) TotalCount(ctx context.Context, obj *types.MailingListSubscriberConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionMailingListSubscriberList); err != nil {
return 0, err
}
switch obj.Resolver.(type) {
case *mailingListResolver:
count, err := r.mailman.CountSubscribers(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count mailing list subscribers", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver for mailing list subscriber connection", log.String("resolver", fmt.Sprintf("%T", obj.Resolver)))
return 0, gqlutils.Internal(ctx)
}
// TotalCount is the resolver for the totalCount field on MailingListUpdateConnection.
func (r *mailingListUpdateConnectionResolver) TotalCount(ctx context.Context, obj *types.MailingListUpdateConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionMailingListUpdateList); err != nil {
return 0, err
}
count, err := r.mailman.CountMailingListUpdates(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count mailing list updates", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
// CreateMailingListUpdate is the resolver for the createMailingListUpdate field.
func (r *mutationResolver) CreateMailingListUpdate(ctx context.Context, input types.CreateMailingListUpdateInput) (*types.CreateMailingListUpdatePayload, error) {
if err := r.authorize(ctx, input.MailingListID, probo.ActionMailingListUpdateCreate); err != nil {
return nil, err
}
mlu, err := r.mailman.CreateMailingListUpdate(
ctx,
&mailman.CreateMailingListUpdateRequest{
MailingListID: input.MailingListID,
Title: input.Title,
Body: input.Body,
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create mailing list update", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateMailingListUpdatePayload{
MailingListUpdate: types.NewMailingListUpdate(mlu),
}, nil
}
// UpdateMailingListUpdate is the resolver for the updateMailingListUpdate field.
func (r *mutationResolver) UpdateMailingListUpdate(ctx context.Context, input types.UpdateMailingListUpdateInput) (*types.UpdateMailingListUpdatePayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionMailingListUpdateUpdate); err != nil {
return nil, err
}
mlu, err := r.mailman.UpdateMailingListUpdate(
ctx,
&mailman.UpdateMailingListUpdateRequest{
ID: input.ID,
Title: input.Title,
Body: input.Body,
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
if errors.Is(err, mailman.ErrMailingListUpdateAlreadySent) {
return nil, gqlutils.Conflictf(ctx, "mailing list update can only be edited when in draft")
}
if errors.Is(err, mailman.ErrMailingListUpdateNotFound) {
return nil, gqlutils.NotFoundf(ctx, "mailing list update not found")
}
r.logger.ErrorCtx(ctx, "cannot update mailing list update", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateMailingListUpdatePayload{
MailingListUpdate: types.NewMailingListUpdate(mlu),
}, nil
}
// SendMailingListUpdate is the resolver for the sendMailingListUpdate field.
func (r *mutationResolver) SendMailingListUpdate(ctx context.Context, input types.SendMailingListUpdateInput) (*types.SendMailingListUpdatePayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionMailingListUpdateUpdate); err != nil {
return nil, err
}
mlu, err := r.mailman.SendMailingListUpdate(ctx, input.ID)
if err != nil {
if errors.Is(err, mailman.ErrMailingListUpdateAlreadySent) {
return nil, gqlutils.Conflictf(ctx, "mailing list update has already been queued for sending")
}
if errors.Is(err, mailman.ErrMailingListUpdateNotFound) {
return nil, gqlutils.NotFoundf(ctx, "mailing list update not found")
}
r.logger.ErrorCtx(ctx, "cannot queue mailing list update for sending", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.SendMailingListUpdatePayload{
MailingListUpdate: types.NewMailingListUpdate(mlu),
}, nil
}
// DeleteMailingListUpdate is the resolver for the deleteMailingListUpdate field.
func (r *mutationResolver) DeleteMailingListUpdate(ctx context.Context, input types.DeleteMailingListUpdateInput) (*types.DeleteMailingListUpdatePayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionMailingListUpdateDelete); err != nil {
return nil, err
}
if err := r.mailman.DeleteMailingListUpdate(ctx, input.ID); err != nil {
if errors.Is(err, mailman.ErrMailingListUpdateNotFound) {
return nil, gqlutils.NotFoundf(ctx, "mailing list update not found")
}
r.logger.ErrorCtx(ctx, "cannot delete mailing list update", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteMailingListUpdatePayload{
DeletedMailingListUpdateID: input.ID,
}, nil
}
// UpdateMailingList is the resolver for the updateMailingList field.
func (r *mutationResolver) UpdateMailingList(ctx context.Context, input types.UpdateMailingListInput) (*types.UpdateMailingListPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionMailingListUpdate); err != nil {
return nil, err
}
ml, err := r.mailman.UpdateMailingList(ctx, input.ID, input.ReplyTo)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot update mailing list", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateMailingListPayload{
MailingList: types.NewMailingList(ml),
}, nil
}
// CreateMailingListSubscriber is the resolver for the createMailingListSubscriber field.
func (r *mutationResolver) CreateMailingListSubscriber(ctx context.Context, input types.CreateMailingListSubscriberInput) (*types.CreateMailingListSubscriberPayload, error) {
if err := r.authorize(ctx, input.MailingListID, probo.ActionMailingListSubscriberCreate); err != nil {
return nil, err
}
subscriber, err := r.mailman.CreateSubscriber(
ctx,
&mailman.CreateSubscriberRequest{
MailingListID: input.MailingListID,
Email: input.Email,
FullName: input.FullName,
Confirmed: input.Confirmed != nil && *input.Confirmed,
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
if errors.Is(err, mailman.ErrSubscriberAlreadyExist) {
return nil, gqlutils.Conflictf(ctx, "subscriber already exists in this mailing list")
}
r.logger.ErrorCtx(ctx, "cannot create mailing list subscriber", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateMailingListSubscriberPayload{
MailingListSubscriberEdge: types.NewMailingListSubscriberEdge(subscriber, coredata.MailingListSubscriberOrderFieldCreatedAt),
}, nil
}
// DeleteMailingListSubscriber is the resolver for the deleteMailingListSubscriber field.
func (r *mutationResolver) DeleteMailingListSubscriber(ctx context.Context, input types.DeleteMailingListSubscriberInput) (*types.DeleteMailingListSubscriberPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionMailingListSubscriberDelete); err != nil {
return nil, err
}
if err := r.mailman.DeleteSubscriber(ctx, input.ID); err != nil {
if errors.Is(err, mailman.ErrSubscriberNotFound) {
return nil, gqlutils.NotFoundf(ctx, "mailing list subscriber not found")
}
r.logger.ErrorCtx(ctx, "cannot delete mailing list subscriber", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteMailingListSubscriberPayload{
DeletedMailingListSubscriberID: input.ID,
}, nil
}
// MailingList returns schema.MailingListResolver implementation.
func (r *Resolver) MailingList() schema.MailingListResolver { return &mailingListResolver{r} }
// MailingListSubscriberConnection returns schema.MailingListSubscriberConnectionResolver implementation.
func (r *Resolver) MailingListSubscriberConnection() schema.MailingListSubscriberConnectionResolver {
return &mailingListSubscriberConnectionResolver{r}
}
// MailingListUpdateConnection returns schema.MailingListUpdateConnectionResolver implementation.
func (r *Resolver) MailingListUpdateConnection() schema.MailingListUpdateConnectionResolver {
return &mailingListUpdateConnectionResolver{r}
}
type mailingListResolver struct{ *Resolver }
type mailingListSubscriberConnectionResolver struct{ *Resolver }
type mailingListUpdateConnectionResolver struct{ *Resolver }

View File

@@ -0,0 +1,433 @@
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"
"encoding/json"
"errors"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/page"
"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"
"go.probo.inc/probo/pkg/validator"
)
// Evidences is the resolver for the evidences field.
func (r *measureResolver) Evidences(ctx context.Context, obj *types.Measure, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.EvidenceOrderBy) (*types.EvidenceConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionEvidenceList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.EvidenceOrderField]{
Field: coredata.EvidenceOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.EvidenceOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.Evidences.ListForMeasureID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list measure evidences", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewEvidenceConnection(page, r, obj.ID), nil
}
// Tasks is the resolver for the tasks field.
func (r *measureResolver) Tasks(ctx context.Context, obj *types.Measure, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.TaskOrderBy) (*types.TaskConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionTaskList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.TaskOrderField]{
Field: coredata.TaskOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.TaskOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.Tasks.ListForMeasureID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list measure tasks", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewTaskConnection(page, r, obj.ID), nil
}
// Risks is the resolver for the risks field.
func (r *measureResolver) Risks(ctx context.Context, obj *types.Measure, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.RiskOrderBy, filter *types.RiskFilter) (*types.RiskConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionRiskList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.RiskOrderField]{
Field: coredata.RiskOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.RiskOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
var riskFilter = coredata.NewRiskFilter(nil, nil)
if filter != nil {
riskFilter = coredata.NewRiskFilter(filter.Query, &filter.SnapshotID)
}
page, err := prb.Risks.ListForMeasureID(ctx, obj.ID, cursor, riskFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list measure risks", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewRiskConnection(page, r, obj.ID, riskFilter), nil
}
// Controls is the resolver for the controls field.
func (r *measureResolver) Controls(ctx context.Context, obj *types.Measure, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionControlList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ControlOrderField]{
Field: coredata.ControlOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.ControlOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
var controlFilter = coredata.NewControlFilter(nil)
if filter != nil {
controlFilter = coredata.NewControlFilter(filter.Query)
}
page, err := prb.Controls.ListForMeasureID(ctx, obj.ID, cursor, controlFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list measure controls", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewControlConnection(page, r, obj.ID, controlFilter), nil
}
// Documents is the resolver for the documents field.
func (r *measureResolver) Documents(ctx context.Context, obj *types.Measure, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy, filter *types.DocumentFilter) (*types.DocumentConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.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)
var documentFilter = coredata.NewDocumentFilter(nil)
if filter != nil {
documentFilter = coredata.NewDocumentFilter(filter.Query).
WithDocumentTypes(filter.DocumentTypes).
WithClassifications(filter.Classifications)
}
pg, err := prb.Documents.ListForMeasureID(ctx, obj.ID, cursor, documentFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list documents", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewDocumentConnection(pg, r, obj.ID, documentFilter), nil
}
// Permission is the resolver for the permission field.
func (r *measureResolver) Permission(ctx context.Context, obj *types.Measure, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *measureConnectionResolver) TotalCount(ctx context.Context, obj *types.MeasureConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionMeasureList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *organizationResolver:
count, err := prb.Measures.CountForOrganizationID(ctx, obj.ParentID, obj.Filters)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count measures", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
case *controlResolver:
count, err := prb.Measures.CountForControlID(ctx, obj.ParentID, obj.Filters)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count measures", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
case *riskResolver:
count, err := prb.Measures.CountForRiskID(ctx, obj.ParentID, obj.Filters)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count measures", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver")
return 0, gqlutils.Internal(ctx)
}
// // CreateMeasure is the resolver for the createMeasure field.
func (r *mutationResolver) CreateMeasure(ctx context.Context, input types.CreateMeasureInput) (*types.CreateMeasurePayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionMeasureCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
measure, err := prb.Measures.Create(
ctx,
probo.CreateMeasureRequest{
OrganizationID: input.OrganizationID,
Name: input.Name,
Description: input.Description,
Category: input.Category,
},
)
if err != nil {
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create measure", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateMeasurePayload{
MeasureEdge: types.NewMeasureEdge(measure, coredata.MeasureOrderFieldCreatedAt),
}, nil
}
// UpdateMeasure is the resolver for the updateMeasure field.
func (r *mutationResolver) UpdateMeasure(ctx context.Context, input types.UpdateMeasureInput) (*types.UpdateMeasurePayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionMeasureUpdate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ID.TenantID())
measure, err := prb.Measures.Update(
ctx,
probo.UpdateMeasureRequest{
ID: input.ID,
Name: input.Name,
Description: gqlutils.UnwrapOmittable(input.Description),
Category: input.Category,
State: input.State,
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update measure", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateMeasurePayload{
Measure: types.NewMeasure(measure),
}, nil
}
// ImportMeasure is the resolver for the importMeasure field.
func (r *mutationResolver) ImportMeasure(ctx context.Context, input types.ImportMeasureInput) (*types.ImportMeasurePayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionMeasureImport); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
var req probo.ImportMeasureRequest
if err := json.NewDecoder(input.File.File).Decode(&req.Measures); err != nil {
r.logger.ErrorCtx(ctx, "cannot unmarshal measure", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
measures, err := prb.Measures.Import(ctx, input.OrganizationID, req)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot import measure", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
measureEdges := make([]*types.MeasureEdge, len(measures.Data))
for i, measure := range measures.Data {
measureEdges[i] = types.NewMeasureEdge(measure, coredata.MeasureOrderFieldCreatedAt)
}
return &types.ImportMeasurePayload{
MeasureEdges: measureEdges,
}, nil
}
// DeleteMeasure is the resolver for the deleteMeasure field.
func (r *mutationResolver) DeleteMeasure(ctx context.Context, input types.DeleteMeasureInput) (*types.DeleteMeasurePayload, error) {
if err := r.authorize(ctx, input.MeasureID, probo.ActionMeasureDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.MeasureID.TenantID())
err := prb.Measures.Delete(ctx, input.MeasureID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete measure", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteMeasurePayload{
DeletedMeasureID: input.MeasureID,
}, nil
}
// CreateMeasureDocumentMapping is the resolver for the createMeasureDocumentMapping field.
func (r *mutationResolver) CreateMeasureDocumentMapping(ctx context.Context, input types.CreateMeasureDocumentMappingInput) (*types.CreateMeasureDocumentMappingPayload, error) {
if err := r.authorize(ctx, input.MeasureID, probo.ActionMeasureDocumentMappingCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.MeasureID.TenantID())
measure, document, err := prb.Measures.CreateDocumentMapping(ctx, input.MeasureID, input.DocumentID)
if err != nil {
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot create measure document mapping", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateMeasureDocumentMappingPayload{
MeasureEdge: types.NewMeasureEdge(measure, coredata.MeasureOrderFieldCreatedAt),
DocumentEdge: types.NewDocumentEdge(document, coredata.DocumentOrderFieldTitle),
}, nil
}
// DeleteMeasureDocumentMapping is the resolver for the deleteMeasureDocumentMapping field.
func (r *mutationResolver) DeleteMeasureDocumentMapping(ctx context.Context, input types.DeleteMeasureDocumentMappingInput) (*types.DeleteMeasureDocumentMappingPayload, error) {
if err := r.authorize(ctx, input.MeasureID, probo.ActionMeasureDocumentMappingDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.MeasureID.TenantID())
measure, document, err := prb.Measures.DeleteDocumentMapping(ctx, input.MeasureID, input.DocumentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete measure document mapping", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteMeasureDocumentMappingPayload{
DeletedMeasureID: measure.ID,
DeletedDocumentID: document.ID,
}, nil
}
// Measures is the resolver for the measures field.
func (r *organizationResolver) Measures(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MeasureOrderBy, filter *types.MeasureFilter) (*types.MeasureConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionMeasureList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.MeasureOrderField]{
Field: coredata.MeasureOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.MeasureOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
var measureFilter = coredata.NewMeasureFilter(nil, nil, nil)
if filter != nil {
measureFilter = coredata.NewMeasureFilter(filter.Query, filter.State, filter.Category)
}
page, err := prb.Measures.ListForOrganizationID(ctx, obj.ID, cursor, measureFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization measures", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMeasureConnection(page, r, obj.ID, measureFilter), nil
}
// Measure returns schema.MeasureResolver implementation.
func (r *Resolver) Measure() schema.MeasureResolver { return &measureResolver{r} }
// MeasureConnection returns schema.MeasureConnectionResolver implementation.
func (r *Resolver) MeasureConnection() schema.MeasureConnectionResolver {
return &measureConnectionResolver{r}
}
type measureResolver struct{ *Resolver }
type measureConnectionResolver struct{ *Resolver }

View File

@@ -0,0 +1,227 @@
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"
"github.com/vikstrous/dataloadgen"
"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/console/v1/dataloader"
"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"
"go.probo.inc/probo/pkg/validator"
)
// Attendees is the resolver for the attendees field.
func (r *meetingResolver) Attendees(ctx context.Context, obj *types.Meeting) ([]*types.Profile, error) {
// TODO bug must be paginated
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
attendees, err := prb.Meetings.GetAttendees(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load meeting attendees", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if len(attendees) == 0 {
return []*types.Profile{}, nil
}
people := make([]*types.Profile, len(attendees))
for i, attendee := range attendees {
people[i] = types.NewProfile(attendee)
}
return people, nil
}
// Organization is the resolver for the organization field.
func (r *meetingResolver) Organization(ctx context.Context, obj *types.Meeting) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
organization, err := loaders.Organization.Load(ctx, obj.Organization.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot load organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Permission is the resolver for the permission field.
func (r *meetingResolver) Permission(ctx context.Context, obj *types.Meeting, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *meetingConnectionResolver) TotalCount(ctx context.Context, obj *types.MeetingConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionMeetingList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *organizationResolver:
count, err := prb.Meetings.CountForOrganizationID(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count meetings", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver")
return 0, gqlutils.Internal(ctx)
}
// CreateMeeting is the resolver for the createMeeting field.
func (r *mutationResolver) CreateMeeting(ctx context.Context, input types.CreateMeetingInput) (*types.CreateMeetingPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionMeetingCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
meeting, err := prb.Meetings.Create(
ctx,
probo.CreateMeetingRequest{
OrganizationID: input.OrganizationID,
Name: input.Name,
Date: input.Date,
AttendeeIDs: input.AttendeeIds,
Minutes: input.Minutes,
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create meeting", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateMeetingPayload{
MeetingEdge: types.NewMeetingEdge(meeting, coredata.MeetingOrderFieldCreatedAt),
}, nil
}
// UpdateMeeting is the resolver for the updateMeeting field.
func (r *mutationResolver) UpdateMeeting(ctx context.Context, input types.UpdateMeetingInput) (*types.UpdateMeetingPayload, error) {
if err := r.authorize(ctx, input.MeetingID, probo.ActionMeetingUpdate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.MeetingID.TenantID())
var attendeeIDs []gid.GID
if input.AttendeeIds != nil {
attendeeIDs = input.AttendeeIds
}
meeting, err := prb.Meetings.Update(
ctx,
probo.UpdateMeetingRequest{
MeetingID: input.MeetingID,
Name: input.Name,
Date: input.Date,
AttendeeIDs: attendeeIDs,
Minutes: gqlutils.UnwrapOmittable(input.Minutes),
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update meeting", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateMeetingPayload{
Meeting: types.NewMeeting(meeting),
}, nil
}
// DeleteMeeting is the resolver for the deleteMeeting field.
func (r *mutationResolver) DeleteMeeting(ctx context.Context, input types.DeleteMeetingInput) (*types.DeleteMeetingPayload, error) {
if err := r.authorize(ctx, input.MeetingID, probo.ActionMeetingDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.MeetingID.TenantID())
err := prb.Meetings.Delete(ctx, input.MeetingID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete meeting", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteMeetingPayload{
DeletedMeetingID: input.MeetingID,
}, nil
}
// Meetings is the resolver for the meetings field.
func (r *organizationResolver) Meetings(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MeetingOrderBy) (*types.MeetingConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionMeetingList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.MeetingOrderField]{
Field: coredata.MeetingOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.MeetingOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.Meetings.ListForOrganizationID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization meetings", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMeetingConnection(page, r, obj.ID), nil
}
// Meeting returns schema.MeetingResolver implementation.
func (r *Resolver) Meeting() schema.MeetingResolver { return &meetingResolver{r} }
// MeetingConnection returns schema.MeetingConnectionResolver implementation.
func (r *Resolver) MeetingConnection() schema.MeetingConnectionResolver {
return &meetingConnectionResolver{r}
}
type meetingResolver struct{ *Resolver }
type meetingConnectionResolver struct{ *Resolver }

View File

@@ -0,0 +1,247 @@
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"
"github.com/vikstrous/dataloadgen"
"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/probo"
"go.probo.inc/probo/pkg/server/api/console/v1/dataloader"
"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"
"go.probo.inc/probo/pkg/validator"
)
// CreateObligation is the resolver for the createObligation field.
func (r *mutationResolver) CreateObligation(ctx context.Context, input types.CreateObligationInput) (*types.CreateObligationPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionObligationCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
req := probo.CreateObligationRequest{
OrganizationID: input.OrganizationID,
Area: input.Area,
Source: input.Source,
Requirement: input.Requirement,
ActionsToBeImplemented: input.ActionsToBeImplemented,
Regulator: input.Regulator,
OwnerID: input.OwnerID,
LastReviewDate: input.LastReviewDate,
DueDate: input.DueDate,
Status: input.Status,
Type: input.Type,
}
obligation, err := prb.Obligations.Create(ctx, &req)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create obligation", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateObligationPayload{
ObligationEdge: types.NewObligationEdge(obligation, coredata.ObligationOrderFieldCreatedAt),
}, nil
}
// UpdateObligation is the resolver for the updateObligation field.
func (r *mutationResolver) UpdateObligation(ctx context.Context, input types.UpdateObligationInput) (*types.UpdateObligationPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionObligationUpdate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ID.TenantID())
req := probo.UpdateObligationRequest{
ID: input.ID,
Area: gqlutils.UnwrapOmittable(input.Area),
Source: gqlutils.UnwrapOmittable(input.Source),
Requirement: gqlutils.UnwrapOmittable(input.Requirement),
ActionsToBeImplemented: gqlutils.UnwrapOmittable(input.ActionsToBeImplemented),
Regulator: gqlutils.UnwrapOmittable(input.Regulator),
OwnerID: input.OwnerID,
LastReviewDate: gqlutils.UnwrapOmittable(input.LastReviewDate),
DueDate: gqlutils.UnwrapOmittable(input.DueDate),
Status: input.Status,
Type: input.Type,
}
obligation, err := prb.Obligations.Update(ctx, &req)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update obligation", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateObligationPayload{
Obligation: types.NewObligation(obligation),
}, nil
}
// DeleteObligation is the resolver for the deleteObligation field.
func (r *mutationResolver) DeleteObligation(ctx context.Context, input types.DeleteObligationInput) (*types.DeleteObligationPayload, error) {
if err := r.authorize(ctx, input.ObligationID, probo.ActionObligationDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ObligationID.TenantID())
err := prb.Obligations.Delete(ctx, input.ObligationID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete obligation", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteObligationPayload{
DeletedObligationID: input.ObligationID,
}, nil
}
// Organization is the resolver for the organization field.
func (r *obligationResolver) Organization(ctx context.Context, obj *types.Obligation) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
organization, err := loaders.Organization.Load(ctx, obj.Organization.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get obligation organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Owner is the resolver for the owner field.
func (r *obligationResolver) Owner(ctx context.Context, obj *types.Obligation) (*types.Profile, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
owner, err := loaders.Profile.Load(ctx, obj.Owner.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get obligation owner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewProfile(owner), nil
}
// Permission is the resolver for the permission field.
func (r *obligationResolver) Permission(ctx context.Context, obj *types.Obligation, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *obligationConnectionResolver) TotalCount(ctx context.Context, obj *types.ObligationConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionObligationList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *organizationResolver:
obligationFilter := coredata.NewObligationFilter(nil)
if obj.Filter != nil {
obligationFilter = coredata.NewObligationFilter(&obj.Filter.SnapshotID)
}
count, err := prb.Obligations.CountForOrganizationID(ctx, obj.ParentID, obligationFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count obligations", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
case *riskResolver:
obligationFilter := coredata.NewObligationFilter(nil)
if obj.Filter != nil {
obligationFilter = coredata.NewObligationFilter(&obj.Filter.SnapshotID)
}
count, err := prb.Obligations.CountForRiskID(ctx, obj.ParentID, obligationFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count risk obligations", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver")
return 0, gqlutils.Internal(ctx)
}
// Obligations is the resolver for the obligations field.
func (r *organizationResolver) Obligations(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ObligationOrderBy, filter *types.ObligationFilter) (*types.ObligationConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionObligationList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ObligationOrderField]{
Field: coredata.ObligationOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.ObligationOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
obligationFilter := coredata.NewObligationFilter(nil)
if filter != nil {
obligationFilter = coredata.NewObligationFilter(&filter.SnapshotID)
}
page, err := prb.Obligations.ListForOrganizationID(ctx, obj.ID, cursor, obligationFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization obligations", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewObligationConnection(page, r, obj.ID, filter), nil
}
// Obligation returns schema.ObligationResolver implementation.
func (r *Resolver) Obligation() schema.ObligationResolver { return &obligationResolver{r} }
// ObligationConnection returns schema.ObligationConnectionResolver implementation.
func (r *Resolver) ObligationConnection() schema.ObligationConnectionResolver {
return &obligationConnectionResolver{r}
}
type obligationResolver struct{ *Resolver }
type obligationConnectionResolver struct{ *Resolver }

View File

@@ -0,0 +1,61 @@
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"
"fmt"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/iam"
"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"
)
// Permission is the resolver for the permission field.
func (r *profileResolver) Permission(ctx context.Context, obj *types.Profile, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *profileConnectionResolver) TotalCount(ctx context.Context, obj *types.ProfileConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, iam.ActionMembershipProfileList); err != nil {
return 0, err
}
switch obj.Resolver.(type) {
case *organizationResolver:
count, err := r.iam.OrganizationService.CountProfiles(ctx, obj.ParentID, obj.Filters)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count profiles", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
case *documentVersionResolver:
prb := r.ProboService(ctx, obj.ParentID.TenantID())
count, err := prb.Documents.CountVersionApprovers(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count document version approvers", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver for profile connection", log.String("resolver", fmt.Sprintf("%T", obj.Resolver)))
return 0, gqlutils.Internal(ctx)
}
// Profile returns schema.ProfileResolver implementation.
func (r *Resolver) Profile() schema.ProfileResolver { return &profileResolver{r} }
// ProfileConnection returns schema.ProfileConnectionResolver implementation.
func (r *Resolver) ProfileConnection() schema.ProfileConnectionResolver {
return &profileConnectionResolver{r}
}
type profileResolver struct{ *Resolver }
type profileConnectionResolver struct{ *Resolver }

View File

@@ -0,0 +1,353 @@
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"
"encoding/base64"
"errors"
"fmt"
"github.com/vikstrous/dataloadgen"
"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/console/v1/dataloader"
"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"
)
// CreateProcessingActivity is the resolver for the createProcessingActivity field.
func (r *mutationResolver) CreateProcessingActivity(ctx context.Context, input types.CreateProcessingActivityInput) (*types.CreateProcessingActivityPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionProcessingActivityCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
req := probo.CreateProcessingActivityRequest{
OrganizationID: input.OrganizationID,
Name: input.Name,
Purpose: input.Purpose,
DataSubjectCategory: input.DataSubjectCategory,
PersonalDataCategory: input.PersonalDataCategory,
SpecialOrCriminalData: input.SpecialOrCriminalData,
LawfulBasis: input.LawfulBasis,
Recipients: input.Recipients,
Location: input.Location,
InternationalTransfers: input.InternationalTransfers,
TransferSafeguard: input.TransferSafeguards,
RetentionPeriod: input.RetentionPeriod,
SecurityMeasures: input.SecurityMeasures,
DataProtectionImpactAssessmentNeeded: input.DataProtectionImpactAssessmentNeeded,
TransferImpactAssessmentNeeded: input.TransferImpactAssessmentNeeded,
LastReviewDate: input.LastReviewDate,
NextReviewDate: input.NextReviewDate,
Role: input.Role,
DataProtectionOfficerID: input.DataProtectionOfficerID,
VendorIDs: input.VendorIds,
}
activity, err := prb.ProcessingActivities.Create(ctx, &req)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create processing activity", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateProcessingActivityPayload{
ProcessingActivityEdge: types.NewProcessingActivityEdge(activity, coredata.ProcessingActivityOrderFieldCreatedAt),
}, nil
}
// UpdateProcessingActivity is the resolver for the updateProcessingActivity field.
func (r *mutationResolver) UpdateProcessingActivity(ctx context.Context, input types.UpdateProcessingActivityInput) (*types.UpdateProcessingActivityPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionProcessingActivityUpdate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ID.TenantID())
req := probo.UpdateProcessingActivityRequest{
ID: input.ID,
Name: input.Name,
Purpose: gqlutils.UnwrapOmittable(input.Purpose),
DataSubjectCategory: gqlutils.UnwrapOmittable(input.DataSubjectCategory),
PersonalDataCategory: gqlutils.UnwrapOmittable(input.PersonalDataCategory),
SpecialOrCriminalData: input.SpecialOrCriminalData,
LawfulBasis: input.LawfulBasis,
Recipients: gqlutils.UnwrapOmittable(input.Recipients),
Location: gqlutils.UnwrapOmittable(input.Location),
InternationalTransfers: input.InternationalTransfers,
TransferSafeguard: gqlutils.UnwrapOmittable(input.TransferSafeguards),
RetentionPeriod: gqlutils.UnwrapOmittable(input.RetentionPeriod),
SecurityMeasures: gqlutils.UnwrapOmittable(input.SecurityMeasures),
DataProtectionImpactAssessmentNeeded: input.DataProtectionImpactAssessmentNeeded,
TransferImpactAssessmentNeeded: input.TransferImpactAssessmentNeeded,
LastReviewDate: gqlutils.UnwrapOmittable(input.LastReviewDate),
NextReviewDate: gqlutils.UnwrapOmittable(input.NextReviewDate),
Role: input.Role,
DataProtectionOfficerID: gqlutils.UnwrapOmittable(input.DataProtectionOfficerID),
VendorIDs: &input.VendorIds,
}
activity, err := prb.ProcessingActivities.Update(ctx, &req)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot update processing activity", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateProcessingActivityPayload{
ProcessingActivity: types.NewProcessingActivity(activity),
}, nil
}
// DeleteProcessingActivity is the resolver for the deleteProcessingActivity field.
func (r *mutationResolver) DeleteProcessingActivity(ctx context.Context, input types.DeleteProcessingActivityInput) (*types.DeleteProcessingActivityPayload, error) {
if err := r.authorize(ctx, input.ProcessingActivityID, probo.ActionProcessingActivityDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ProcessingActivityID.TenantID())
err := prb.ProcessingActivities.Delete(ctx, input.ProcessingActivityID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete processing activity", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteProcessingActivityPayload{
DeletedProcessingActivityID: input.ProcessingActivityID,
}, nil
}
// ExportProcessingActivitiesPDF is the resolver for the exportProcessingActivitiesPDF field.
func (r *mutationResolver) ExportProcessingActivitiesPDF(ctx context.Context, input types.ExportProcessingActivitiesPDFInput) (*types.ExportProcessingActivitiesPDFPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionProcessingActivityExport); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
var snapshotIDPtr *gid.GID
if input.Filter != nil {
snapshotIDPtr = input.Filter.SnapshotID
}
processingActivityFilter := coredata.NewProcessingActivityFilter(&snapshotIDPtr)
pdf, err := prb.ProcessingActivities.ExportPDF(ctx, input.OrganizationID, processingActivityFilter)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot export processing activities PDF", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportProcessingActivitiesPDFPayload{
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
}, nil
}
// ProcessingActivities is the resolver for the processingActivities field.
func (r *organizationResolver) ProcessingActivities(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ProcessingActivityOrderBy, filter *types.ProcessingActivityFilter) (*types.ProcessingActivityConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionProcessingActivityList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ProcessingActivityOrderField]{
Field: coredata.ProcessingActivityOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.ProcessingActivityOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
processingActivityFilter := coredata.NewProcessingActivityFilter(nil)
if filter != nil {
processingActivityFilter = coredata.NewProcessingActivityFilter(&filter.SnapshotID)
}
page, err := prb.ProcessingActivities.ListForOrganizationID(ctx, obj.ID, cursor, processingActivityFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization processing activities", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewProcessingActivityConnection(page, r, obj.ID, filter), nil
}
// Organization is the resolver for the organization field.
func (r *processingActivityResolver) Organization(ctx context.Context, obj *types.ProcessingActivity) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
organization, err := loaders.Organization.Load(ctx, obj.Organization.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// DataProtectionOfficer is the resolver for the dataProtectionOfficer field.
func (r *processingActivityResolver) DataProtectionOfficer(ctx context.Context, obj *types.ProcessingActivity) (*types.Profile, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil {
return nil, err
}
if obj.DataProtectionOfficer == nil {
return nil, nil
}
loaders := dataloader.FromContext(ctx)
dpo, err := loaders.Profile.Load(ctx, obj.DataProtectionOfficer.ID)
if err != nil {
if errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get data protection officer", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewProfile(dpo), nil
}
// Vendors is the resolver for the vendors field.
func (r *processingActivityResolver) Vendors(ctx context.Context, obj *types.ProcessingActivity, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorOrderBy) (*types.VendorConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionVendorList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.VendorOrderField]{
Field: coredata.VendorOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.VendorOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.Vendors.ListForProcessingActivityID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list processing activity vendors", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewVendorConnection(page, r, obj.ID), nil
}
// DataProtectionImpactAssessment is the resolver for the dataProtectionImpactAssessment field.
func (r *processingActivityResolver) DataProtectionImpactAssessment(ctx context.Context, obj *types.ProcessingActivity) (*types.DataProtectionImpactAssessment, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionDataProtectionImpactAssessmentGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
dpia, err := prb.DataProtectionImpactAssessments.GetByProcessingActivityID(ctx, obj.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get processing activity dpia", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewDataProtectionImpactAssessment(dpia), nil
}
// TransferImpactAssessment is the resolver for the transferImpactAssessment field.
func (r *processingActivityResolver) TransferImpactAssessment(ctx context.Context, obj *types.ProcessingActivity) (*types.TransferImpactAssessment, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionTransferImpactAssessmentGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
tia, err := prb.TransferImpactAssessments.GetByProcessingActivityID(ctx, obj.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get processing activity tia", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewTransferImpactAssessment(tia), nil
}
// Permission is the resolver for the permission field.
func (r *processingActivityResolver) Permission(ctx context.Context, obj *types.ProcessingActivity, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *processingActivityConnectionResolver) TotalCount(ctx context.Context, obj *types.ProcessingActivityConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionProcessingActivityList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *organizationResolver:
processingActivityFilter := coredata.NewProcessingActivityFilter(nil)
if obj.Filter != nil {
processingActivityFilter = coredata.NewProcessingActivityFilter(&obj.Filter.SnapshotID)
}
count, err := prb.ProcessingActivities.CountForOrganizationID(ctx, obj.ParentID, processingActivityFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count organization processing activities", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver")
return 0, gqlutils.Internal(ctx)
}
// ProcessingActivity returns schema.ProcessingActivityResolver implementation.
func (r *Resolver) ProcessingActivity() schema.ProcessingActivityResolver {
return &processingActivityResolver{r}
}
// ProcessingActivityConnection returns schema.ProcessingActivityConnectionResolver implementation.
func (r *Resolver) ProcessingActivityConnection() schema.ProcessingActivityConnectionResolver {
return &processingActivityConnectionResolver{r}
}
type processingActivityResolver struct{ *Resolver }
type processingActivityConnectionResolver struct{ *Resolver }

View File

@@ -0,0 +1,202 @@
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/iam"
"go.probo.inc/probo/pkg/page"
"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"
"go.probo.inc/probo/pkg/validator"
)
// CreateRightsRequest is the resolver for the createRightsRequest field.
func (r *mutationResolver) CreateRightsRequest(ctx context.Context, input types.CreateRightsRequestInput) (*types.CreateRightsRequestPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionRightsRequestCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
req := probo.CreateRightsRequestRequest{
OrganizationID: input.OrganizationID,
RequestType: &input.RequestType,
RequestState: &input.RequestState,
DataSubject: input.DataSubject,
Contact: input.Contact,
Details: input.Details,
Deadline: input.Deadline,
ActionTaken: input.ActionTaken,
}
rightsRequest, err := prb.RightsRequests.Create(ctx, &req)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create rights request", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateRightsRequestPayload{
RightsRequestEdge: types.NewRightsRequestEdge(rightsRequest, coredata.RightsRequestOrderFieldCreatedAt),
}, nil
}
// UpdateRightsRequest is the resolver for the updateRightsRequest field.
func (r *mutationResolver) UpdateRightsRequest(ctx context.Context, input types.UpdateRightsRequestInput) (*types.UpdateRightsRequestPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionRightsRequestUpdate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ID.TenantID())
req := probo.UpdateRightsRequestRequest{
ID: input.ID,
RequestType: input.RequestType,
RequestState: input.RequestState,
DataSubject: gqlutils.UnwrapOmittable(input.DataSubject),
Contact: gqlutils.UnwrapOmittable(input.Contact),
Details: gqlutils.UnwrapOmittable(input.Details),
Deadline: gqlutils.UnwrapOmittable(input.Deadline),
ActionTaken: gqlutils.UnwrapOmittable(input.ActionTaken),
}
rightsRequest, err := prb.RightsRequests.Update(ctx, &req)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update rights request", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateRightsRequestPayload{
RightsRequest: types.NewRightsRequest(rightsRequest),
}, nil
}
// DeleteRightsRequest is the resolver for the deleteRightsRequest field.
func (r *mutationResolver) DeleteRightsRequest(ctx context.Context, input types.DeleteRightsRequestInput) (*types.DeleteRightsRequestPayload, error) {
if err := r.authorize(ctx, input.RightsRequestID, probo.ActionRightsRequestDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.RightsRequestID.TenantID())
err := prb.RightsRequests.Delete(ctx, input.RightsRequestID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete rights request", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteRightsRequestPayload{
DeletedRightsRequestID: input.RightsRequestID,
}, nil
}
// RightsRequests is the resolver for the rightsRequests field.
func (r *organizationResolver) RightsRequests(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.RightsRequestOrderBy) (*types.RightsRequestConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionRightsRequestList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.RightsRequestOrderField]{
Field: coredata.RightsRequestOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.RightsRequestOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.RightsRequests.ListForOrganizationID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization rights requests", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewRightsRequestConnection(page, r, obj.ID), nil
}
// Organization is the resolver for the organization field.
func (r *rightsRequestResolver) Organization(ctx context.Context, obj *types.RightsRequest) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionOrganizationGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
rightsRequest, err := prb.RightsRequests.Get(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get rights request", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
organization, err := prb.Organizations.Get(ctx, rightsRequest.OrganizationID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Permission is the resolver for the permission field.
func (r *rightsRequestResolver) Permission(ctx context.Context, obj *types.RightsRequest, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *rightsRequestConnectionResolver) TotalCount(ctx context.Context, obj *types.RightsRequestConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionRightsRequestList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *organizationResolver:
count, err := prb.RightsRequests.CountByOrganizationID(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count rights requests", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
default:
r.logger.ErrorCtx(ctx, "unsupported resolver type for RightsRequestConnection")
return 0, gqlutils.Internal(ctx)
}
}
// RightsRequest returns schema.RightsRequestResolver implementation.
func (r *Resolver) RightsRequest() schema.RightsRequestResolver { return &rightsRequestResolver{r} }
// RightsRequestConnection returns schema.RightsRequestConnectionResolver implementation.
func (r *Resolver) RightsRequestConnection() schema.RightsRequestConnectionResolver {
return &rightsRequestConnectionResolver{r}
}
type rightsRequestResolver struct{ *Resolver }
type rightsRequestConnectionResolver struct{ *Resolver }

View File

@@ -0,0 +1,505 @@
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"
"github.com/vikstrous/dataloadgen"
"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/probo"
"go.probo.inc/probo/pkg/server/api/console/v1/dataloader"
"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"
"go.probo.inc/probo/pkg/validator"
)
// CreateRisk is the resolver for the createRisk field.
func (r *mutationResolver) CreateRisk(ctx context.Context, input types.CreateRiskInput) (*types.CreateRiskPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionRiskCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
risk, err := prb.Risks.Create(
ctx,
probo.CreateRiskRequest{
OrganizationID: input.OrganizationID,
Name: input.Name,
Description: input.Description,
Category: input.Category,
Treatment: input.Treatment,
OwnerID: input.OwnerID,
InherentLikelihood: input.InherentLikelihood,
InherentImpact: input.InherentImpact,
ResidualLikelihood: input.ResidualLikelihood,
ResidualImpact: input.ResidualImpact,
Note: input.Note,
},
)
if err != nil {
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create risk", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateRiskPayload{
RiskEdge: types.NewRiskEdge(risk, coredata.RiskOrderFieldCreatedAt),
}, nil
}
// UpdateRisk is the resolver for the updateRisk field.
func (r *mutationResolver) UpdateRisk(ctx context.Context, input types.UpdateRiskInput) (*types.UpdateRiskPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionRiskUpdate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ID.TenantID())
risk, err := prb.Risks.Update(
ctx,
probo.UpdateRiskRequest{
ID: input.ID,
Name: input.Name,
Description: gqlutils.UnwrapOmittable(input.Description),
Category: input.Category,
Treatment: input.Treatment,
OwnerID: gqlutils.UnwrapOmittable(input.OwnerID),
InherentLikelihood: input.InherentLikelihood,
InherentImpact: input.InherentImpact,
ResidualLikelihood: input.ResidualLikelihood,
ResidualImpact: input.ResidualImpact,
Note: input.Note,
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update risk", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateRiskPayload{
Risk: types.NewRisk(risk),
}, nil
}
// DeleteRisk is the resolver for the deleteRisk field.
func (r *mutationResolver) DeleteRisk(ctx context.Context, input types.DeleteRiskInput) (*types.DeleteRiskPayload, error) {
if err := r.authorize(ctx, input.RiskID, probo.ActionRiskDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.RiskID.TenantID())
err := prb.Risks.Delete(ctx, input.RiskID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete risk", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteRiskPayload{
DeletedRiskID: input.RiskID,
}, nil
}
// CreateRiskMeasureMapping is the resolver for the createRiskMeasureMapping field.
func (r *mutationResolver) CreateRiskMeasureMapping(ctx context.Context, input types.CreateRiskMeasureMappingInput) (*types.CreateRiskMeasureMappingPayload, error) {
if err := r.authorize(ctx, input.RiskID, probo.ActionRiskMeasureMappingCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.RiskID.TenantID())
risk, measure, err := prb.Risks.CreateMeasureMapping(ctx, input.RiskID, input.MeasureID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create risk measure mapping", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateRiskMeasureMappingPayload{
RiskEdge: types.NewRiskEdge(risk, coredata.RiskOrderFieldCreatedAt),
MeasureEdge: types.NewMeasureEdge(measure, coredata.MeasureOrderFieldCreatedAt),
}, nil
}
// DeleteRiskMeasureMapping is the resolver for the deleteRiskMeasureMapping field.
func (r *mutationResolver) DeleteRiskMeasureMapping(ctx context.Context, input types.DeleteRiskMeasureMappingInput) (*types.DeleteRiskMeasureMappingPayload, error) {
if err := r.authorize(ctx, input.RiskID, probo.ActionRiskMeasureMappingDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.RiskID.TenantID())
risk, measure, err := prb.Risks.DeleteMeasureMapping(ctx, input.RiskID, input.MeasureID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete risk measure mapping", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteRiskMeasureMappingPayload{
DeletedRiskID: risk.ID,
DeletedMeasureID: measure.ID,
}, nil
}
// CreateRiskDocumentMapping is the resolver for the createRiskDocumentMapping field.
func (r *mutationResolver) CreateRiskDocumentMapping(ctx context.Context, input types.CreateRiskDocumentMappingInput) (*types.CreateRiskDocumentMappingPayload, error) {
if err := r.authorize(ctx, input.RiskID, probo.ActionRiskDocumentMappingCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.RiskID.TenantID())
risk, document, err := prb.Risks.CreateDocumentMapping(ctx, input.RiskID, input.DocumentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create risk document mapping", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateRiskDocumentMappingPayload{
RiskEdge: types.NewRiskEdge(risk, coredata.RiskOrderFieldCreatedAt),
DocumentEdge: types.NewDocumentEdge(document, coredata.DocumentOrderFieldTitle),
}, nil
}
// DeleteRiskDocumentMapping is the resolver for the deleteRiskDocumentMapping field.
func (r *mutationResolver) DeleteRiskDocumentMapping(ctx context.Context, input types.DeleteRiskDocumentMappingInput) (*types.DeleteRiskDocumentMappingPayload, error) {
if err := r.authorize(ctx, input.RiskID, probo.ActionRiskDocumentMappingDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.RiskID.TenantID())
risk, document, err := prb.Risks.DeleteDocumentMapping(ctx, input.RiskID, input.DocumentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete risk document mapping", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteRiskDocumentMappingPayload{
DeletedRiskID: risk.ID,
DeletedDocumentID: document.ID,
}, nil
}
// CreateRiskObligationMapping is the resolver for the createRiskObligationMapping field.
func (r *mutationResolver) CreateRiskObligationMapping(ctx context.Context, input types.CreateRiskObligationMappingInput) (*types.CreateRiskObligationMappingPayload, error) {
if err := r.authorize(ctx, input.RiskID, probo.ActionRiskObligationMappingCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.RiskID.TenantID())
risk, obligation, err := prb.Risks.CreateObligationMapping(ctx, input.RiskID, input.ObligationID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create risk obligation mapping", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateRiskObligationMappingPayload{
RiskEdge: types.NewRiskEdge(risk, coredata.RiskOrderFieldCreatedAt),
ObligationEdge: types.NewObligationEdge(obligation, coredata.ObligationOrderFieldCreatedAt),
}, nil
}
// DeleteRiskObligationMapping is the resolver for the deleteRiskObligationMapping field.
func (r *mutationResolver) DeleteRiskObligationMapping(ctx context.Context, input types.DeleteRiskObligationMappingInput) (*types.DeleteRiskObligationMappingPayload, error) {
if err := r.authorize(ctx, input.RiskID, probo.ActionRiskObligationMappingDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.RiskID.TenantID())
risk, obligation, err := prb.Risks.DeleteObligationMapping(ctx, input.RiskID, input.ObligationID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete risk obligation mapping", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteRiskObligationMappingPayload{
DeletedRiskID: risk.ID,
DeletedObligationID: obligation.ID,
}, nil
}
// Risks is the resolver for the risks field.
func (r *organizationResolver) Risks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.RiskOrderBy, filter *types.RiskFilter) (*types.RiskConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionRiskList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.RiskOrderField]{
Field: coredata.RiskOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.RiskOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
var riskFilter = coredata.NewRiskFilter(nil, nil)
if filter != nil {
riskFilter = coredata.NewRiskFilter(filter.Query, &filter.SnapshotID)
}
page, err := prb.Risks.ListForOrganizationID(ctx, obj.ID, cursor, riskFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization risks", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewRiskConnection(page, r, obj.ID, riskFilter), nil
}
// Owner is the resolver for the owner field.
func (r *riskResolver) Owner(ctx context.Context, obj *types.Risk) (*types.Profile, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil {
return nil, err
}
if obj.Owner == nil {
return nil, nil
}
loaders := dataloader.FromContext(ctx)
owner, err := loaders.Profile.Load(ctx, obj.Owner.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get owner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewProfile(owner), nil
}
// Organization is the resolver for the organization field.
func (r *riskResolver) Organization(ctx context.Context, obj *types.Risk) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
organization, err := loaders.Organization.Load(ctx, obj.Organization.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Measures is the resolver for the measures field.
func (r *riskResolver) Measures(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MeasureOrderBy, filter *types.MeasureFilter) (*types.MeasureConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionMeasureList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.MeasureOrderField]{
Field: coredata.MeasureOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.MeasureOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
var measureFilter = coredata.NewMeasureFilter(nil, nil, nil)
if filter != nil {
measureFilter = coredata.NewMeasureFilter(filter.Query, filter.State, filter.Category)
}
page, err := prb.Measures.ListForRiskID(ctx, obj.ID, cursor, measureFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list risk measures", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMeasureConnection(page, r, obj.ID, measureFilter), nil
}
// Documents is the resolver for the documents field.
func (r *riskResolver) Documents(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy, filter *types.DocumentFilter) (*types.DocumentConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionDocumentList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.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)
var documentFilter = coredata.NewDocumentFilter(nil)
if filter != nil {
documentFilter = coredata.NewDocumentFilter(filter.Query).
WithDocumentTypes(filter.DocumentTypes).
WithClassifications(filter.Classifications)
}
page, err := prb.Documents.ListForRiskID(ctx, obj.ID, cursor, documentFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list risk documents", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewDocumentConnection(page, r, obj.ID, documentFilter), nil
}
// Controls is the resolver for the controls field.
func (r *riskResolver) Controls(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionControlList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ControlOrderField]{
Field: coredata.ControlOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.ControlOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
var filters = coredata.NewControlFilter(nil)
if filter != nil {
filters = coredata.NewControlFilter(filter.Query)
}
page, err := prb.Controls.ListForRiskID(ctx, obj.ID, cursor, filters)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list risk controls", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewControlConnection(page, r, obj.ID, filters), nil
}
// Obligations is the resolver for the obligations field.
func (r *riskResolver) Obligations(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ObligationOrderBy, filter *types.ObligationFilter) (*types.ObligationConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionObligationList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ObligationOrderField]{
Field: coredata.ObligationOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.ObligationOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
var obligationFilter = coredata.NewObligationFilter(nil)
if filter != nil {
obligationFilter = coredata.NewObligationFilter(&filter.SnapshotID)
}
page, err := prb.Obligations.ListForRiskID(ctx, obj.ID, cursor, obligationFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list risk obligations", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewObligationConnection(page, r, obj.ID, filter), nil
}
// Permission is the resolver for the permission field.
func (r *riskResolver) Permission(ctx context.Context, obj *types.Risk, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *riskConnectionResolver) TotalCount(ctx context.Context, obj *types.RiskConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionRiskList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *measureResolver:
count, err := prb.Risks.CountForMeasureID(ctx, obj.ParentID, obj.Filters)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count risks", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
case *organizationResolver:
count, err := prb.Risks.CountForOrganizationID(ctx, obj.ParentID, obj.Filters)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count risks", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver")
return 0, gqlutils.Internal(ctx)
}
// Risk returns schema.RiskResolver implementation.
func (r *Resolver) Risk() schema.RiskResolver { return &riskResolver{r} }
// RiskConnection returns schema.RiskConnectionResolver implementation.
func (r *Resolver) RiskConnection() schema.RiskConnectionResolver { return &riskConnectionResolver{r} }
type riskResolver struct{ *Resolver }
type riskConnectionResolver struct{ *Resolver }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,195 @@
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/page"
"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"
)
// CreateSnapshot is the resolver for the createSnapshot field.
func (r *mutationResolver) CreateSnapshot(ctx context.Context, input types.CreateSnapshotInput) (*types.CreateSnapshotPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionSnapshotCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
snapshot, err := prb.Snapshots.Create(
ctx,
&probo.CreateSnapshotRequest{
OrganizationID: input.OrganizationID,
Name: input.Name,
Description: input.Description,
Type: input.Type,
},
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create snapshot", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateSnapshotPayload{
SnapshotEdge: types.NewSnapshotEdge(snapshot, coredata.SnapshotOrderFieldCreatedAt),
}, nil
}
// DeleteSnapshot is the resolver for the deleteSnapshot field.
func (r *mutationResolver) DeleteSnapshot(ctx context.Context, input types.DeleteSnapshotInput) (*types.DeleteSnapshotPayload, error) {
if err := r.authorize(ctx, input.SnapshotID, probo.ActionSnapshotDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.SnapshotID.TenantID())
err := prb.Snapshots.Delete(ctx, input.SnapshotID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete snapshot", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteSnapshotPayload{
DeletedSnapshotID: input.SnapshotID,
}, nil
}
// Snapshots is the resolver for the snapshots field.
func (r *organizationResolver) Snapshots(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.SnapshotOrderBy) (*types.SnapshotConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionSnapshotList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.SnapshotOrderField]{
Field: coredata.SnapshotOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.SnapshotOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.Snapshots.ListForOrganizationID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization snapshots", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSnapshotConnection(page, r, obj.ID), nil
}
// Organization is the resolver for the organization field.
func (r *snapshotResolver) Organization(ctx context.Context, obj *types.Snapshot) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
snapshot, err := prb.Snapshots.Get(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get snapshot", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
organization, err := prb.Organizations.Get(ctx, snapshot.OrganizationID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Controls is the resolver for the controls field.
func (r *snapshotResolver) Controls(ctx context.Context, obj *types.Snapshot, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionControlList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ControlOrderField]{
Field: coredata.ControlOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.ControlOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
var controlFilter = coredata.NewControlFilter(nil)
if filter != nil {
controlFilter = coredata.NewControlFilter(filter.Query)
}
page, err := prb.Controls.ListForSnapshotID(ctx, obj.ID, cursor, controlFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list snapshot controls", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewControlConnection(page, r, obj.ID, controlFilter), nil
}
// Permission is the resolver for the permission field.
func (r *snapshotResolver) Permission(ctx context.Context, obj *types.Snapshot, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *snapshotConnectionResolver) TotalCount(ctx context.Context, obj *types.SnapshotConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionSnapshotList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *organizationResolver:
count, err := prb.Snapshots.CountForOrganizationID(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count snapshots", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver")
return 0, gqlutils.Internal(ctx)
}
// Snapshot returns schema.SnapshotResolver implementation.
func (r *Resolver) Snapshot() schema.SnapshotResolver { return &snapshotResolver{r} }
// SnapshotConnection returns schema.SnapshotConnectionResolver implementation.
func (r *Resolver) SnapshotConnection() schema.SnapshotConnectionResolver {
return &snapshotConnectionResolver{r}
}
type snapshotResolver struct{ *Resolver }
type snapshotConnectionResolver struct{ *Resolver }

View File

@@ -0,0 +1,289 @@
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"
"github.com/vikstrous/dataloadgen"
"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/probo"
"go.probo.inc/probo/pkg/server/api/console/v1/dataloader"
"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"
"go.probo.inc/probo/pkg/validator"
)
// CreateTask is the resolver for the createTask field.
func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTaskInput) (*types.CreateTaskPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionTaskCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
task, err := prb.Tasks.Create(
ctx,
probo.CreateTaskRequest{
MeasureID: input.MeasureID,
OrganizationID: input.OrganizationID,
Name: input.Name,
Description: input.Description,
Priority: input.Priority,
TimeEstimate: input.TimeEstimate,
AssignedToID: input.AssignedToID,
Deadline: input.Deadline,
},
)
if err != nil {
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create task", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateTaskPayload{
TaskEdge: types.NewTaskEdge(task, coredata.TaskOrderFieldCreatedAt),
}, nil
}
// UpdateTask is the resolver for the updateTask field.
func (r *mutationResolver) UpdateTask(ctx context.Context, input types.UpdateTaskInput) (*types.UpdateTaskPayload, error) {
if err := r.authorize(ctx, input.TaskID, probo.ActionTaskUpdate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.TaskID.TenantID())
task, err := prb.Tasks.Update(
ctx,
probo.UpdateTaskRequest{
TaskID: input.TaskID,
Name: input.Name,
Description: gqlutils.UnwrapOmittable(input.Description),
State: input.State,
Priority: input.Priority,
Rank: input.Rank,
TimeEstimate: gqlutils.UnwrapOmittable(input.TimeEstimate),
Deadline: gqlutils.UnwrapOmittable(input.Deadline),
AssignedToID: gqlutils.UnwrapOmittable(input.AssignedToID),
MeasureID: gqlutils.UnwrapOmittable(input.MeasureID),
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update task", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateTaskPayload{
Task: types.NewTask(task),
}, nil
}
// DeleteTask is the resolver for the deleteTask field.
func (r *mutationResolver) DeleteTask(ctx context.Context, input types.DeleteTaskInput) (*types.DeleteTaskPayload, error) {
if err := r.authorize(ctx, input.TaskID, probo.ActionTaskDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.TaskID.TenantID())
err := prb.Tasks.Delete(ctx, input.TaskID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete task", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteTaskPayload{
DeletedTaskID: input.TaskID,
}, nil
}
// Tasks is the resolver for the tasks field.
func (r *organizationResolver) Tasks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.TaskOrderBy) (*types.TaskConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionTaskList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.TaskOrderField]{
Field: coredata.TaskOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.TaskOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.Tasks.ListForOrganizationID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization tasks", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewTaskConnection(page, r, obj.ID), nil
}
// AssignedTo is the resolver for the assignedTo field.
func (r *taskResolver) AssignedTo(ctx context.Context, obj *types.Task) (*types.Profile, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil {
return nil, err
}
if obj.AssignedTo == nil {
return nil, nil
}
loaders := dataloader.FromContext(ctx)
assignee, err := loaders.Profile.Load(ctx, obj.AssignedTo.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get assigned to", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewProfile(assignee), nil
}
// Organization is the resolver for the organization field.
func (r *taskResolver) Organization(ctx context.Context, obj *types.Task) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
organization, err := loaders.Organization.Load(ctx, obj.Organization.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Measure is the resolver for the measure field.
func (r *taskResolver) Measure(ctx context.Context, obj *types.Task) (*types.Measure, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionMeasureGet); err != nil {
return nil, err
}
if obj.Measure == nil {
return nil, nil
}
loaders := dataloader.FromContext(ctx)
measure, err := loaders.Measure.Load(ctx, obj.Measure.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get measure", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMeasure(measure), nil
}
// Evidences is the resolver for the evidences field.
func (r *taskResolver) Evidences(ctx context.Context, obj *types.Task, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.EvidenceOrderBy) (*types.EvidenceConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionEvidenceList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.EvidenceOrderField]{
Field: coredata.EvidenceOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.EvidenceOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.Evidences.ListForTaskID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list task evidences", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewEvidenceConnection(page, r, obj.ID), nil
}
// Permission is the resolver for the permission field.
func (r *taskResolver) Permission(ctx context.Context, obj *types.Task, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *taskConnectionResolver) TotalCount(ctx context.Context, obj *types.TaskConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionTaskList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *measureResolver:
count, err := prb.Tasks.CountForMeasureID(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count tasks", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
case *organizationResolver:
count, err := prb.Tasks.CountForOrganizationID(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count tasks", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver")
return 0, gqlutils.Internal(ctx)
}
// Task returns schema.TaskResolver implementation.
func (r *Resolver) Task() schema.TaskResolver { return &taskResolver{r} }
// TaskConnection returns schema.TaskConnectionResolver implementation.
func (r *Resolver) TaskConnection() schema.TaskConnectionResolver { return &taskConnectionResolver{r} }
type taskResolver struct{ *Resolver }
type taskConnectionResolver struct{ *Resolver }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,261 @@
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"
"fmt"
"github.com/vikstrous/dataloadgen"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/probo"
"go.probo.inc/probo/pkg/server/api/console/v1/dataloader"
"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"
"go.probo.inc/probo/pkg/validator"
)
// CreateWebhookSubscription is the resolver for the createWebhookSubscription field.
func (r *mutationResolver) CreateWebhookSubscription(ctx context.Context, input types.CreateWebhookSubscriptionInput) (*types.CreateWebhookSubscriptionPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionWebhookSubscriptionCreate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.OrganizationID.TenantID())
wc, err := prb.WebhookSubscriptions.Create(
ctx,
probo.CreateWebhookSubscriptionRequest{
OrganizationID: input.OrganizationID,
EndpointURL: input.EndpointURL,
SelectedEvents: input.SelectedEvents,
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create webhook subscription", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateWebhookSubscriptionPayload{
WebhookSubscriptionEdge: types.NewWebhookSubscriptionEdge(wc, coredata.WebhookSubscriptionOrderFieldCreatedAt),
}, nil
}
// UpdateWebhookSubscription is the resolver for the updateWebhookSubscription field.
func (r *mutationResolver) UpdateWebhookSubscription(ctx context.Context, input types.UpdateWebhookSubscriptionInput) (*types.UpdateWebhookSubscriptionPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionWebhookSubscriptionUpdate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.ID.TenantID())
wc, err := prb.WebhookSubscriptions.Update(
ctx,
probo.UpdateWebhookSubscriptionRequest{
WebhookSubscriptionID: input.ID,
EndpointURL: input.EndpointURL,
SelectedEvents: input.SelectedEvents,
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update webhook subscription", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateWebhookSubscriptionPayload{
WebhookSubscription: types.NewWebhookSubscription(wc),
}, nil
}
// DeleteWebhookSubscription is the resolver for the deleteWebhookSubscription field.
func (r *mutationResolver) DeleteWebhookSubscription(ctx context.Context, input types.DeleteWebhookSubscriptionInput) (*types.DeleteWebhookSubscriptionPayload, error) {
if err := r.authorize(ctx, input.WebhookSubscriptionID, probo.ActionWebhookSubscriptionDelete); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.WebhookSubscriptionID.TenantID())
err := prb.WebhookSubscriptions.Delete(ctx, input.WebhookSubscriptionID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete webhook subscription", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteWebhookSubscriptionPayload{
DeletedWebhookSubscriptionID: input.WebhookSubscriptionID,
}, nil
}
// WebhookSubscriptions is the resolver for the webhookSubscriptions field.
func (r *organizationResolver) WebhookSubscriptions(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.WebhookSubscriptionOrderBy) (*types.WebhookSubscriptionConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionWebhookSubscriptionList); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.WebhookSubscriptionOrderField]{
Field: coredata.WebhookSubscriptionOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.WebhookSubscriptionOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.WebhookSubscriptions.ListForOrganizationID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization webhook subscriptions", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewWebhookSubscriptionConnection(page, r, obj.ID), nil
}
// TotalCount is the resolver for the totalCount field.
func (r *webhookEventConnectionResolver) TotalCount(ctx context.Context, obj *types.WebhookEventConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionWebhookSubscriptionGet); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
count, err := prb.WebhookSubscriptions.CountEventsForSubscriptionID(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count webhook events", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
// Organization is the resolver for the organization field.
func (r *webhookSubscriptionResolver) Organization(ctx context.Context, obj *types.WebhookSubscription) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
organization, err := loaders.Organization.Load(ctx, obj.Organization.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot load organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// SigningSecret is the resolver for the signingSecret field.
func (r *webhookSubscriptionResolver) SigningSecret(ctx context.Context, obj *types.WebhookSubscription) (string, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionWebhookSubscriptionUpdate); err != nil {
return "", err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
signingSecret, err := prb.WebhookSubscriptions.GetSigningSecret(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get signing secret", log.Error(err))
return "", gqlutils.Internal(ctx)
}
return signingSecret, nil
}
// Events is the resolver for the events field.
func (r *webhookSubscriptionResolver) Events(ctx context.Context, obj *types.WebhookSubscription, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.WebhookEventOrderBy) (*types.WebhookEventConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionWebhookSubscriptionGet); err != nil {
return nil, err
}
prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.WebhookEventOrderField]{
Field: coredata.WebhookEventOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.WebhookEventOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := prb.WebhookSubscriptions.ListEventsForSubscriptionID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list webhook events", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewWebhookEventConnection(page, r, obj.ID), nil
}
// Permission is the resolver for the permission field.
func (r *webhookSubscriptionResolver) Permission(ctx context.Context, obj *types.WebhookSubscription, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *webhookSubscriptionConnectionResolver) TotalCount(ctx context.Context, obj *types.WebhookSubscriptionConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionWebhookSubscriptionList); err != nil {
return 0, err
}
prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *organizationResolver:
count, err := prb.WebhookSubscriptions.CountForOrganizationID(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count webhook subscriptions", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver for webhook subscription connection", log.String("resolver", fmt.Sprintf("%T", obj.Resolver)))
return 0, gqlutils.Internal(ctx)
}
// WebhookEventConnection returns schema.WebhookEventConnectionResolver implementation.
func (r *Resolver) WebhookEventConnection() schema.WebhookEventConnectionResolver {
return &webhookEventConnectionResolver{r}
}
// WebhookSubscription returns schema.WebhookSubscriptionResolver implementation.
func (r *Resolver) WebhookSubscription() schema.WebhookSubscriptionResolver {
return &webhookSubscriptionResolver{r}
}
// WebhookSubscriptionConnection returns schema.WebhookSubscriptionConnectionResolver implementation.
func (r *Resolver) WebhookSubscriptionConnection() schema.WebhookSubscriptionConnectionResolver {
return &webhookSubscriptionConnectionResolver{r}
}
type webhookEventConnectionResolver struct{ *Resolver }
type webhookSubscriptionResolver struct{ *Resolver }
type webhookSubscriptionConnectionResolver struct{ *Resolver }

View File

@@ -0,0 +1,526 @@
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"
"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/page"
"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"
"go.probo.inc/probo/pkg/server/api/trust/v1/types"
"go.probo.inc/probo/pkg/server/gqlutils"
"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)
if identity == nil {
return nil, nil
}
return &types.Identity{
ID: identity.ID,
Email: identity.EmailAddress,
FullName: identity.FullName,
EmailVerified: identity.EmailAddressVerified,
CreatedAt: identity.CreatedAt,
UpdatedAt: identity.UpdatedAt,
}, nil
}
// Node is the resolver for the node field.
func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) {
trustService := r.TrustService(ctx, id.TenantID())
switch id.EntityType() {
case coredata.OrganizationEntityType:
organization, err := trustService.Organizations.Get(ctx, id)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
case coredata.DocumentEntityType:
trustCenter := compliancepage.CompliancePageFromContext(ctx)
document, err := trustService.Documents.Get(ctx, trustCenter.OrganizationID, id)
if err != nil {
if errors.Is(err, trust.ErrDocumentNotFound) || errors.Is(err, trust.ErrDocumentNotVisible) || errors.Is(err, coredata.ErrResourceNotFound) {
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
}
if _, ok := errors.AsType[*trust.ErrDocumentArchived](err); ok {
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
}
r.logger.ErrorCtx(ctx, "cannot get document", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewDocument(document), nil
case coredata.FrameworkEntityType:
framework, err := trustService.Frameworks.Get(ctx, id)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewFramework(framework), nil
case coredata.ReportEntityType:
trustCenter := compliancepage.CompliancePageFromContext(ctx)
report, err := trustService.Reports.Get(ctx, trustCenter.OrganizationID, id)
if err != nil {
if errors.Is(err, trust.ErrReportNotFound) || errors.Is(err, coredata.ErrResourceNotFound) {
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
}
r.logger.ErrorCtx(ctx, "cannot get report", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewReport(report), nil
case coredata.AuditEntityType:
audit, err := trustService.Audits.Get(ctx, id)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get audit", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewAudit(audit), nil
case coredata.VendorEntityType:
vendor, err := trustService.Vendors.Get(ctx, id)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get vendor", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSubprocessor(vendor), nil
case coredata.TrustCenterEntityType:
trustCenter, err := trustService.TrustCenters.Get(ctx, id)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get trust center", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewTrustCenter(trustCenter), nil
case coredata.TrustCenterReferenceEntityType:
reference, err := trustService.TrustCenterReferences.Get(ctx, id)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get trust center reference", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewTrustCenterReference(reference), nil
case coredata.TrustCenterFileEntityType:
trustCenter := compliancepage.CompliancePageFromContext(ctx)
trustCenterFile, err := trustService.TrustCenterFiles.Get(ctx, trustCenter.OrganizationID, id)
if err != nil {
if errors.Is(err, trust.ErrTrustCenterFileNotFound) || errors.Is(err, trust.ErrTrustCenterFileNotVisible) {
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
}
r.logger.ErrorCtx(ctx, "cannot get trust center file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewTrustCenterFile(trustCenterFile), nil
default:
return nil, gqlutils.NotFoundf(ctx, "node %q not found", id)
}
}
// CurrentTrustCenter is the resolver for the currentTrustCenter field.
func (r *queryResolver) CurrentTrustCenter(ctx context.Context) (*types.TrustCenter, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())
org, err := trustService.Organizations.Get(ctx, trustCenter.OrganizationID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
trustCenter, err = trustService.TrustCenters.Get(ctx, trustCenter.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get trust center", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
response := types.NewTrustCenter(trustCenter)
response.Organization = types.NewOrganization(org)
return response, nil
}
// OidcProviders is the resolver for the oidcProviders field.
func (r *queryResolver) OidcProviders(ctx context.Context) ([]*types.OIDCProviderInfo, error) {
providers := r.iam.OIDCService.EnabledProviders()
result := make([]*types.OIDCProviderInfo, 0, len(providers))
for _, p := range providers {
name := strings.ToLower(p.String())
result = append(result, &types.OIDCProviderInfo{
Name: name,
LoginURL: r.baseURL.WithPath("/api/connect/v1/oidc/" + name + "/login").MustString(),
})
}
return result, nil
}
// LogoFileURL is the resolver for the logoFileUrl field.
func (r *trustCenterResolver) LogoFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
return trustService.TrustCenters.GenerateLogoURL(ctx, obj.ID, 1*time.Hour)
}
// DarkLogoFileURL is the resolver for the darkLogoFileUrl field.
func (r *trustCenterResolver) DarkLogoFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
return trustService.TrustCenters.GenerateDarkLogoURL(ctx, obj.ID, 1*time.Hour)
}
// Organization is the resolver for the organization field.
func (r *trustCenterResolver) Organization(ctx context.Context, obj *types.TrustCenter) (*types.Organization, error) {
return obj.Organization, nil
}
// Documents is the resolver for the documents field.
func (r *trustCenterResolver) Documents(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.DocumentConnection, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.DocumentOrderField]{
Field: coredata.DocumentOrderFieldTitle,
Direction: page.OrderDirectionAsc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
documentPage, err := trustService.Documents.ListForOrganizationId(ctx, obj.Organization.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list public documents", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewDocumentConnection(documentPage), nil
}
// Audits is the resolver for the audits field.
func (r *trustCenterResolver) Audits(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.AuditConnection, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.AuditOrderField]{
Field: coredata.AuditOrderFieldValidFrom,
Direction: page.OrderDirectionDesc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
auditPage, err := trustService.Audits.ListForOrganizationId(ctx, obj.Organization.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list public audits", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewAuditConnection(auditPage), nil
}
// Subprocessors is the resolver for the subprocessors field.
func (r *trustCenterResolver) Subprocessors(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.SubprocessorConnection, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.VendorOrderField]{
Field: coredata.VendorOrderFieldName,
Direction: page.OrderDirectionAsc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
vendorPage, err := trustService.Vendors.ListForOrganizationId(ctx, obj.Organization.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list subprocessors", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSubprocessorConnection(vendorPage, r, obj.ID), nil
}
// References is the resolver for the references field.
func (r *trustCenterResolver) References(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.TrustCenterReferenceConnection, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.TrustCenterReferenceOrderField]{
Field: coredata.TrustCenterReferenceOrderFieldRank,
Direction: page.OrderDirectionAsc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
referencePage, err := trustService.TrustCenterReferences.ListForTrustCenterID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list public trust center references", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewTrustCenterReferenceConnection(referencePage), nil
}
// TrustCenterFiles is the resolver for the trustCenterFiles field.
func (r *trustCenterResolver) TrustCenterFiles(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.TrustCenterFileConnection, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.TrustCenterFileOrderField]{
Field: coredata.TrustCenterFileOrderFieldName,
Direction: page.OrderDirectionAsc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
filter := coredata.NewTrustCenterFileFilter(
coredata.WithTrustCenterFileVisibilities(
coredata.TrustCenterVisibilityPublic,
coredata.TrustCenterVisibilityPrivate,
),
)
trustCenterFilePage, err := trustService.TrustCenterFiles.ListForOrganizationId(ctx, obj.Organization.ID, cursor, filter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list public trust center files", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewTrustCenterFileConnection(trustCenterFilePage), nil
}
// ComplianceFrameworks is the resolver for the complianceFrameworks field.
func (r *trustCenterResolver) ComplianceFrameworks(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.ComplianceFrameworkConnection, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ComplianceFrameworkOrderField]{
Field: coredata.ComplianceFrameworkOrderFieldRank,
Direction: page.OrderDirectionAsc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
cfPage, err := trustService.ComplianceFrameworks.ListByTrustCenterID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list compliance frameworks", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewComplianceFrameworkConnection(cfPage), nil
}
// ExternalUrls is the resolver for the externalUrls field.
func (r *trustCenterResolver) ExternalUrls(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.ComplianceExternalURLConnection, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ComplianceExternalURLOrderField]{
Field: coredata.ComplianceExternalURLOrderFieldRank,
Direction: page.OrderDirectionAsc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
result, err := trustService.ComplianceExternalURLs.ListForTrustCenterID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list compliance external URLs", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewComplianceExternalURLConnection(result), nil
}
// 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} }
// TrustCenter returns schema.TrustCenterResolver implementation.
func (r *Resolver) TrustCenter() schema.TrustCenterResolver { return &trustCenterResolver{r} }
type mutationResolver struct{ *Resolver }
type organizationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
type trustCenterResolver struct{ *Resolver }

View File

@@ -1,5 +1,5 @@
schema:
- "schema.graphql"
- "graphql/*.graphql"
- "../../../gqlutils/directives/session/schema.graphql"
exec:
@@ -14,7 +14,7 @@ resolver:
layout: "follow-schema"
dir: "."
package: "trust_v1"
filename_template: "v1_resolver.go"
filename_template: "{name}.resolvers.go"
autobind: []
call_argument_directives_with_null: true

View File

@@ -0,0 +1,24 @@
input SendMagicLinkInput {
email: EmailAddr!
continue: String
}
type SendMagicLinkPayload {
success: Boolean!
}
input VerifyMagicLinkInput {
token: String!
}
type VerifyMagicLinkPayload {
continue: String
}
input UpdateFullNameInput {
fullName: String!
}
type UpdateFullNamePayload {
success: Boolean!
}

View File

@@ -1,4 +1,3 @@
# Directives
directive @goField(
forceResolver: Boolean
name: String
@@ -49,111 +48,9 @@ type Organization implements Node {
headquarterAddress: String
}
enum DocumentType
@goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentType") {
OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeOther")
GOVERNANCE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeGovernance")
POLICY @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePolicy")
PROCEDURE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeProcedure")
PLAN @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePlan")
REGISTER
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeRegister")
RECORD @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeRecord")
REPORT @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeReport")
TEMPLATE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeTemplate")
}
type Document implements Node @nda {
id: ID!
title: String!
documentType: DocumentType!
isUserAuthorized: Boolean! @goField(forceResolver: true)
access: DocumentAccess @goField(forceResolver: true)
}
type DocumentConnection @nda {
edges: [DocumentEdge!]!
pageInfo: PageInfo!
}
type DocumentEdge @nda {
cursor: CursorKey!
node: Document!
}
type Framework implements Node @nda {
id: ID!
type OIDCProviderInfo {
name: String!
lightLogoURL: String @goField(forceResolver: true)
darkLogoURL: String @goField(forceResolver: true)
}
type Report implements Node @nda {
id: ID!
filename: String!
isUserAuthorized: Boolean! @goField(forceResolver: true)
access: DocumentAccess @goField(forceResolver: true)
}
type Audit implements Node @nda {
id: ID!
name: String
framework: Framework! @goField(forceResolver: true)
report: Report @goField(forceResolver: true)
}
type AuditConnection @nda {
edges: [AuditEdge!]!
pageInfo: PageInfo!
}
type AuditEdge @nda {
cursor: CursorKey!
node: Audit!
}
type ComplianceFramework implements Node
@goModel(
model: "go.probo.inc/probo/pkg/server/api/trust/v1/types.ComplianceFramework"
) {
id: ID!
framework: Framework! @goField(forceResolver: true)
}
type ComplianceFrameworkConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/trust/v1/types.ComplianceFrameworkConnection"
) {
edges: [ComplianceFrameworkEdge!]!
pageInfo: PageInfo!
}
type ComplianceFrameworkEdge
@goModel(
model: "go.probo.inc/probo/pkg/server/api/trust/v1/types.ComplianceFrameworkEdge"
) {
cursor: CursorKey!
node: ComplianceFramework!
}
type MailingListUpdate implements Node {
id: ID!
title: String!
body: String!
updatedAt: Datetime!
}
type MailingListUpdateConnection {
edges: [MailingListUpdateEdge!]!
pageInfo: PageInfo!
}
type MailingListUpdateEdge {
cursor: CursorKey!
node: MailingListUpdate!
loginURL: String!
}
enum CountryCode
@@ -409,141 +306,13 @@ enum CountryCode
ZW @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeZW")
}
enum SubprocessorCategory
@goModel(model: "go.probo.inc/probo/pkg/coredata.VendorCategory") {
ANALYTICS
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryAnalytics")
CLOUD_MONITORING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCloudMonitoring"
)
CLOUD_PROVIDER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCloudProvider"
)
COLLABORATION
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCollaboration"
)
CUSTOMER_SUPPORT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCustomerSupport"
)
DATA_STORAGE_AND_PROCESSING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryDataStorageAndProcessing"
)
DOCUMENT_MANAGEMENT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryDocumentManagement"
)
EMPLOYEE_MANAGEMENT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEmployeeManagement"
)
ENGINEERING
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEngineering")
FINANCE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryFinance")
IDENTITY_PROVIDER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIdentityProvider"
)
IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIT")
MARKETING
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryMarketing")
OFFICE_OPERATIONS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOfficeOperations"
)
OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOther")
PASSWORD_MANAGEMENT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryPasswordManagement"
)
PRODUCT_AND_DESIGN
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryProductAndDesign"
)
PROFESSIONAL_SERVICES
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryProfessionalServices"
)
RECRUITING
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryRecruiting")
SALES @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySales")
SECURITY
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySecurity")
VERSION_CONTROL
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryVersionControl"
)
}
type Subprocessor implements Node @nda {
id: ID!
name: String!
description: String
category: SubprocessorCategory!
websiteUrl: String
privacyPolicyUrl: String
countries: [CountryCode!]!
}
type SubprocessorConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/trust/v1/types.SubprocessorConnection"
) @nda {
edges: [SubprocessorEdge!]!
pageInfo: PageInfo!
totalCount: Int! @goField(forceResolver: true)
}
type SubprocessorEdge @nda {
cursor: CursorKey!
node: Subprocessor!
}
type TrustCenterReference implements Node @nda {
id: ID!
name: String!
description: String
websiteUrl: String!
logoUrl: String! @goField(forceResolver: true)
}
type TrustCenterReferenceConnection @nda {
edges: [TrustCenterReferenceEdge!]!
pageInfo: PageInfo!
}
type TrustCenterReferenceEdge @nda {
cursor: CursorKey!
node: TrustCenterReference!
}
type TrustCenterFile implements Node @nda {
id: ID!
name: String!
category: String!
isUserAuthorized: Boolean! @goField(forceResolver: true)
access: DocumentAccess @goField(forceResolver: true)
}
type TrustCenterFileConnection @nda {
edges: [TrustCenterFileEdge!]!
pageInfo: PageInfo!
}
type TrustCenterFileEdge @nda {
cursor: CursorKey!
node: TrustCenterFile!
}
type NonDisclosureAgreement {
fileName: String!
fileUrl: String! @goField(forceResolver: true)
viewerSignature: ElectronicSignature @goField(forceResolver: true)
type Query {
viewer: Identity
node(id: ID!): Node
currentTrustCenter: TrustCenter
oidcProviders: [OIDCProviderInfo!]!
@goField(forceResolver: true)
@session(required: OPTIONAL)
}
type TrustCenter implements Node {
@@ -553,9 +322,6 @@ type TrustCenter implements Node {
logoFileUrl: String @goField(forceResolver: true)
darkLogoFileUrl: String @goField(forceResolver: true)
nonDisclosureAgreement: NonDisclosureAgreement @goField(forceResolver: true)
viewerSubscription: MailingListSubscriber @goField(forceResolver: true)
organization: Organization! @goField(forceResolver: true)
documents(
@@ -606,292 +372,6 @@ type TrustCenter implements Node {
last: Int
before: CursorKey
): ComplianceExternalURLConnection! @goField(forceResolver: true)
updates(
first: Int
after: CursorKey
last: Int
before: CursorKey
): MailingListUpdateConnection! @goField(forceResolver: true)
}
type ComplianceExternalURL implements Node {
id: ID!
name: String!
url: String!
rank: Int!
}
type ComplianceExternalURLConnection {
edges: [ComplianceExternalURLEdge!]!
pageInfo: PageInfo!
}
type ComplianceExternalURLEdge {
cursor: CursorKey!
node: ComplianceExternalURL!
}
type TrustCenterAccess implements Node {
id: ID!
email: EmailAddr!
name: String!
createdAt: Datetime!
updatedAt: Datetime!
}
enum DocumentAccessStatus
@goModel(
model: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatus"
) {
REQUESTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusRequested"
)
GRANTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusGranted"
)
REJECTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusRejected"
)
REVOKED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusRevoked"
)
}
type DocumentAccess implements Node {
id: ID!
status: DocumentAccessStatus!
}
input SendMagicLinkInput {
email: EmailAddr!
continue: String
}
type SendMagicLinkPayload {
success: Boolean!
}
input VerifyMagicLinkInput {
token: String!
}
type VerifyMagicLinkPayload {
continue: String
}
input UpdateFullNameInput {
fullName: String!
}
type UpdateFullNamePayload {
success: Boolean!
}
type RequestDocumentAccessPayload {
document: Document
}
type RequestReportAccessPayload {
audit: Audit
}
type RequestFileAccessPayload {
file: TrustCenterFile
}
type RequestAccessesPayload {
trustCenterAccess: TrustCenterAccess!
}
input ExportDocumentPDFInput {
documentId: ID!
}
input ExportReportPDFInput {
reportId: ID!
}
input RequestDocumentAccessInput {
documentId: ID!
}
input RequestReportAccessInput {
reportId: ID!
}
input RequestTrustCenterFileAccessInput {
trustCenterFileId: ID!
}
input ExportTrustCenterFileInput {
trustCenterFileId: ID!
}
type ExportDocumentPDFPayload {
data: String!
}
type ExportReportPDFPayload {
data: String!
}
type ExportTrustCenterFilePayload {
data: String!
}
# Electronic Signature
enum ElectronicSignatureStatus
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatus"
) {
PENDING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusPending"
)
ACCEPTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusAccepted"
)
PROCESSING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusProcessing"
)
COMPLETED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusCompleted"
)
FAILED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusFailed"
)
}
enum ElectronicSignatureDocumentType
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentType"
) {
NDA
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeNDA"
)
DPA
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeDPA"
)
MSA
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeMSA"
)
SOW
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeSOW"
)
SLA
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeSLA"
)
TOS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeTOS"
)
PRIVACY_POLICY
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypePrivacyPolicy"
)
OTHER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeOther"
)
}
enum ElectronicSignatureEventType
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventType"
) {
DOCUMENT_VIEWED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeDocumentViewed"
)
CONSENT_GIVEN
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeConsentGiven"
)
FULL_NAME_TYPED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeFullNameTyped"
)
SIGNATURE_ACCEPTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeSignatureAccepted"
)
SIGNATURE_COMPLETED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeSignatureCompleted"
)
SEAL_COMPUTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeSealComputed"
)
TIMESTAMP_REQUESTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeTimestampRequested"
)
CERTIFICATE_GENERATED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeCertificateGenerated"
)
PROCESSING_ERROR
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeProcessingError"
)
}
type ElectronicSignature implements Node {
id: ID!
status: ElectronicSignatureStatus!
documentType: ElectronicSignatureDocumentType!
consentText: String!
lastError: String
signedAt: Datetime
createdAt: Datetime!
updatedAt: Datetime!
}
input AcceptElectronicSignatureInput {
signatureId: ID!
}
type AcceptElectronicSignaturePayload {
signature: ElectronicSignature!
}
input RecordSigningEventInput {
signatureId: ID!
eventType: ElectronicSignatureEventType!
}
type RecordSigningEventPayload {
success: Boolean!
}
type OIDCProviderInfo {
name: String!
loginURL: String!
}
type Query {
viewer: Identity
node(id: ID!): Node
currentTrustCenter: TrustCenter
oidcProviders: [OIDCProviderInfo!]!
@goField(forceResolver: true)
@session(required: OPTIONAL)
}
type Mutation {
@@ -901,74 +381,4 @@ type Mutation {
@session(required: OPTIONAL)
updateFullName(input: UpdateFullNameInput!): UpdateFullNamePayload
@session(required: PRESENT)
requestAllAccesses: RequestAccessesPayload! @session(required: PRESENT) @nda
exportDocumentPDF(input: ExportDocumentPDFInput!): ExportDocumentPDFPayload!
@session(required: OPTIONAL) @nda
exportReportPDF(input: ExportReportPDFInput!): ExportReportPDFPayload!
@session(required: OPTIONAL) @nda
exportTrustCenterFile(
input: ExportTrustCenterFileInput!
): ExportTrustCenterFilePayload! @session(required: OPTIONAL) @nda
requestDocumentAccess(
input: RequestDocumentAccessInput!
): RequestDocumentAccessPayload! @session(required: PRESENT) @nda
requestReportAccess(
input: RequestReportAccessInput!
): RequestReportAccessPayload! @session(required: PRESENT) @nda
requestTrustCenterFileAccess(
input: RequestTrustCenterFileAccessInput!
): RequestFileAccessPayload! @session(required: PRESENT) @nda
acceptElectronicSignature(
input: AcceptElectronicSignatureInput!
): AcceptElectronicSignaturePayload @session(required: PRESENT)
recordSigningEvent(
input: RecordSigningEventInput!
): RecordSigningEventPayload @session(required: PRESENT)
subscribeToMailingList: SubscribeToMailingListPayload! @session(required: PRESENT)
unsubscribeFromMailingList: UnsubscribeFromMailingListPayload! @session(required: PRESENT)
}
type SubscribeToMailingListPayload {
subscription: MailingListSubscriber!
}
type UnsubscribeFromMailingListPayload {
deletedMailingListSubscriberId: ID
}
enum MailingListSubscriberStatus
@goModel(
model: "go.probo.inc/probo/pkg/coredata.MailingListSubscriberStatus"
) {
PENDING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MailingListSubscriberStatusPending"
)
CONFIRMED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MailingListSubscriberStatusConfirmed"
)
}
type MailingListSubscriber implements Node
@goModel(
model: "go.probo.inc/probo/pkg/server/api/trust/v1/types.MailingListSubscriber"
) {
id: ID!
fullName: String!
email: EmailAddr!
status: MailingListSubscriberStatus!
createdAt: Datetime!
updatedAt: Datetime!
}

View File

@@ -0,0 +1,67 @@
extend type TrustCenter {
viewerSubscription: MailingListSubscriber @goField(forceResolver: true)
updates(
first: Int
after: CursorKey
last: Int
before: CursorKey
): MailingListUpdateConnection! @goField(forceResolver: true)
}
type MailingListUpdate implements Node {
id: ID!
title: String!
body: String!
updatedAt: Datetime!
}
type MailingListUpdateConnection {
edges: [MailingListUpdateEdge!]!
pageInfo: PageInfo!
}
type MailingListUpdateEdge {
cursor: CursorKey!
node: MailingListUpdate!
}
enum MailingListSubscriberStatus
@goModel(
model: "go.probo.inc/probo/pkg/coredata.MailingListSubscriberStatus"
) {
PENDING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MailingListSubscriberStatusPending"
)
CONFIRMED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.MailingListSubscriberStatusConfirmed"
)
}
type MailingListSubscriber implements Node
@goModel(
model: "go.probo.inc/probo/pkg/server/api/trust/v1/types.MailingListSubscriber"
) {
id: ID!
fullName: String!
email: EmailAddr!
status: MailingListSubscriberStatus!
createdAt: Datetime!
updatedAt: Datetime!
}
extend type Mutation {
subscribeToMailingList: SubscribeToMailingListPayload! @session(required: PRESENT)
unsubscribeFromMailingList: UnsubscribeFromMailingListPayload! @session(required: PRESENT)
}
type SubscribeToMailingListPayload {
subscription: MailingListSubscriber!
}
type UnsubscribeFromMailingListPayload {
deletedMailingListSubscriberId: ID
}

View File

@@ -0,0 +1,153 @@
extend type TrustCenter {
nonDisclosureAgreement: NonDisclosureAgreement @goField(forceResolver: true)
}
type NonDisclosureAgreement {
fileName: String!
fileUrl: String! @goField(forceResolver: true)
viewerSignature: ElectronicSignature @goField(forceResolver: true)
}
enum ElectronicSignatureStatus
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatus"
) {
PENDING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusPending"
)
ACCEPTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusAccepted"
)
PROCESSING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusProcessing"
)
COMPLETED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusCompleted"
)
FAILED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureStatusFailed"
)
}
enum ElectronicSignatureDocumentType
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentType"
) {
NDA
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeNDA"
)
DPA
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeDPA"
)
MSA
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeMSA"
)
SOW
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeSOW"
)
SLA
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeSLA"
)
TOS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeTOS"
)
PRIVACY_POLICY
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypePrivacyPolicy"
)
OTHER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureDocumentTypeOther"
)
}
enum ElectronicSignatureEventType
@goModel(
model: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventType"
) {
DOCUMENT_VIEWED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeDocumentViewed"
)
CONSENT_GIVEN
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeConsentGiven"
)
FULL_NAME_TYPED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeFullNameTyped"
)
SIGNATURE_ACCEPTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeSignatureAccepted"
)
SIGNATURE_COMPLETED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeSignatureCompleted"
)
SEAL_COMPUTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeSealComputed"
)
TIMESTAMP_REQUESTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeTimestampRequested"
)
CERTIFICATE_GENERATED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeCertificateGenerated"
)
PROCESSING_ERROR
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.ElectronicSignatureEventTypeProcessingError"
)
}
type ElectronicSignature implements Node {
id: ID!
status: ElectronicSignatureStatus!
documentType: ElectronicSignatureDocumentType!
consentText: String!
lastError: String
signedAt: Datetime
createdAt: Datetime!
updatedAt: Datetime!
}
extend type Mutation {
acceptElectronicSignature(
input: AcceptElectronicSignatureInput!
): AcceptElectronicSignaturePayload @session(required: PRESENT)
recordSigningEvent(
input: RecordSigningEventInput!
): RecordSigningEventPayload @session(required: PRESENT)
}
input AcceptElectronicSignatureInput {
signatureId: ID!
}
type AcceptElectronicSignaturePayload {
signature: ElectronicSignature!
}
input RecordSigningEventInput {
signatureId: ID!
eventType: ElectronicSignatureEventType!
}
type RecordSigningEventPayload {
success: Boolean!
}

View File

@@ -0,0 +1,350 @@
enum DocumentType
@goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentType") {
OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeOther")
GOVERNANCE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeGovernance")
POLICY @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePolicy")
PROCEDURE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeProcedure")
PLAN @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePlan")
REGISTER
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeRegister")
RECORD @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeRecord")
REPORT @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeReport")
TEMPLATE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeTemplate")
}
type Document implements Node @nda {
id: ID!
title: String!
documentType: DocumentType!
isUserAuthorized: Boolean! @goField(forceResolver: true)
access: DocumentAccess @goField(forceResolver: true)
}
type DocumentConnection @nda {
edges: [DocumentEdge!]!
pageInfo: PageInfo!
}
type DocumentEdge @nda {
cursor: CursorKey!
node: Document!
}
type Framework implements Node @nda {
id: ID!
name: String!
lightLogoURL: String @goField(forceResolver: true)
darkLogoURL: String @goField(forceResolver: true)
}
type Report implements Node @nda {
id: ID!
filename: String!
isUserAuthorized: Boolean! @goField(forceResolver: true)
access: DocumentAccess @goField(forceResolver: true)
}
type Audit implements Node @nda {
id: ID!
name: String
framework: Framework! @goField(forceResolver: true)
report: Report @goField(forceResolver: true)
}
type AuditConnection @nda {
edges: [AuditEdge!]!
pageInfo: PageInfo!
}
type AuditEdge @nda {
cursor: CursorKey!
node: Audit!
}
type ComplianceFramework implements Node
@goModel(
model: "go.probo.inc/probo/pkg/server/api/trust/v1/types.ComplianceFramework"
) {
id: ID!
framework: Framework! @goField(forceResolver: true)
}
type ComplianceFrameworkConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/trust/v1/types.ComplianceFrameworkConnection"
) {
edges: [ComplianceFrameworkEdge!]!
pageInfo: PageInfo!
}
type ComplianceFrameworkEdge
@goModel(
model: "go.probo.inc/probo/pkg/server/api/trust/v1/types.ComplianceFrameworkEdge"
) {
cursor: CursorKey!
node: ComplianceFramework!
}
enum SubprocessorCategory
@goModel(model: "go.probo.inc/probo/pkg/coredata.VendorCategory") {
ANALYTICS
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryAnalytics")
CLOUD_MONITORING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCloudMonitoring"
)
CLOUD_PROVIDER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCloudProvider"
)
COLLABORATION
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCollaboration"
)
CUSTOMER_SUPPORT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCustomerSupport"
)
DATA_STORAGE_AND_PROCESSING
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryDataStorageAndProcessing"
)
DOCUMENT_MANAGEMENT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryDocumentManagement"
)
EMPLOYEE_MANAGEMENT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEmployeeManagement"
)
ENGINEERING
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEngineering")
FINANCE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryFinance")
IDENTITY_PROVIDER
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIdentityProvider"
)
IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIT")
MARKETING
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryMarketing")
OFFICE_OPERATIONS
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOfficeOperations"
)
OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOther")
PASSWORD_MANAGEMENT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryPasswordManagement"
)
PRODUCT_AND_DESIGN
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryProductAndDesign"
)
PROFESSIONAL_SERVICES
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryProfessionalServices"
)
RECRUITING
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryRecruiting")
SALES @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySales")
SECURITY
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySecurity")
VERSION_CONTROL
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryVersionControl"
)
}
type Subprocessor implements Node @nda {
id: ID!
name: String!
description: String
category: SubprocessorCategory!
websiteUrl: String
privacyPolicyUrl: String
countries: [CountryCode!]!
}
type SubprocessorConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/trust/v1/types.SubprocessorConnection"
) @nda {
edges: [SubprocessorEdge!]!
pageInfo: PageInfo!
totalCount: Int! @goField(forceResolver: true)
}
type SubprocessorEdge @nda {
cursor: CursorKey!
node: Subprocessor!
}
type TrustCenterReference implements Node @nda {
id: ID!
name: String!
description: String
websiteUrl: String!
logoUrl: String! @goField(forceResolver: true)
}
type TrustCenterReferenceConnection @nda {
edges: [TrustCenterReferenceEdge!]!
pageInfo: PageInfo!
}
type TrustCenterReferenceEdge @nda {
cursor: CursorKey!
node: TrustCenterReference!
}
type TrustCenterFile implements Node @nda {
id: ID!
name: String!
category: String!
isUserAuthorized: Boolean! @goField(forceResolver: true)
access: DocumentAccess @goField(forceResolver: true)
}
type TrustCenterFileConnection @nda {
edges: [TrustCenterFileEdge!]!
pageInfo: PageInfo!
}
type TrustCenterFileEdge @nda {
cursor: CursorKey!
node: TrustCenterFile!
}
type ComplianceExternalURL implements Node {
id: ID!
name: String!
url: String!
rank: Int!
}
type ComplianceExternalURLConnection {
edges: [ComplianceExternalURLEdge!]!
pageInfo: PageInfo!
}
type ComplianceExternalURLEdge {
cursor: CursorKey!
node: ComplianceExternalURL!
}
type TrustCenterAccess implements Node {
id: ID!
email: EmailAddr!
name: String!
createdAt: Datetime!
updatedAt: Datetime!
}
enum DocumentAccessStatus
@goModel(
model: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatus"
) {
REQUESTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusRequested"
)
GRANTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusGranted"
)
REJECTED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusRejected"
)
REVOKED
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.TrustCenterDocumentAccessStatusRevoked"
)
}
type DocumentAccess implements Node {
id: ID!
status: DocumentAccessStatus!
}
extend type Mutation {
requestAllAccesses: RequestAccessesPayload! @session(required: PRESENT) @nda
exportDocumentPDF(input: ExportDocumentPDFInput!): ExportDocumentPDFPayload!
@session(required: OPTIONAL) @nda
exportReportPDF(input: ExportReportPDFInput!): ExportReportPDFPayload!
@session(required: OPTIONAL) @nda
exportTrustCenterFile(
input: ExportTrustCenterFileInput!
): ExportTrustCenterFilePayload! @session(required: OPTIONAL) @nda
requestDocumentAccess(
input: RequestDocumentAccessInput!
): RequestDocumentAccessPayload! @session(required: PRESENT) @nda
requestReportAccess(
input: RequestReportAccessInput!
): RequestReportAccessPayload! @session(required: PRESENT) @nda
requestTrustCenterFileAccess(
input: RequestTrustCenterFileAccessInput!
): RequestFileAccessPayload! @session(required: PRESENT) @nda
}
type RequestDocumentAccessPayload {
document: Document
}
type RequestReportAccessPayload {
audit: Audit
}
type RequestFileAccessPayload {
file: TrustCenterFile
}
type RequestAccessesPayload {
trustCenterAccess: TrustCenterAccess!
}
input ExportDocumentPDFInput {
documentId: ID!
}
input ExportReportPDFInput {
reportId: ID!
}
input RequestDocumentAccessInput {
documentId: ID!
}
input RequestReportAccessInput {
reportId: ID!
}
input RequestTrustCenterFileAccessInput {
trustCenterFileId: ID!
}
input ExportTrustCenterFileInput {
trustCenterFileId: ID!
}
type ExportDocumentPDFPayload {
data: String!
}
type ExportReportPDFPayload {
data: String!
}
type ExportTrustCenterFilePayload {
data: String!
}

View File

@@ -0,0 +1,137 @@
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/coredata"
"go.probo.inc/probo/pkg/mailman"
"go.probo.inc/probo/pkg/page"
"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"
"go.probo.inc/probo/pkg/validator"
)
// SubscribeToMailingList is the resolver for the subscribeToMailingList field.
func (r *mutationResolver) SubscribeToMailingList(ctx context.Context) (*types.SubscribeToMailingListPayload, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
if trustCenter.MailingListID == nil {
return nil, gqlutils.NotFoundf(ctx, "mailing list not found")
}
identity := authn.IdentityFromContext(ctx)
subscriber, err := r.mailman.CreateSubscriber(
ctx,
&mailman.CreateSubscriberRequest{
MailingListID: *trustCenter.MailingListID,
Email: identity.EmailAddress,
FullName: identity.FullName,
},
)
if err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
if errors.Is(err, mailman.ErrSubscriberAlreadyExist) {
return nil, gqlutils.Conflictf(ctx, "already subscribed to this mailing list")
}
r.logger.ErrorCtx(ctx, "cannot subscribe to mailing list", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.SubscribeToMailingListPayload{
Subscription: types.NewMailingListSubscriber(subscriber),
}, nil
}
// UnsubscribeFromMailingList is the resolver for the unsubscribeFromMailingList field.
func (r *mutationResolver) UnsubscribeFromMailingList(ctx context.Context) (*types.UnsubscribeFromMailingListPayload, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
if trustCenter.MailingListID == nil {
return nil, gqlutils.NotFoundf(ctx, "mailing list not found")
}
identity := authn.IdentityFromContext(ctx)
subscriber, err := r.mailman.GetSubscriber(ctx, *trustCenter.MailingListID, identity.EmailAddress)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get mailing list subscription", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if subscriber == nil {
return nil, gqlutils.NotFoundf(ctx, "not subscribed to this mailing list")
}
if err := r.mailman.DeleteSubscriber(ctx, subscriber.ID); err != nil {
if errors.Is(err, mailman.ErrSubscriberNotFound) {
return nil, gqlutils.NotFoundf(ctx, "not subscribed to this mailing list")
}
r.logger.ErrorCtx(ctx, "cannot unsubscribe from mailing list", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UnsubscribeFromMailingListPayload{DeletedMailingListSubscriberID: &subscriber.ID}, nil
}
// ViewerSubscription is the resolver for the viewerSubscription field.
func (r *trustCenterResolver) ViewerSubscription(ctx context.Context, obj *types.TrustCenter) (*types.MailingListSubscriber, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
if trustCenter.MailingListID == nil {
return nil, nil
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, nil
}
subscriber, err := r.mailman.GetSubscriber(ctx, *trustCenter.MailingListID, identity.EmailAddress)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get mailing list subscription", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if subscriber == nil {
return nil, nil
}
return types.NewMailingListSubscriber(subscriber), nil
}
// Updates is the resolver for the updates field.
func (r *trustCenterResolver) Updates(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.MailingListUpdateConnection, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
tc, err := trustService.TrustCenters.Get(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load trust center", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if tc.MailingListID == nil {
return &types.MailingListUpdateConnection{Edges: []*types.MailingListUpdateEdge{}, PageInfo: &types.PageInfo{}}, nil
}
pageOrderBy := page.OrderBy[coredata.MailingListUpdateOrderField]{
Field: coredata.MailingListUpdateOrderFieldUpdatedAt,
Direction: page.OrderDirectionDesc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
result, err := r.mailman.ListSentMailingListUpdates(ctx, *tc.MailingListID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list mailing list updates", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMailingListUpdateConnection(result), nil
}

View File

@@ -0,0 +1,166 @@
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"
"net"
"time"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/esign"
"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"
"go.probo.inc/probo/pkg/server/api/trust/v1/types"
"go.probo.inc/probo/pkg/server/gqlutils"
)
// AcceptElectronicSignature is the resolver for the acceptElectronicSignature field.
func (r *mutationResolver) AcceptElectronicSignature(ctx context.Context, input types.AcceptElectronicSignatureInput) (*types.AcceptElectronicSignaturePayload, error) {
var (
identity = authn.IdentityFromContext(ctx)
httpReq = gqlutils.HTTPRequestFromContext(ctx)
)
signerIP, _, _ := net.SplitHostPort(httpReq.RemoteAddr)
if signerIP == "" {
signerIP = httpReq.RemoteAddr
}
signature, err := r.esign.AcceptSignature(
ctx,
&esign.AcceptSignatureRequest{
SignatureID: input.SignatureID,
SignerFullName: identity.FullName,
SignerEmail: identity.EmailAddress,
SignerIPAddr: signerIP,
SignerUA: httpReq.UserAgent(),
},
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot accept electronic signature", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.AcceptElectronicSignaturePayload{
Signature: types.NewElectronicSignature(signature),
}, nil
}
// RecordSigningEvent is the resolver for the recordSigningEvent field.
func (r *mutationResolver) RecordSigningEvent(ctx context.Context, input types.RecordSigningEventInput) (*types.RecordSigningEventPayload, error) {
var (
identity = authn.IdentityFromContext(ctx)
httpReq = gqlutils.HTTPRequestFromContext(ctx)
)
actorIP, _, _ := net.SplitHostPort(httpReq.RemoteAddr)
if actorIP == "" {
actorIP = httpReq.RemoteAddr
}
if err := r.esign.RecordEvent(
ctx,
&esign.RecordEventRequest{
SignatureID: input.SignatureID,
EventType: input.EventType,
EventSource: coredata.ElectronicSignatureEventSourceClient,
ActorEmail: identity.EmailAddress,
ActorIPAddr: actorIP,
ActorUA: httpReq.UserAgent(),
},
); err != nil {
r.logger.ErrorCtx(ctx, "cannot record signing event", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RecordSigningEventPayload{Success: true}, nil
}
// FileURL is the resolver for the fileUrl field.
func (r *nonDisclosureAgreementResolver) FileURL(ctx context.Context, obj *types.NonDisclosureAgreement) (string, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
if identity := authn.IdentityFromContext(ctx); identity != nil && r.esign != nil {
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())
access, err := trustService.TrustCenterAccesses.GetAccess(ctx, trustCenter.ID, identity.ID)
if err == nil && access.ElectronicSignatureID != nil {
fileURL, err := r.esign.GenerateSignatureFileURL(ctx, *access.ElectronicSignatureID, 15*time.Minute)
if err == nil {
return fileURL, nil
}
r.logger.ErrorCtx(ctx, "cannot generate signature file URL, falling back to original NDA", log.Error(err))
}
}
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())
fileURL, err := trustService.TrustCenters.GenerateNDAFileURL(ctx, trustCenter.ID, 15*time.Minute)
if err != nil {
return "", gqlutils.Internal(ctx)
}
return fileURL, nil
}
// ViewerSignature is the resolver for the viewerSignature field.
func (r *nonDisclosureAgreementResolver) ViewerSignature(ctx context.Context, obj *types.NonDisclosureAgreement) (*types.ElectronicSignature, error) {
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, nil
}
trustCenter := compliancepage.CompliancePageFromContext(ctx)
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())
access, err := trustService.TrustCenterAccesses.GetAccess(ctx, trustCenter.ID, identity.ID)
if err != nil {
return nil, nil
}
if access.ElectronicSignatureID == nil {
return nil, nil
}
sig, err := r.esign.GetSignatureByID(ctx, *access.ElectronicSignatureID)
if err != nil {
return nil, nil
}
return types.NewElectronicSignature(sig), nil
}
// NonDisclosureAgreement is the resolver for the nonDisclosureAgreement field.
func (r *trustCenterResolver) NonDisclosureAgreement(ctx context.Context, obj *types.TrustCenter) (*types.NonDisclosureAgreement, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
if trustCenter.NonDisclosureAgreementFileID == nil {
return nil, nil
}
trustService := r.TrustService(ctx, obj.ID.TenantID())
file, err := trustService.TrustCenters.GetNDAFile(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load NDA file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if file == nil {
return nil, nil
}
return types.NewNonDisclosureAgreement(file), nil
}
// NonDisclosureAgreement returns schema.NonDisclosureAgreementResolver implementation.
func (r *Resolver) NonDisclosureAgreement() schema.NonDisclosureAgreementResolver {
return &nonDisclosureAgreementResolver{r}
}
type nonDisclosureAgreementResolver struct{ *Resolver }

View File

@@ -0,0 +1,745 @@
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"
"encoding/base64"
"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/server/api/authn"
"go.probo.inc/probo/pkg/server/api/compliancepage"
"go.probo.inc/probo/pkg/server/api/trust/v1/schema"
"go.probo.inc/probo/pkg/server/api/trust/v1/types"
"go.probo.inc/probo/pkg/server/gqlutils"
"go.probo.inc/probo/pkg/trust"
)
// Framework is the resolver for the framework field.
func (r *auditResolver) Framework(ctx context.Context, obj *types.Audit) (*types.Framework, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
audit, err := trustService.Audits.Get(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
framework, err := trustService.Frameworks.Get(ctx, audit.FrameworkID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewFramework(framework), nil
}
// Report is the resolver for the report field.
func (r *auditResolver) Report(ctx context.Context, obj *types.Audit) (*types.Report, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
audit, err := trustService.Audits.Get(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if audit.ReportID == nil {
return nil, nil
}
trustCenter := compliancepage.CompliancePageFromContext(ctx)
report, err := trustService.Reports.Get(ctx, trustCenter.OrganizationID, *audit.ReportID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load report", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewReport(report), nil
}
// Framework is the resolver for the framework field on ComplianceFramework.
func (r *complianceFrameworkResolver) Framework(ctx context.Context, obj *types.ComplianceFramework) (*types.Framework, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
framework, err := trustService.Frameworks.Get(ctx, obj.FrameworkID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load framework", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewFramework(framework), nil
}
// IsUserAuthorized is the resolver for the isUserAuthorized field.
func (r *documentResolver) IsUserAuthorized(ctx context.Context, obj *types.Document) (bool, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
trustCenter := compliancepage.CompliancePageFromContext(ctx)
document, err := trustService.Documents.Get(ctx, trustCenter.OrganizationID, obj.ID)
if err != nil {
if errors.Is(err, trust.ErrDocumentNotFound) || errors.Is(err, trust.ErrDocumentNotVisible) || errors.Is(err, coredata.ErrResourceNotFound) {
return false, gqlutils.NotFoundf(ctx, "document %q not found", obj.ID)
}
if _, ok := errors.AsType[*trust.ErrDocumentArchived](err); ok {
return false, gqlutils.NotFoundf(ctx, "document %q not found", obj.ID)
}
r.logger.ErrorCtx(ctx, "cannot load document", log.Error(err))
return false, gqlutils.Internal(ctx)
}
if document.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
return true, nil
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return false, nil
}
documentAccess, err := trustService.TrustCenterAccesses.GetDocumentAccess(
ctx,
trustCenter.ID,
identity.ID,
obj.ID,
)
if err != nil {
if errors.Is(err, trust.ErrMembershipNotFound) ||
errors.Is(err, trust.ErrUserNotFound) ||
errors.Is(err, trust.ErrUserInactive) ||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
return false, nil
}
r.logger.ErrorCtx(ctx, "cannot check document access", log.Error(err))
return false, gqlutils.Internal(ctx)
}
return documentAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
}
// Access is the resolver for the access field.
func (r *documentResolver) Access(ctx context.Context, obj *types.Document) (*types.DocumentAccess, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
trustCenter := compliancepage.CompliancePageFromContext(ctx)
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, nil // User is not authenticated, so no access requested
}
access, err := trustService.TrustCenterAccesses.GetDocumentAccess(
ctx,
trustCenter.ID,
identity.ID,
obj.ID,
)
if err != nil {
if errors.Is(err, trust.ErrMembershipNotFound) ||
errors.Is(err, trust.ErrUserNotFound) ||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
return nil, nil
}
if errors.Is(err, trust.ErrUserInactive) {
return nil, gqlutils.Forbidden(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get document access", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DocumentAccess{
ID: access.ID,
Status: access.Status,
}, nil
}
// LightLogoURL is the resolver for the lightLogoURL field.
func (r *frameworkResolver) LightLogoURL(ctx context.Context, obj *types.Framework) (*string, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
return trustService.Frameworks.GenerateLightLogoURL(ctx, obj.ID, 1*time.Hour)
}
// DarkLogoURL is the resolver for the darkLogoURL field.
func (r *frameworkResolver) DarkLogoURL(ctx context.Context, obj *types.Framework) (*string, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
return trustService.Frameworks.GenerateDarkLogoURL(ctx, obj.ID, 1*time.Hour)
}
// RequestAllAccesses is the resolver for the requestAllAccesses field.
func (r *mutationResolver) RequestAllAccesses(ctx context.Context) (*types.RequestAccessesPayload, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access")
}
access, err := trustService.TrustCenterAccesses.Request(
ctx,
&trust.TrustCenterAccessRequest{
TrustCenterID: trustCenter.ID,
IdentityID: identity.ID,
DocumentIDs: nil,
ReportIDs: nil,
},
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create trust center access", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RequestAccessesPayload{
TrustCenterAccess: &types.TrustCenterAccess{
ID: access.ID,
CreatedAt: access.CreatedAt,
UpdatedAt: access.UpdatedAt,
},
}, nil
}
// ExportDocumentPDF is the resolver for the exportDocumentPDF field.
func (r *mutationResolver) ExportDocumentPDF(ctx context.Context, input types.ExportDocumentPDFInput) (*types.ExportDocumentPDFPayload, error) {
trustService := r.TrustService(ctx, input.DocumentID.TenantID())
trustCenter := compliancepage.CompliancePageFromContext(ctx)
document, err := trustService.Documents.Get(ctx, trustCenter.OrganizationID, input.DocumentID)
if err != nil {
if errors.Is(err, trust.ErrDocumentNotFound) || errors.Is(err, trust.ErrDocumentNotVisible) || errors.Is(err, coredata.ErrResourceNotFound) {
return nil, gqlutils.NotFoundf(ctx, "document %q not found", input.DocumentID)
}
if _, ok := errors.AsType[*trust.ErrDocumentArchived](err); ok {
return nil, gqlutils.NotFoundf(ctx, "document %q not found", input.DocumentID)
}
r.logger.ErrorCtx(ctx, "cannot load document", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if document.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
pdf, err := trustService.Documents.ExportPDFWithoutWatermark(ctx, input.DocumentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot export document PDF", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportDocumentPDFPayload{
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
}, nil
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, gqlutils.Unauthenticated(ctx, errors.New("unauthenticated"))
}
documentAccess, err := trustService.TrustCenterAccesses.GetDocumentAccess(
ctx,
trustCenter.ID,
identity.ID,
input.DocumentID,
)
if err != nil {
return nil, nil
}
if documentAccess.Status != coredata.TrustCenterDocumentAccessStatusGranted {
return nil, gqlutils.Forbiddenf(ctx, "access denied: no permission to access this document")
}
pdf, err := trustService.Documents.ExportPDF(ctx, input.DocumentID, identity.EmailAddress)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot export document PDF", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportDocumentPDFPayload{
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
}, nil
}
// ExportReportPDF is the resolver for the exportReportPDF field.
func (r *mutationResolver) ExportReportPDF(ctx context.Context, input types.ExportReportPDFInput) (*types.ExportReportPDFPayload, error) {
trustService := r.TrustService(ctx, input.ReportID.TenantID())
trustCenter := compliancepage.CompliancePageFromContext(ctx)
audit, err := trustService.Audits.GetByReportID(ctx, input.ReportID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
pdf, err := trustService.Reports.ExportPDFWithoutWatermark(ctx, input.ReportID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot export report PDF", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportReportPDFPayload{
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
}, nil
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
}
reportAccess, err := trustService.TrustCenterAccesses.GetReportAccess(
ctx,
trustCenter.ID,
identity.ID,
input.ReportID,
)
if err != nil {
return nil, nil
}
if reportAccess.Status != coredata.TrustCenterDocumentAccessStatusGranted {
return nil, gqlutils.Forbiddenf(ctx, "access denied: no permission to access this report")
}
pdf, err := trustService.Reports.ExportPDF(ctx, input.ReportID, identity.EmailAddress)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot export report PDF", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportReportPDFPayload{
Data: fmt.Sprintf("data:application/pdf;base64,%s", base64.StdEncoding.EncodeToString(pdf)),
}, nil
}
// ExportTrustCenterFile is the resolver for the exportTrustCenterFile field.
func (r *mutationResolver) ExportTrustCenterFile(ctx context.Context, input types.ExportTrustCenterFileInput) (*types.ExportTrustCenterFilePayload, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())
trustCenterFile, err := trustService.TrustCenterFiles.Get(ctx, trustCenter.OrganizationID, input.TrustCenterFileID)
if err != nil {
if errors.Is(err, trust.ErrTrustCenterFileNotFound) || errors.Is(err, trust.ErrTrustCenterFileNotVisible) {
return nil, gqlutils.NotFoundf(ctx, "trust center file %q not found", input.TrustCenterFileID)
}
r.logger.ErrorCtx(ctx, "cannot load trust center file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if trustCenterFile.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
fileData, mimeType, err := trustService.TrustCenterFiles.ExportFileWithoutWatermark(ctx, input.TrustCenterFileID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot export trust center file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportTrustCenterFilePayload{
Data: fmt.Sprintf("data:%s;base64,%s", mimeType, base64.StdEncoding.EncodeToString(fileData)),
}, nil
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, gqlutils.Unauthenticatedf(ctx, "unauthenticated")
}
fileAccess, err := trustService.TrustCenterAccesses.GetTrustCenterFileAccess(ctx,
trustCenter.ID,
identity.ID,
input.TrustCenterFileID,
)
if err != nil {
return nil, nil
}
if fileAccess.Status != coredata.TrustCenterDocumentAccessStatusGranted {
return nil, gqlutils.Forbiddenf(ctx, "access denied: no permission to access this file")
}
fileData, mimeType, err := trustService.TrustCenterFiles.ExportFile(ctx, input.TrustCenterFileID, identity.EmailAddress)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot export trust center file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ExportTrustCenterFilePayload{
Data: fmt.Sprintf("data:%s;base64,%s", mimeType, base64.StdEncoding.EncodeToString(fileData)),
}, nil
}
// RequestDocumentAccess is the resolver for the requestDocumentAccess field.
func (r *mutationResolver) RequestDocumentAccess(ctx context.Context, input types.RequestDocumentAccessInput) (*types.RequestDocumentAccessPayload, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())
document, err := trustService.Documents.Get(ctx, trustCenter.OrganizationID, input.DocumentID)
if err != nil {
if errors.Is(err, trust.ErrDocumentNotFound) || errors.Is(err, trust.ErrDocumentNotVisible) || errors.Is(err, coredata.ErrResourceNotFound) {
return nil, gqlutils.NotFoundf(ctx, "document %q not found", input.DocumentID)
}
if _, ok := errors.AsType[*trust.ErrDocumentArchived](err); ok {
return nil, gqlutils.NotFoundf(ctx, "document %q not found", input.DocumentID)
}
r.logger.ErrorCtx(ctx, "cannot load document", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if document.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
return nil, gqlutils.Invalidf(
ctx,
"document is publicly available and does not require access request",
)
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access")
}
if _, err := trustService.TrustCenterAccesses.Request(
ctx,
&trust.TrustCenterAccessRequest{
TrustCenterID: trustCenter.ID,
IdentityID: identity.ID,
DocumentIDs: []gid.GID{input.DocumentID},
ReportIDs: []gid.GID{},
TrustCenterFileIDs: []gid.GID{},
},
); err != nil {
r.logger.ErrorCtx(ctx, "cannot request document access", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RequestDocumentAccessPayload{
Document: types.NewDocument(document),
}, nil
}
// RequestReportAccess is the resolver for the requestReportAccess field.
func (r *mutationResolver) RequestReportAccess(ctx context.Context, input types.RequestReportAccessInput) (*types.RequestReportAccessPayload, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())
audit, err := trustService.Audits.GetByReportID(ctx, input.ReportID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load audit", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
return nil, gqlutils.Invalidf(
ctx,
"report is publicly available and does not require access request",
)
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access")
}
if _, err := trustService.TrustCenterAccesses.Request(
ctx,
&trust.TrustCenterAccessRequest{
TrustCenterID: trustCenter.ID,
IdentityID: identity.ID,
DocumentIDs: []gid.GID{},
ReportIDs: []gid.GID{input.ReportID},
TrustCenterFileIDs: []gid.GID{},
},
); err != nil {
r.logger.ErrorCtx(ctx, "cannot request report access", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RequestReportAccessPayload{
Audit: types.NewAudit(audit),
}, nil
}
// RequestTrustCenterFileAccess is the resolver for the requestTrustCenterFileAccess field.
func (r *mutationResolver) RequestTrustCenterFileAccess(ctx context.Context, input types.RequestTrustCenterFileAccessInput) (*types.RequestFileAccessPayload, error) {
trustCenter := compliancepage.CompliancePageFromContext(ctx)
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())
trustCenterFile, err := trustService.TrustCenterFiles.Get(ctx, trustCenter.OrganizationID, input.TrustCenterFileID)
if err != nil {
if errors.Is(err, trust.ErrTrustCenterFileNotFound) || errors.Is(err, trust.ErrTrustCenterFileNotVisible) {
return nil, gqlutils.NotFoundf(ctx, "trust center file %q not found", input.TrustCenterFileID)
}
r.logger.ErrorCtx(ctx, "cannot load trust center file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if trustCenterFile.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
return nil, gqlutils.Invalidf(
ctx,
"trust center file is publicly available and does not require access request",
)
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access")
}
if _, err := trustService.TrustCenterAccesses.Request(
ctx,
&trust.TrustCenterAccessRequest{
TrustCenterID: trustCenter.ID,
IdentityID: identity.ID,
DocumentIDs: []gid.GID{},
ReportIDs: []gid.GID{},
TrustCenterFileIDs: []gid.GID{input.TrustCenterFileID},
},
); err != nil {
r.logger.ErrorCtx(ctx, "cannot request trust center file access", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RequestFileAccessPayload{
File: types.NewTrustCenterFile(trustCenterFile),
}, nil
}
// IsUserAuthorized is the resolver for the isUserAuthorized field.
func (r *reportResolver) IsUserAuthorized(ctx context.Context, obj *types.Report) (bool, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
trustCenter := compliancepage.CompliancePageFromContext(ctx)
audit, err := trustService.Audits.GetByReportID(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load document", log.Error(err))
return false, gqlutils.Internal(ctx)
}
if audit.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
return true, nil
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return false, nil
}
reportAccess, err := trustService.TrustCenterAccesses.GetReportAccess(ctx,
trustCenter.ID,
identity.ID,
obj.ID,
)
if err != nil {
if errors.Is(err, trust.ErrMembershipNotFound) ||
errors.Is(err, trust.ErrUserNotFound) ||
errors.Is(err, trust.ErrUserInactive) ||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
return false, nil
}
r.logger.ErrorCtx(ctx, "cannot check report access", log.Error(err))
return false, gqlutils.Internal(ctx)
}
return reportAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
}
// Access is the resolver for the access field.
func (r *reportResolver) Access(ctx context.Context, obj *types.Report) (*types.DocumentAccess, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
trustCenter := compliancepage.CompliancePageFromContext(ctx)
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, nil // User is not authenticated, so no access requested
}
access, err := trustService.TrustCenterAccesses.GetReportAccess(
ctx,
trustCenter.ID,
identity.ID,
obj.ID,
)
if err != nil {
if errors.Is(err, trust.ErrMembershipNotFound) ||
errors.Is(err, trust.ErrUserNotFound) ||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
return nil, nil
}
if errors.Is(err, trust.ErrUserInactive) {
return nil, gqlutils.Forbidden(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get audit report access", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DocumentAccess{
ID: access.ID,
Status: access.Status,
}, nil
}
// TotalCount is the resolver for the totalCount field.
func (r *subprocessorConnectionResolver) TotalCount(ctx context.Context, obj *types.SubprocessorConnection) (int, error) {
trustService := r.TrustService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) {
case *trustCenterResolver:
count, err := trustService.Vendors.CountForTrustCenterId(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count subprocessors", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
r.logger.ErrorCtx(ctx, "not implemented: TotalCount for parent type")
return 0, gqlutils.Internal(ctx)
}
// IsUserAuthorized is the resolver for the isUserAuthorized field.
func (r *trustCenterFileResolver) IsUserAuthorized(ctx context.Context, obj *types.TrustCenterFile) (bool, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
trustCenter := compliancepage.CompliancePageFromContext(ctx)
trustCenterFile, err := trustService.TrustCenterFiles.Get(ctx, trustCenter.OrganizationID, obj.ID)
if err != nil {
if errors.Is(err, trust.ErrTrustCenterFileNotFound) || errors.Is(err, trust.ErrTrustCenterFileNotVisible) {
return false, gqlutils.NotFoundf(ctx, "trust center file %q not found", obj.ID)
}
r.logger.ErrorCtx(ctx, "cannot load trust center file", log.Error(err))
return false, gqlutils.Internal(ctx)
}
if trustCenterFile.TrustCenterVisibility == coredata.TrustCenterVisibilityPublic {
return true, nil
}
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return false, nil
}
fileAccess, err := trustService.TrustCenterAccesses.GetTrustCenterFileAccess(ctx,
trustCenter.ID,
identity.ID,
obj.ID,
)
if err != nil {
if errors.Is(err, trust.ErrMembershipNotFound) ||
errors.Is(err, trust.ErrUserNotFound) ||
errors.Is(err, trust.ErrUserInactive) ||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
return false, nil
}
r.logger.ErrorCtx(ctx, "cannot check trust center file access", log.Error(err))
return false, gqlutils.Internal(ctx)
}
return fileAccess.Status == coredata.TrustCenterDocumentAccessStatusGranted, nil
}
// Access is the resolver for the access field.
func (r *trustCenterFileResolver) Access(ctx context.Context, obj *types.TrustCenterFile) (*types.DocumentAccess, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
trustCenter := compliancepage.CompliancePageFromContext(ctx)
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, nil // User is not authenticated, so no access requested
}
access, err := trustService.TrustCenterAccesses.GetTrustCenterFileAccess(
ctx,
trustCenter.ID,
identity.ID,
obj.ID,
)
if err != nil {
if errors.Is(err, trust.ErrMembershipNotFound) ||
errors.Is(err, trust.ErrUserNotFound) ||
errors.Is(err, trust.ErrDocumentAccessNotFound) {
return nil, nil
}
if errors.Is(err, trust.ErrUserInactive) {
return nil, gqlutils.Forbidden(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get file access", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DocumentAccess{
ID: access.ID,
Status: access.Status,
}, nil
}
// LogoURL is the resolver for the logoUrl field.
func (r *trustCenterReferenceResolver) LogoURL(ctx context.Context, obj *types.TrustCenterReference) (string, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
logoURL, err := trustService.TrustCenterReferences.GenerateLogoURL(ctx, obj.ID, 1*time.Hour)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate logo URL", log.Error(err))
return "", gqlutils.Internal(ctx)
}
return logoURL, nil
}
// Audit returns schema.AuditResolver implementation.
func (r *Resolver) Audit() schema.AuditResolver { return &auditResolver{r} }
// ComplianceFramework returns schema.ComplianceFrameworkResolver implementation.
func (r *Resolver) ComplianceFramework() schema.ComplianceFrameworkResolver {
return &complianceFrameworkResolver{r}
}
// Document returns schema.DocumentResolver implementation.
func (r *Resolver) Document() schema.DocumentResolver { return &documentResolver{r} }
// Framework returns schema.FrameworkResolver implementation.
func (r *Resolver) Framework() schema.FrameworkResolver { return &frameworkResolver{r} }
// Report returns schema.ReportResolver implementation.
func (r *Resolver) Report() schema.ReportResolver { return &reportResolver{r} }
// SubprocessorConnection returns schema.SubprocessorConnectionResolver implementation.
func (r *Resolver) SubprocessorConnection() schema.SubprocessorConnectionResolver {
return &subprocessorConnectionResolver{r}
}
// TrustCenterFile returns schema.TrustCenterFileResolver implementation.
func (r *Resolver) TrustCenterFile() schema.TrustCenterFileResolver {
return &trustCenterFileResolver{r}
}
// TrustCenterReference returns schema.TrustCenterReferenceResolver implementation.
func (r *Resolver) TrustCenterReference() schema.TrustCenterReferenceResolver {
return &trustCenterReferenceResolver{r}
}
type auditResolver struct{ *Resolver }
type complianceFrameworkResolver struct{ *Resolver }
type documentResolver struct{ *Resolver }
type frameworkResolver struct{ *Resolver }
type reportResolver struct{ *Resolver }
type subprocessorConnectionResolver struct{ *Resolver }
type trustCenterFileResolver struct{ *Resolver }
type trustCenterReferenceResolver struct{ *Resolver }

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,8 @@
},
"projects": {
"core": {
"schema": "pkg/server/api/console/v1/schema.graphql",
"schema": "pkg/server/api/console/v1/graphql/base.graphql",
"schemaExtensions": ["pkg/server/api/console/v1/graphql"],
"language": "typescript",
"noFutureProofEnums": true,
"output": "apps/console/src/__generated__/core",
@@ -22,7 +23,8 @@
}
},
"iam": {
"schema": "pkg/server/api/connect/v1/schema.graphql",
"schema": "pkg/server/api/connect/v1/graphql/base.graphql",
"schemaExtensions": ["pkg/server/api/connect/v1/graphql"],
"language": "typescript",
"noFutureProofEnums": true,
"output": "apps/console/src/__generated__/iam",
@@ -37,7 +39,8 @@
}
},
"trust": {
"schema": "pkg/server/api/trust/v1/schema.graphql",
"schema": "pkg/server/api/trust/v1/graphql/base.graphql",
"schemaExtensions": ["pkg/server/api/trust/v1/graphql"],
"language": "typescript",
"noFutureProofEnums": true,
"customScalarTypes": {