Files
probo/pkg/server/api/connect/v1/organization_resolvers.go
Sacha Al Himdani 83e7b3bdd4 Fix lint issues in connect v1 resolvers
Drop the redundant embedded Resolver selector flagged by staticcheck
(QF1008) in the membership and organization permission resolvers, and
add the whitespace wsl_v5 expects around the attribute range loop and
the final return in permission.

Signed-off-by: Sacha Al Himdani <sacha@probo.com>
2026-07-08 22:02:31 +02:00

369 lines
12 KiB
Go

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.93
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/iam/scim/bridge/provider/googleworkspace"
"go.probo.inc/probo/pkg/iam/scim/bridge/provider/microsoft365"
"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"
)
// 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"))
}
// Logo is the resolver for the logo field.
func (r *organizationResolver) Logo(ctx context.Context, obj *types.Organization) (*types.File, error) {
file, err := r.iam.OrganizationService.LogoFile(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load logo file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if file == nil {
return nil, nil
}
return types.NewFile(file, r.fileManager), nil
}
// HorizontalLogo is the resolver for the horizontalLogo field.
func (r *organizationResolver) HorizontalLogo(ctx context.Context, obj *types.Organization) (*types.File, error) {
file, err := r.iam.OrganizationService.HorizontalLogoFile(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load horizontal logo file", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if file == nil {
return nil, nil
}
return types.NewFile(file, r.fileManager), 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
}
// 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
}
// 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 {
if _, ok := errors.AsType[*iam.ErrNoSCIMConfigurationFound](err); ok {
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,
},
{
Type: coredata.SCIMBridgeTypeMicrosoft365,
Oauth2Scopes: microsoft365.OAuth2Scopes,
},
}, 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
}
// 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 {
if _, ok := errors.AsType[*iam.ErrProfileNotFound](err); ok {
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, attributes map[string]any) (bool, error) {
return r.permission(ctx, obj, action, attributes)
}
// Organization returns schema.OrganizationResolver implementation.
func (r *Resolver) Organization() schema.OrganizationResolver { return &organizationResolver{r} }
type organizationResolver struct{ *Resolver }