diff --git a/plugins/organizations/hooks_executor.go b/plugins/organizations/hooks_executor.go deleted file mode 100644 index 50e9f660..00000000 --- a/plugins/organizations/hooks_executor.go +++ /dev/null @@ -1,203 +0,0 @@ -package organizations - -import "github.com/Authula/authula/plugins/organizations/types" - -type OrganizationsHookExecutor struct { - config *types.OrganizationsDatabaseHooksConfig -} - -func NewOrganizationsHookExecutor(config *types.OrganizationsDatabaseHooksConfig) *OrganizationsHookExecutor { - return &OrganizationsHookExecutor{config: config} -} - -// Organization Hooks - -func (e *OrganizationsHookExecutor) BeforeCreateOrganization(organization *types.Organization) error { - if e == nil || e.config == nil || e.config.Organizations == nil || e.config.Organizations.BeforeCreate == nil { - return nil - } - return e.config.Organizations.BeforeCreate(organization) -} - -func (e *OrganizationsHookExecutor) AfterCreateOrganization(organization types.Organization) error { - if e == nil || e.config == nil || e.config.Organizations == nil || e.config.Organizations.AfterCreate == nil { - return nil - } - return e.config.Organizations.AfterCreate(organization) -} - -func (e *OrganizationsHookExecutor) BeforeUpdateOrganization(organization *types.Organization) error { - if e == nil || e.config == nil || e.config.Organizations == nil || e.config.Organizations.BeforeUpdate == nil { - return nil - } - return e.config.Organizations.BeforeUpdate(organization) -} - -func (e *OrganizationsHookExecutor) AfterUpdateOrganization(organization types.Organization) error { - if e == nil || e.config == nil || e.config.Organizations == nil || e.config.Organizations.AfterUpdate == nil { - return nil - } - return e.config.Organizations.AfterUpdate(organization) -} - -func (e *OrganizationsHookExecutor) BeforeDeleteOrganization(organization *types.Organization) error { - if e == nil || e.config == nil || e.config.Organizations == nil || e.config.Organizations.BeforeDelete == nil { - return nil - } - return e.config.Organizations.BeforeDelete(organization) -} - -func (e *OrganizationsHookExecutor) AfterDeleteOrganization(organization types.Organization) error { - if e == nil || e.config == nil || e.config.Organizations == nil || e.config.Organizations.AfterDelete == nil { - return nil - } - return e.config.Organizations.AfterDelete(organization) -} - -// Organization Member Hooks - -func (e *OrganizationsHookExecutor) BeforeCreateOrganizationMember(member *types.OrganizationMember) error { - if e == nil || e.config == nil || e.config.Members == nil || e.config.Members.BeforeCreate == nil { - return nil - } - return e.config.Members.BeforeCreate(member) -} - -func (e *OrganizationsHookExecutor) AfterCreateOrganizationMember(member types.OrganizationMember) error { - if e == nil || e.config == nil || e.config.Members == nil || e.config.Members.AfterCreate == nil { - return nil - } - return e.config.Members.AfterCreate(member) -} - -func (e *OrganizationsHookExecutor) BeforeUpdateOrganizationMember(member *types.OrganizationMember) error { - if e == nil || e.config == nil || e.config.Members == nil || e.config.Members.BeforeUpdate == nil { - return nil - } - return e.config.Members.BeforeUpdate(member) -} - -func (e *OrganizationsHookExecutor) AfterUpdateOrganizationMember(member types.OrganizationMember) error { - if e == nil || e.config == nil || e.config.Members == nil || e.config.Members.AfterUpdate == nil { - return nil - } - return e.config.Members.AfterUpdate(member) -} - -func (e *OrganizationsHookExecutor) BeforeDeleteOrganizationMember(member *types.OrganizationMember) error { - if e == nil || e.config == nil || e.config.Members == nil || e.config.Members.BeforeDelete == nil { - return nil - } - return e.config.Members.BeforeDelete(member) -} - -func (e *OrganizationsHookExecutor) AfterDeleteOrganizationMember(member types.OrganizationMember) error { - if e == nil || e.config == nil || e.config.Members == nil || e.config.Members.AfterDelete == nil { - return nil - } - return e.config.Members.AfterDelete(member) -} - -// Organization Invitation Hooks - -func (e *OrganizationsHookExecutor) BeforeCreateOrganizationInvitation(invitation *types.OrganizationInvitation) error { - if e == nil || e.config == nil || e.config.Invitations == nil || e.config.Invitations.BeforeCreate == nil { - return nil - } - return e.config.Invitations.BeforeCreate(invitation) -} - -func (e *OrganizationsHookExecutor) AfterCreateOrganizationInvitation(invitation types.OrganizationInvitation) error { - if e == nil || e.config == nil || e.config.Invitations == nil || e.config.Invitations.AfterCreate == nil { - return nil - } - return e.config.Invitations.AfterCreate(invitation) -} - -func (e *OrganizationsHookExecutor) BeforeUpdateOrganizationInvitation(invitation *types.OrganizationInvitation) error { - if e == nil || e.config == nil || e.config.Invitations == nil || e.config.Invitations.BeforeUpdate == nil { - return nil - } - return e.config.Invitations.BeforeUpdate(invitation) -} - -func (e *OrganizationsHookExecutor) AfterUpdateOrganizationInvitation(invitation types.OrganizationInvitation) error { - if e == nil || e.config == nil || e.config.Invitations == nil || e.config.Invitations.AfterUpdate == nil { - return nil - } - return e.config.Invitations.AfterUpdate(invitation) -} - -// Organization Team Hooks - -func (e *OrganizationsHookExecutor) BeforeCreateOrganizationTeam(team *types.OrganizationTeam) error { - if e == nil || e.config == nil || e.config.Teams == nil || e.config.Teams.BeforeCreate == nil { - return nil - } - return e.config.Teams.BeforeCreate(team) -} - -func (e *OrganizationsHookExecutor) AfterCreateOrganizationTeam(team types.OrganizationTeam) error { - if e == nil || e.config == nil || e.config.Teams == nil || e.config.Teams.AfterCreate == nil { - return nil - } - return e.config.Teams.AfterCreate(team) -} - -func (e *OrganizationsHookExecutor) BeforeUpdateOrganizationTeam(team *types.OrganizationTeam) error { - if e == nil || e.config == nil || e.config.Teams == nil || e.config.Teams.BeforeUpdate == nil { - return nil - } - return e.config.Teams.BeforeUpdate(team) -} - -func (e *OrganizationsHookExecutor) AfterUpdateOrganizationTeam(team types.OrganizationTeam) error { - if e == nil || e.config == nil || e.config.Teams == nil || e.config.Teams.AfterUpdate == nil { - return nil - } - return e.config.Teams.AfterUpdate(team) -} - -func (e *OrganizationsHookExecutor) BeforeDeleteOrganizationTeam(team *types.OrganizationTeam) error { - if e == nil || e.config == nil || e.config.Teams == nil || e.config.Teams.BeforeDelete == nil { - return nil - } - return e.config.Teams.BeforeDelete(team) -} - -func (e *OrganizationsHookExecutor) AfterDeleteOrganizationTeam(team types.OrganizationTeam) error { - if e == nil || e.config == nil || e.config.Teams == nil || e.config.Teams.AfterDelete == nil { - return nil - } - return e.config.Teams.AfterDelete(team) -} - -// Organization Team Member Hooks - -func (e *OrganizationsHookExecutor) BeforeCreateOrganizationTeamMember(member *types.OrganizationTeamMember) error { - if e == nil || e.config == nil || e.config.TeamMembers == nil || e.config.TeamMembers.BeforeCreate == nil { - return nil - } - return e.config.TeamMembers.BeforeCreate(member) -} - -func (e *OrganizationsHookExecutor) AfterCreateOrganizationTeamMember(member types.OrganizationTeamMember) error { - if e == nil || e.config == nil || e.config.TeamMembers == nil || e.config.TeamMembers.AfterCreate == nil { - return nil - } - return e.config.TeamMembers.AfterCreate(member) -} - -func (e *OrganizationsHookExecutor) BeforeDeleteOrganizationTeamMember(member *types.OrganizationTeamMember) error { - if e == nil || e.config == nil || e.config.TeamMembers == nil || e.config.TeamMembers.BeforeDelete == nil { - return nil - } - return e.config.TeamMembers.BeforeDelete(member) -} - -func (e *OrganizationsHookExecutor) AfterDeleteOrganizationTeamMember(member types.OrganizationTeamMember) error { - if e == nil || e.config == nil || e.config.TeamMembers == nil || e.config.TeamMembers.AfterDelete == nil { - return nil - } - return e.config.TeamMembers.AfterDelete(member) -} diff --git a/plugins/organizations/hooks_executor_test.go b/plugins/organizations/hooks_executor_test.go deleted file mode 100644 index 7b308410..00000000 --- a/plugins/organizations/hooks_executor_test.go +++ /dev/null @@ -1,149 +0,0 @@ -package organizations - -import ( - "errors" - "testing" - - "github.com/Authula/authula/plugins/organizations/types" -) - -func TestOrganizationsHookExecutor_NilHooksAreNoop(t *testing.T) { - t.Parallel() - - executor := NewOrganizationsHookExecutor(nil) - - if err := executor.BeforeCreateOrganization(&types.Organization{ID: "org-1"}); err != nil { - t.Fatalf("expected nil error, got %v", err) - } - if err := executor.AfterCreateOrganization(types.Organization{ID: "org-1"}); err != nil { - t.Fatalf("expected nil error, got %v", err) - } - if err := executor.BeforeCreateOrganizationInvitation(&types.OrganizationInvitation{ID: "inv-1"}); err != nil { - t.Fatalf("expected nil error, got %v", err) - } - if err := executor.AfterCreateOrganizationTeam(types.OrganizationTeam{ID: "team-1"}); err != nil { - t.Fatalf("expected nil error, got %v", err) - } -} - -func TestOrganizationsHookExecutor_OrganizationCreateHooks(t *testing.T) { - t.Parallel() - - var beforeCalled bool - var afterCalled bool - - executor := NewOrganizationsHookExecutor(&types.OrganizationsDatabaseHooksConfig{ - Organizations: &types.OrganizationDatabaseHooksConfig{ - BeforeCreate: func(organization *types.Organization) error { - beforeCalled = true - if organization == nil { - return errors.New("organization is nil") - } - if organization.ID != "org-1" { - t.Fatalf("unexpected organization ID: %s", organization.ID) - } - return nil - }, - AfterCreate: func(organization types.Organization) error { - afterCalled = true - if organization.ID != "org-1" { - t.Fatalf("unexpected organization ID: %s", organization.ID) - } - return nil - }, - }, - }) - - organization := &types.Organization{ID: "org-1", Name: "Acme"} - if err := executor.BeforeCreateOrganization(organization); err != nil { - t.Fatalf("expected nil error, got %v", err) - } - if err := executor.AfterCreateOrganization(*organization); err != nil { - t.Fatalf("expected nil error, got %v", err) - } - - if !beforeCalled { - t.Fatal("expected BeforeCreate hook to be called") - } - if !afterCalled { - t.Fatal("expected AfterCreate hook to be called") - } -} - -func TestOrganizationsHookExecutor_OrganizationCreateHookError(t *testing.T) { - t.Parallel() - - someErr := errors.New("some error") - executor := NewOrganizationsHookExecutor(&types.OrganizationsDatabaseHooksConfig{ - Organizations: &types.OrganizationDatabaseHooksConfig{ - BeforeCreate: func(organization *types.Organization) error { - return someErr - }, - }, - }) - - err := executor.BeforeCreateOrganization(&types.Organization{ID: "org-1"}) - if !errors.Is(err, someErr) { - t.Fatalf("expected someErr error, got %v", err) - } -} - -func TestOrganizationsHookExecutor_MemberUpdateDeleteHooks(t *testing.T) { - t.Parallel() - - var beforeUpdateCalled bool - var afterUpdateCalled bool - var beforeDeleteCalled bool - var afterDeleteCalled bool - - executor := NewOrganizationsHookExecutor(&types.OrganizationsDatabaseHooksConfig{ - Members: &types.OrganizationMemberDatabaseHooksConfig{ - BeforeUpdate: func(member *types.OrganizationMember) error { - beforeUpdateCalled = true - if member == nil || member.ID != "mem-1" { - t.Fatalf("unexpected member in before update hook: %+v", member) - } - return nil - }, - AfterUpdate: func(member types.OrganizationMember) error { - afterUpdateCalled = true - if member.ID != "mem-1" { - t.Fatalf("unexpected member in after update hook: %+v", member) - } - return nil - }, - BeforeDelete: func(member *types.OrganizationMember) error { - beforeDeleteCalled = true - if member == nil || member.ID != "mem-1" { - t.Fatalf("unexpected member in before delete hook: %+v", member) - } - return nil - }, - AfterDelete: func(member types.OrganizationMember) error { - afterDeleteCalled = true - if member.ID != "mem-1" { - t.Fatalf("unexpected member in after delete hook: %+v", member) - } - return nil - }, - }, - }) - - member := &types.OrganizationMember{ID: "mem-1", Role: "member"} - if err := executor.BeforeUpdateOrganizationMember(member); err != nil { - t.Fatalf("expected nil error, got %v", err) - } - if err := executor.AfterUpdateOrganizationMember(*member); err != nil { - t.Fatalf("expected nil error, got %v", err) - } - if err := executor.BeforeDeleteOrganizationMember(member); err != nil { - t.Fatalf("expected nil error, got %v", err) - } - if err := executor.AfterDeleteOrganizationMember(*member); err != nil { - t.Fatalf("expected nil error, got %v", err) - } - - if !beforeUpdateCalled || !afterUpdateCalled || !beforeDeleteCalled || !afterDeleteCalled { - t.Fatal("expected member update and delete hooks to be called") - } -} diff --git a/plugins/organizations/plugin.go b/plugins/organizations/plugin.go index 12452493..c5b93596 100644 --- a/plugins/organizations/plugin.go +++ b/plugins/organizations/plugin.go @@ -34,7 +34,7 @@ type OrganizationsPlugin struct { teamService services.OrganizationTeamService teamMemberService services.OrganizationTeamMemberService accessControlService rootservices.AccessControlService - databaseHooks *OrganizationsHookExecutor + hooksExecutor *services.ServiceHookExecutor emailTemplateManager *emailtmpl.Manager useCases *usecases.UseCases } @@ -84,25 +84,25 @@ func (p *OrganizationsPlugin) Init(ctx *models.PluginContext) error { return err } - p.databaseHooks = NewOrganizationsHookExecutor(p.pluginConfig.DatabaseHooks) - p.organizationRepo = repositories.NewBunOrganizationRepository(ctx.DB, p.databaseHooks) - p.invitationRepo = repositories.NewBunOrganizationInvitationRepository(ctx.DB, p.databaseHooks) - p.memberRepo = repositories.NewBunOrganizationMemberRepository(ctx.DB, p.databaseHooks) - p.teamRepo = repositories.NewBunOrganizationTeamRepository(ctx.DB, p.databaseHooks) - p.teamMemberRepo = repositories.NewBunOrganizationTeamMemberRepository(ctx.DB, p.databaseHooks) + p.hooksExecutor = services.NewServiceHookExecutor(p.pluginConfig.ServiceHooks) + p.organizationRepo = repositories.NewBunOrganizationRepository(ctx.DB) + p.invitationRepo = repositories.NewBunOrganizationInvitationRepository(ctx.DB) + p.memberRepo = repositories.NewBunOrganizationMemberRepository(ctx.DB) + p.teamRepo = repositories.NewBunOrganizationTeamRepository(ctx.DB) + p.teamMemberRepo = repositories.NewBunOrganizationTeamMemberRepository(ctx.DB) p.serviceUtils = services.NewServiceUtils(p.organizationRepo, p.memberRepo, p.teamRepo, p.teamMemberRepo) - p.organizationService = services.NewOrganizationService(p.organizationRepo, p.memberRepo, p.serviceUtils, accessControlService, p.pluginConfig.OrganizationsLimit, ctx.DB) + p.organizationService = services.NewOrganizationService(p.organizationRepo, p.memberRepo, p.serviceUtils, accessControlService, p.pluginConfig.OrganizationsLimit, ctx.DB, p.hooksExecutor) emailTemplateManager, err := newOrganizationEmailTemplateManager() if err != nil { return fmt.Errorf("failed to initialize organization email templates: %w", err) } p.emailTemplateManager = emailTemplateManager - p.invitationService = services.NewOrganizationInvitationService(ctx.DB, p.globalConfig, &p.pluginConfig, p.logger, ctx.EventBus, userService, mailerService, accessControlService, p.organizationRepo, p.invitationRepo, p.memberRepo, p.serviceUtils, p.emailTemplateManager) - p.memberService = services.NewOrganizationMemberService(userService, accessControlService, p.organizationRepo, p.memberRepo, p.pluginConfig.MembersLimit, ctx.DB, p.serviceUtils) - p.teamService = services.NewOrganizationTeamService(p.organizationRepo, p.memberRepo, p.teamRepo, p.teamMemberRepo, p.serviceUtils, ctx.DB) - p.teamMemberService = services.NewOrganizationTeamMemberService(p.organizationRepo, p.memberRepo, p.teamRepo, p.teamMemberRepo, p.serviceUtils) + p.invitationService = services.NewOrganizationInvitationService(ctx.DB, p.globalConfig, &p.pluginConfig, p.logger, ctx.EventBus, userService, mailerService, accessControlService, p.organizationRepo, p.invitationRepo, p.memberRepo, p.serviceUtils, p.emailTemplateManager, p.hooksExecutor) + p.memberService = services.NewOrganizationMemberService(userService, accessControlService, p.organizationRepo, p.memberRepo, p.pluginConfig.MembersLimit, ctx.DB, p.serviceUtils, p.hooksExecutor) + p.teamService = services.NewOrganizationTeamService(p.organizationRepo, p.memberRepo, p.teamRepo, p.teamMemberRepo, p.serviceUtils, ctx.DB, p.hooksExecutor) + p.teamMemberService = services.NewOrganizationTeamMemberService(p.organizationRepo, p.memberRepo, p.teamRepo, p.teamMemberRepo, p.serviceUtils, p.hooksExecutor) authorizer := rootservices.NewDefaultAuthorizer() p.useCases = usecases.NewUseCases(p.organizationService, p.invitationService, p.memberService, p.teamService, p.teamMemberService, authorizer) diff --git a/plugins/organizations/repositories/bun_organization_invitation_repository.go b/plugins/organizations/repositories/bun_organization_invitation_repository.go index ea53d354..888d8d8a 100644 --- a/plugins/organizations/repositories/bun_organization_invitation_repository.go +++ b/plugins/organizations/repositories/bun_organization_invitation_repository.go @@ -11,26 +11,15 @@ import ( ) type BunOrganizationInvitationRepository struct { - db bun.IDB - hooks OrganizationInvitationHookExecutor + db bun.IDB } -func NewBunOrganizationInvitationRepository(db bun.IDB, hooks ...OrganizationInvitationHookExecutor) OrganizationInvitationRepository { - var hook OrganizationInvitationHookExecutor - if len(hooks) > 0 { - hook = hooks[0] - } - return &BunOrganizationInvitationRepository{db: db, hooks: hook} +func NewBunOrganizationInvitationRepository(db bun.IDB) OrganizationInvitationRepository { + return &BunOrganizationInvitationRepository{db: db} } func (r *BunOrganizationInvitationRepository) Create(ctx context.Context, invitation *types.OrganizationInvitation) (*types.OrganizationInvitation, error) { err := r.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { - if r.hooks != nil { - if err := r.hooks.BeforeCreateOrganizationInvitation(invitation); err != nil { - return err - } - } - _, err := tx.NewInsert().Model(invitation).Exec(ctx) if err != nil { return err @@ -40,12 +29,6 @@ func (r *BunOrganizationInvitationRepository) Create(ctx context.Context, invita return err } - if r.hooks != nil { - if err := r.hooks.AfterCreateOrganizationInvitation(*invitation); err != nil { - return err - } - } - return nil }) if err != nil { @@ -111,12 +94,6 @@ func (r *BunOrganizationInvitationRepository) GetAllPendingByEmail(ctx context.C func (r *BunOrganizationInvitationRepository) Update(ctx context.Context, invitation *types.OrganizationInvitation) (*types.OrganizationInvitation, error) { err := r.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { - if r.hooks != nil { - if err := r.hooks.BeforeUpdateOrganizationInvitation(invitation); err != nil { - return err - } - } - _, err := tx.NewUpdate().Model(invitation).WherePK().Exec(ctx) if err != nil { return err @@ -126,12 +103,6 @@ func (r *BunOrganizationInvitationRepository) Update(ctx context.Context, invita return err } - if r.hooks != nil { - if err := r.hooks.AfterUpdateOrganizationInvitation(*invitation); err != nil { - return err - } - } - return nil }) if err != nil { @@ -146,5 +117,5 @@ func (r *BunOrganizationInvitationRepository) CountByOrganizationIDAndEmail(ctx } func (r *BunOrganizationInvitationRepository) WithTx(tx bun.IDB) OrganizationInvitationRepository { - return &BunOrganizationInvitationRepository{db: tx, hooks: r.hooks} + return &BunOrganizationInvitationRepository{db: tx} } diff --git a/plugins/organizations/repositories/bun_organization_invitation_repository_test.go b/plugins/organizations/repositories/bun_organization_invitation_repository_test.go index 8ca0e628..a93b72d6 100644 --- a/plugins/organizations/repositories/bun_organization_invitation_repository_test.go +++ b/plugins/organizations/repositories/bun_organization_invitation_repository_test.go @@ -554,86 +554,3 @@ func TestBunOrganizationInvitationRepository_WithTx(t *testing.T) { }) } } - -func TestBunOrganizationInvitationRepository_Hooks(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - run func(*testing.T) - }{ - { - name: "create hooks", - run: func(t *testing.T) { - t.Helper() - db := plugintests.SetupRepoDB(t) - plugintests.SeedOrganization(t, db, "org-1", "user-1", "Acme Inc", "acme-inc") - - beforeCalled := false - afterCalled := false - hooks := &plugintests.MockOrganizationInvitationHooks{ - Before: func(invitation *types.OrganizationInvitation) error { - beforeCalled = true - require.Equal(t, "inv-1", invitation.ID) - return nil - }, - After: func(invitation types.OrganizationInvitation) error { - afterCalled = true - require.Equal(t, "inv-1", invitation.ID) - return nil - }, - } - - repo := repositories.NewBunOrganizationInvitationRepository(db, hooks) - created, err := repo.Create(context.Background(), &types.OrganizationInvitation{ID: "inv-1", Email: "user@example.com", InviterID: "user-1", OrganizationID: "org-1", Role: "member", Status: types.OrganizationInvitationStatusPending, ExpiresAt: time.Now().UTC().Add(time.Hour)}) - require.NoError(t, err) - require.NotNil(t, created) - require.True(t, beforeCalled) - require.True(t, afterCalled) - }, - }, - { - name: "update hooks", - run: func(t *testing.T) { - t.Helper() - db := plugintests.SetupRepoDB(t) - plugintests.SeedOrganization(t, db, "org-1", "user-1", "Acme Inc", "acme-inc") - - seedRepo := repositories.NewBunOrganizationInvitationRepository(db) - ctx := context.Background() - invitation, err := seedRepo.Create(ctx, &types.OrganizationInvitation{ID: "inv-1", Email: "user@example.com", InviterID: "user-1", OrganizationID: "org-1", Role: "member", Status: types.OrganizationInvitationStatusPending, ExpiresAt: time.Now().UTC().Add(time.Hour)}) - require.NoError(t, err) - - beforeCalled := false - afterCalled := false - hooks := &plugintests.MockOrganizationInvitationHooks{ - BeforeUpdate: func(invitation *types.OrganizationInvitation) error { - beforeCalled = true - require.Equal(t, types.OrganizationInvitationStatusAccepted, invitation.Status) - return nil - }, - AfterUpdate: func(invitation types.OrganizationInvitation) error { - afterCalled = true - require.Equal(t, types.OrganizationInvitationStatusAccepted, invitation.Status) - return nil - }, - } - - repo := repositories.NewBunOrganizationInvitationRepository(db, hooks) - invitation.Status = types.OrganizationInvitationStatusAccepted - updated, err := repo.Update(ctx, invitation) - require.NoError(t, err) - require.Equal(t, types.OrganizationInvitationStatusAccepted, updated.Status) - require.True(t, beforeCalled) - require.True(t, afterCalled) - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - tt.run(t) - }) - } -} diff --git a/plugins/organizations/repositories/bun_organization_member_repository.go b/plugins/organizations/repositories/bun_organization_member_repository.go index e9f64940..7781ba11 100644 --- a/plugins/organizations/repositories/bun_organization_member_repository.go +++ b/plugins/organizations/repositories/bun_organization_member_repository.go @@ -10,26 +10,15 @@ import ( ) type BunOrganizationMemberRepository struct { - db bun.IDB - hooks OrganizationMemberHookExecutor + db bun.IDB } -func NewBunOrganizationMemberRepository(db bun.IDB, hooks ...OrganizationMemberHookExecutor) OrganizationMemberRepository { - var hook OrganizationMemberHookExecutor - if len(hooks) > 0 { - hook = hooks[0] - } - return &BunOrganizationMemberRepository{db: db, hooks: hook} +func NewBunOrganizationMemberRepository(db bun.IDB) OrganizationMemberRepository { + return &BunOrganizationMemberRepository{db: db} } func (r *BunOrganizationMemberRepository) Create(ctx context.Context, member *types.OrganizationMember) (*types.OrganizationMember, error) { err := r.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { - if r.hooks != nil { - if err := r.hooks.BeforeCreateOrganizationMember(member); err != nil { - return err - } - } - _, err := tx.NewInsert().Model(member).Exec(ctx) if err != nil { return err @@ -39,12 +28,6 @@ func (r *BunOrganizationMemberRepository) Create(ctx context.Context, member *ty return err } - if r.hooks != nil { - if err := r.hooks.AfterCreateOrganizationMember(*member); err != nil { - return err - } - } - return nil }) if err != nil { @@ -104,12 +87,6 @@ func (r *BunOrganizationMemberRepository) GetByOrganizationIDAndUserID(ctx conte func (r *BunOrganizationMemberRepository) Update(ctx context.Context, member *types.OrganizationMember) (*types.OrganizationMember, error) { err := r.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { - if r.hooks != nil { - if err := r.hooks.BeforeUpdateOrganizationMember(member); err != nil { - return err - } - } - _, err := tx.NewUpdate().Model(member).WherePK().Exec(ctx) if err != nil { return err @@ -119,12 +96,6 @@ func (r *BunOrganizationMemberRepository) Update(ctx context.Context, member *ty return err } - if r.hooks != nil { - if err := r.hooks.AfterUpdateOrganizationMember(*member); err != nil { - return err - } - } - return nil }) if err != nil { @@ -136,35 +107,14 @@ func (r *BunOrganizationMemberRepository) Update(ctx context.Context, member *ty func (r *BunOrganizationMemberRepository) Delete(ctx context.Context, memberID string) error { return r.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { - memberRepo := r.WithTx(tx) - member, err := memberRepo.GetByID(ctx, memberID) - if err != nil { - return err - } - if member == nil { - return nil - } - - if r.hooks != nil { - if err := r.hooks.BeforeDeleteOrganizationMember(member); err != nil { - return err - } - } - if _, err := tx.NewDelete().Model(&types.OrganizationMember{}).Where("id = ?", memberID).Exec(ctx); err != nil { return err } - if r.hooks != nil { - if err := r.hooks.AfterDeleteOrganizationMember(*member); err != nil { - return err - } - } - return nil }) } func (r *BunOrganizationMemberRepository) WithTx(tx bun.IDB) OrganizationMemberRepository { - return &BunOrganizationMemberRepository{db: tx, hooks: r.hooks} + return &BunOrganizationMemberRepository{db: tx} } diff --git a/plugins/organizations/repositories/bun_organization_member_repository_test.go b/plugins/organizations/repositories/bun_organization_member_repository_test.go index 0a142c74..f57c619d 100644 --- a/plugins/organizations/repositories/bun_organization_member_repository_test.go +++ b/plugins/organizations/repositories/bun_organization_member_repository_test.go @@ -540,122 +540,3 @@ func TestBunOrganizationMemberRepository_WithTx(t *testing.T) { }) } } - -func TestBunOrganizationMemberRepository_Hooks(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - run func(*testing.T) - }{ - { - name: "create hooks", - run: func(t *testing.T) { - t.Helper() - db := plugintests.SetupRepoDB(t) - plugintests.SeedOrganization(t, db, "org-1", "user-1", "Acme Inc", "acme-inc") - - beforeCalled := false - afterCalled := false - hooks := &plugintests.MockOrganizationMemberHooks{ - Before: func(member *types.OrganizationMember) error { - beforeCalled = true - require.Equal(t, "mem-1", member.ID) - return nil - }, - After: func(member types.OrganizationMember) error { - afterCalled = true - require.Equal(t, "mem-1", member.ID) - return nil - }, - } - - repo := repositories.NewBunOrganizationMemberRepository(db, hooks) - created, err := repo.Create(context.Background(), &types.OrganizationMember{ID: "mem-1", OrganizationID: "org-1", UserID: "user-2", Role: "member"}) - require.NoError(t, err) - require.NotNil(t, created) - require.True(t, beforeCalled) - require.True(t, afterCalled) - }, - }, - { - name: "update hooks", - run: func(t *testing.T) { - t.Helper() - db := plugintests.SetupRepoDB(t) - plugintests.SeedOrganization(t, db, "org-1", "user-1", "Acme Inc", "acme-inc") - - seedRepo := repositories.NewBunOrganizationMemberRepository(db) - ctx := context.Background() - member, err := seedRepo.Create(ctx, &types.OrganizationMember{ID: "mem-1", OrganizationID: "org-1", UserID: "user-2", Role: "member"}) - require.NoError(t, err) - - beforeCalled := false - afterCalled := false - hooks := &plugintests.MockOrganizationMemberHooks{ - BeforeUpdate: func(member *types.OrganizationMember) error { - beforeCalled = true - require.Equal(t, "admin", member.Role) - return nil - }, - AfterUpdate: func(member types.OrganizationMember) error { - afterCalled = true - require.Equal(t, "admin", member.Role) - return nil - }, - } - - repo := repositories.NewBunOrganizationMemberRepository(db, hooks) - member.Role = "admin" - updated, err := repo.Update(ctx, member) - require.NoError(t, err) - require.Equal(t, "admin", updated.Role) - require.True(t, beforeCalled) - require.True(t, afterCalled) - }, - }, - { - name: "delete hooks", - run: func(t *testing.T) { - t.Helper() - db := plugintests.SetupRepoDB(t) - plugintests.SeedOrganization(t, db, "org-1", "user-1", "Acme Inc", "acme-inc") - - seedRepo := repositories.NewBunOrganizationMemberRepository(db) - ctx := context.Background() - _, err := seedRepo.Create(ctx, &types.OrganizationMember{ID: "mem-1", OrganizationID: "org-1", UserID: "user-2", Role: "member"}) - require.NoError(t, err) - - beforeCalled := false - afterCalled := false - hooks := &plugintests.MockOrganizationMemberHooks{ - BeforeDelete: func(member *types.OrganizationMember) error { - beforeCalled = true - require.Equal(t, "mem-1", member.ID) - return nil - }, - AfterDelete: func(member types.OrganizationMember) error { - afterCalled = true - require.Equal(t, "mem-1", member.ID) - return nil - }, - } - - repo := repositories.NewBunOrganizationMemberRepository(db, hooks) - require.NoError(t, repo.Delete(ctx, "mem-1")) - found, err := repo.GetByID(ctx, "mem-1") - require.NoError(t, err) - require.Nil(t, found) - require.True(t, beforeCalled) - require.True(t, afterCalled) - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - tt.run(t) - }) - } -} diff --git a/plugins/organizations/repositories/bun_organization_repository.go b/plugins/organizations/repositories/bun_organization_repository.go index 6cd157f3..b906d2d5 100644 --- a/plugins/organizations/repositories/bun_organization_repository.go +++ b/plugins/organizations/repositories/bun_organization_repository.go @@ -10,26 +10,15 @@ import ( ) type BunOrganizationRepository struct { - db bun.IDB - hooks OrganizationHookExecutor + db bun.IDB } -func NewBunOrganizationRepository(db bun.IDB, hooks ...OrganizationHookExecutor) OrganizationRepository { - var hook OrganizationHookExecutor - if len(hooks) > 0 { - hook = hooks[0] - } - return &BunOrganizationRepository{db: db, hooks: hook} +func NewBunOrganizationRepository(db bun.IDB) OrganizationRepository { + return &BunOrganizationRepository{db: db} } func (r *BunOrganizationRepository) Create(ctx context.Context, organization *types.Organization) (*types.Organization, error) { err := r.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { - if r.hooks != nil { - if err := r.hooks.BeforeCreateOrganization(organization); err != nil { - return err - } - } - _, err := tx.NewInsert().Model(organization).Exec(ctx) if err != nil { return err @@ -39,12 +28,6 @@ func (r *BunOrganizationRepository) Create(ctx context.Context, organization *ty return err } - if r.hooks != nil { - if err := r.hooks.AfterCreateOrganization(*organization); err != nil { - return err - } - } - return nil }) if err != nil { @@ -83,12 +66,6 @@ func (r *BunOrganizationRepository) GetAllByOwnerID(ctx context.Context, ownerID func (r *BunOrganizationRepository) Update(ctx context.Context, organization *types.Organization) (*types.Organization, error) { err := r.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { - if r.hooks != nil { - if err := r.hooks.BeforeUpdateOrganization(organization); err != nil { - return err - } - } - _, err := tx.NewUpdate().Model(organization).WherePK().Exec(ctx) if err != nil { return err @@ -98,12 +75,6 @@ func (r *BunOrganizationRepository) Update(ctx context.Context, organization *ty return err } - if r.hooks != nil { - if err := r.hooks.AfterUpdateOrganization(*organization); err != nil { - return err - } - } - return nil }) if err != nil { @@ -115,35 +86,14 @@ func (r *BunOrganizationRepository) Update(ctx context.Context, organization *ty func (r *BunOrganizationRepository) Delete(ctx context.Context, organizationID string) error { return r.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { - orgRepo := r.WithTx(tx) - organization, err := orgRepo.GetByID(ctx, organizationID) - if err != nil { - return err - } - if organization == nil { - return nil - } - - if r.hooks != nil { - if err := r.hooks.BeforeDeleteOrganization(organization); err != nil { - return err - } - } - if _, err := tx.NewDelete().Model(&types.Organization{}).Where("id = ?", organizationID).Exec(ctx); err != nil { return err } - if r.hooks != nil { - if err := r.hooks.AfterDeleteOrganization(*organization); err != nil { - return err - } - } - return nil }) } func (r *BunOrganizationRepository) WithTx(tx bun.IDB) OrganizationRepository { - return &BunOrganizationRepository{db: tx, hooks: r.hooks} + return &BunOrganizationRepository{db: tx} } diff --git a/plugins/organizations/repositories/bun_organization_repository_test.go b/plugins/organizations/repositories/bun_organization_repository_test.go index 44c7c1c9..bf73d39a 100644 --- a/plugins/organizations/repositories/bun_organization_repository_test.go +++ b/plugins/organizations/repositories/bun_organization_repository_test.go @@ -347,57 +347,3 @@ func TestBunOrganizationRepository_WithTx(t *testing.T) { }) } } - -func TestBunOrganizationRepository_CreateHooks(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - setup func(*testing.T) (*repositories.BunOrganizationRepository, context.Context, *types.Organization) - expectErr bool - expectBool bool - }{ - { - name: "hooks are called on create", - setup: func(t *testing.T) (*repositories.BunOrganizationRepository, context.Context, *types.Organization) { - t.Helper() - db := plugintests.SetupRepoDB(t) - var beforeCalled bool - var afterCalled bool - hooks := &plugintests.MockOrganizationHooks{ - BeforeCreate: func(organization *types.Organization) error { - beforeCalled = true - require.Equal(t, "org-1", organization.ID) - return nil - }, - AfterCreate: func(organization types.Organization) error { - afterCalled = true - require.Equal(t, "org-1", organization.ID) - return nil - }, - } - repo := repositories.NewBunOrganizationRepository(db, hooks).(*repositories.BunOrganizationRepository) - org := &types.Organization{ID: "org-1", OwnerID: "user-1", Name: "Acme Inc", Slug: "acme-inc"} - t.Cleanup(func() { - require.True(t, beforeCalled) - require.True(t, afterCalled) - }) - return repo, context.Background(), org - }, - expectBool: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - repo, ctx, organization := tt.setup(t) - created, err := repo.Create(ctx, organization) - require.NoError(t, err) - require.NotNil(t, created) - require.Equal(t, organization.ID, created.ID) - require.Equal(t, tt.expectBool, created.ID == organization.ID) - }) - } -} diff --git a/plugins/organizations/repositories/bun_organization_team_member_repository.go b/plugins/organizations/repositories/bun_organization_team_member_repository.go index 35855f9f..9b4bbd91 100644 --- a/plugins/organizations/repositories/bun_organization_team_member_repository.go +++ b/plugins/organizations/repositories/bun_organization_team_member_repository.go @@ -10,26 +10,15 @@ import ( ) type BunOrganizationTeamMemberRepository struct { - db bun.IDB - hooks OrganizationTeamMemberHookExecutor + db bun.IDB } -func NewBunOrganizationTeamMemberRepository(db bun.IDB, hooks ...OrganizationTeamMemberHookExecutor) OrganizationTeamMemberRepository { - var hook OrganizationTeamMemberHookExecutor - if len(hooks) > 0 { - hook = hooks[0] - } - return &BunOrganizationTeamMemberRepository{db: db, hooks: hook} +func NewBunOrganizationTeamMemberRepository(db bun.IDB) OrganizationTeamMemberRepository { + return &BunOrganizationTeamMemberRepository{db: db} } func (r *BunOrganizationTeamMemberRepository) Create(ctx context.Context, teamMember *types.OrganizationTeamMember) (*types.OrganizationTeamMember, error) { err := r.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { - if r.hooks != nil { - if err := r.hooks.BeforeCreateOrganizationTeamMember(teamMember); err != nil { - return err - } - } - _, err := tx.NewInsert().Model(teamMember).Exec(ctx) if err != nil { return err @@ -39,12 +28,6 @@ func (r *BunOrganizationTeamMemberRepository) Create(ctx context.Context, teamMe return err } - if r.hooks != nil { - if err := r.hooks.AfterCreateOrganizationTeamMember(*teamMember); err != nil { - return err - } - } - return nil }) if err != nil { @@ -89,36 +72,15 @@ func (r *BunOrganizationTeamMemberRepository) GetAllByTeamID(ctx context.Context func (r *BunOrganizationTeamMemberRepository) DeleteByTeamIDAndMemberID(ctx context.Context, teamID, memberID string) error { return r.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { - teamMemberRepo := r.WithTx(tx) - teamMember, err := teamMemberRepo.GetByTeamIDAndMemberID(ctx, teamID, memberID) - if err != nil { - return err - } - if teamMember == nil { - return nil - } - - if r.hooks != nil { - if err := r.hooks.BeforeDeleteOrganizationTeamMember(teamMember); err != nil { - return err - } - } - - _, err = tx.NewDelete().Model(&types.OrganizationTeamMember{}).Where("team_id = ? AND member_id = ?", teamID, memberID).Exec(ctx) + _, err := tx.NewDelete().Model(&types.OrganizationTeamMember{}).Where("team_id = ? AND member_id = ?", teamID, memberID).Exec(ctx) if err != nil { return err } - if r.hooks != nil { - if err := r.hooks.AfterDeleteOrganizationTeamMember(*teamMember); err != nil { - return err - } - } - return nil }) } func (r *BunOrganizationTeamMemberRepository) WithTx(tx bun.IDB) OrganizationTeamMemberRepository { - return &BunOrganizationTeamMemberRepository{db: tx, hooks: r.hooks} + return &BunOrganizationTeamMemberRepository{db: tx} } diff --git a/plugins/organizations/repositories/bun_organization_team_member_repository_test.go b/plugins/organizations/repositories/bun_organization_team_member_repository_test.go index 4f99734d..a2befb95 100644 --- a/plugins/organizations/repositories/bun_organization_team_member_repository_test.go +++ b/plugins/organizations/repositories/bun_organization_team_member_repository_test.go @@ -474,90 +474,3 @@ func TestBunOrganizationTeamMemberRepository_WithTx(t *testing.T) { }) } } - -func TestBunOrganizationTeamMemberRepository_Hooks(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - run func(*testing.T) - }{ - { - name: "create hooks", - run: func(t *testing.T) { - t.Helper() - db := plugintests.SetupRepoDB(t) - plugintests.SeedOrganization(t, db, "org-1", "user-1", "Acme Inc", "acme-inc") - plugintests.SeedOrganizationMember(t, db, "member-1", "org-1", "user-1", "member") - plugintests.SeedOrganizationTeam(t, db, "team-1", "org-1", "Platform", "platform") - - beforeCalled := false - afterCalled := false - hooks := &plugintests.MockOrganizationTeamMemberHooks{ - BeforeCreate: func(teamMember *types.OrganizationTeamMember) error { - beforeCalled = true - require.Equal(t, "team-member-1", teamMember.ID) - return nil - }, - AfterCreate: func(teamMember types.OrganizationTeamMember) error { - afterCalled = true - require.Equal(t, "team-member-1", teamMember.ID) - return nil - }, - } - - repo := repositories.NewBunOrganizationTeamMemberRepository(db, hooks) - created, err := repo.Create(context.Background(), &types.OrganizationTeamMember{ID: "team-member-1", TeamID: "team-1", MemberID: "member-1"}) - require.NoError(t, err) - require.NotNil(t, created) - require.True(t, beforeCalled) - require.True(t, afterCalled) - }, - }, - { - name: "delete hooks", - run: func(t *testing.T) { - t.Helper() - db := plugintests.SetupRepoDB(t) - plugintests.SeedOrganization(t, db, "org-1", "user-1", "Acme Inc", "acme-inc") - plugintests.SeedOrganizationMember(t, db, "member-1", "org-1", "user-1", "member") - plugintests.SeedOrganizationTeam(t, db, "team-1", "org-1", "Platform", "platform") - - seedRepo := repositories.NewBunOrganizationTeamMemberRepository(db) - ctx := context.Background() - _, err := seedRepo.Create(ctx, &types.OrganizationTeamMember{ID: "team-member-1", TeamID: "team-1", MemberID: "member-1"}) - require.NoError(t, err) - - beforeCalled := false - afterCalled := false - hooks := &plugintests.MockOrganizationTeamMemberHooks{ - BeforeDelete: func(teamMember *types.OrganizationTeamMember) error { - beforeCalled = true - require.Equal(t, "team-member-1", teamMember.ID) - return nil - }, - AfterDelete: func(teamMember types.OrganizationTeamMember) error { - afterCalled = true - require.Equal(t, "team-member-1", teamMember.ID) - return nil - }, - } - - repo := repositories.NewBunOrganizationTeamMemberRepository(db, hooks) - require.NoError(t, repo.DeleteByTeamIDAndMemberID(ctx, "team-1", "member-1")) - found, err := repo.GetByTeamIDAndMemberID(ctx, "team-1", "member-1") - require.NoError(t, err) - require.Nil(t, found) - require.True(t, beforeCalled) - require.True(t, afterCalled) - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - tt.run(t) - }) - } -} diff --git a/plugins/organizations/repositories/bun_organization_team_repository.go b/plugins/organizations/repositories/bun_organization_team_repository.go index 00644d1b..65297fe0 100644 --- a/plugins/organizations/repositories/bun_organization_team_repository.go +++ b/plugins/organizations/repositories/bun_organization_team_repository.go @@ -10,26 +10,15 @@ import ( ) type BunOrganizationTeamRepository struct { - db bun.IDB - hooks OrganizationTeamHookExecutor + db bun.IDB } -func NewBunOrganizationTeamRepository(db bun.IDB, hooks ...OrganizationTeamHookExecutor) OrganizationTeamRepository { - var hook OrganizationTeamHookExecutor - if len(hooks) > 0 { - hook = hooks[0] - } - return &BunOrganizationTeamRepository{db: db, hooks: hook} +func NewBunOrganizationTeamRepository(db bun.IDB) OrganizationTeamRepository { + return &BunOrganizationTeamRepository{db: db} } func (r *BunOrganizationTeamRepository) Create(ctx context.Context, team *types.OrganizationTeam) (*types.OrganizationTeam, error) { err := r.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { - if r.hooks != nil { - if err := r.hooks.BeforeCreateOrganizationTeam(team); err != nil { - return err - } - } - _, err := tx.NewInsert().Model(team).Exec(ctx) if err != nil { return err @@ -39,12 +28,6 @@ func (r *BunOrganizationTeamRepository) Create(ctx context.Context, team *types. return err } - if r.hooks != nil { - if err := r.hooks.AfterCreateOrganizationTeam(*team); err != nil { - return err - } - } - return nil }) if err != nil { @@ -85,12 +68,6 @@ func (r *BunOrganizationTeamRepository) GetAllByOrganizationID(ctx context.Conte func (r *BunOrganizationTeamRepository) Update(ctx context.Context, team *types.OrganizationTeam) (*types.OrganizationTeam, error) { err := r.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { - if r.hooks != nil { - if err := r.hooks.BeforeUpdateOrganizationTeam(team); err != nil { - return err - } - } - _, err := tx.NewUpdate().Model(team).WherePK().Exec(ctx) if err != nil { return err @@ -100,12 +77,6 @@ func (r *BunOrganizationTeamRepository) Update(ctx context.Context, team *types. return err } - if r.hooks != nil { - if err := r.hooks.AfterUpdateOrganizationTeam(*team); err != nil { - return err - } - } - return nil }) if err != nil { @@ -117,35 +88,14 @@ func (r *BunOrganizationTeamRepository) Update(ctx context.Context, team *types. func (r *BunOrganizationTeamRepository) Delete(ctx context.Context, teamID string) error { return r.db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { - teamRepo := r.WithTx(tx) - team, err := teamRepo.GetByID(ctx, teamID) - if err != nil { - return err - } - if team == nil { - return nil - } - - if r.hooks != nil { - if err := r.hooks.BeforeDeleteOrganizationTeam(team); err != nil { - return err - } - } - if _, err := tx.NewDelete().Model(&types.OrganizationTeam{}).Where("id = ?", teamID).Exec(ctx); err != nil { return err } - if r.hooks != nil { - if err := r.hooks.AfterDeleteOrganizationTeam(*team); err != nil { - return err - } - } - return nil }) } func (r *BunOrganizationTeamRepository) WithTx(tx bun.IDB) OrganizationTeamRepository { - return &BunOrganizationTeamRepository{db: tx, hooks: r.hooks} + return &BunOrganizationTeamRepository{db: tx} } diff --git a/plugins/organizations/repositories/bun_organization_team_repository_test.go b/plugins/organizations/repositories/bun_organization_team_repository_test.go index 574c7366..2612e100 100644 --- a/plugins/organizations/repositories/bun_organization_team_repository_test.go +++ b/plugins/organizations/repositories/bun_organization_team_repository_test.go @@ -355,126 +355,3 @@ func TestBunOrganizationTeamRepository_WithTx(t *testing.T) { }) } } - -func TestBunOrganizationTeamRepository_Hooks(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - run func(*testing.T) - }{ - { - name: "create hooks", - run: func(t *testing.T) { - t.Helper() - db := plugintests.SetupRepoDB(t) - plugintests.SeedOrganization(t, db, "org-1", "user-1", "Acme Inc", "acme-inc") - - beforeCalled := false - afterCalled := false - hooks := &plugintests.MockOrganizationTeamHooks{ - BeforeCreate: func(team *types.OrganizationTeam) error { - beforeCalled = true - require.Equal(t, "team-1", team.ID) - require.Equal(t, "Platform", team.Name) - return nil - }, - AfterCreate: func(team types.OrganizationTeam) error { - afterCalled = true - require.Equal(t, "team-1", team.ID) - return nil - }, - } - - repo := repositories.NewBunOrganizationTeamRepository(db, hooks) - created, err := repo.Create(context.Background(), &types.OrganizationTeam{ID: "team-1", OrganizationID: "org-1", Name: "Platform", Slug: "platform"}) - require.NoError(t, err) - require.NotNil(t, created) - require.True(t, beforeCalled) - require.True(t, afterCalled) - }, - }, - { - name: "update hooks", - run: func(t *testing.T) { - t.Helper() - db := plugintests.SetupRepoDB(t) - plugintests.SeedOrganization(t, db, "org-1", "user-1", "Acme Inc", "acme-inc") - - seedRepo := repositories.NewBunOrganizationTeamRepository(db) - ctx := context.Background() - team, err := seedRepo.Create(ctx, &types.OrganizationTeam{ID: "team-1", OrganizationID: "org-1", Name: "Platform", Slug: "platform"}) - require.NoError(t, err) - - beforeCalled := false - afterCalled := false - hooks := &plugintests.MockOrganizationTeamHooks{ - BeforeUpdate: func(team *types.OrganizationTeam) error { - beforeCalled = true - require.Equal(t, "Platform Revamp", team.Name) - return nil - }, - AfterUpdate: func(team types.OrganizationTeam) error { - afterCalled = true - require.Equal(t, "Platform Revamp", team.Name) - return nil - }, - } - - repo := repositories.NewBunOrganizationTeamRepository(db, hooks) - team.Name = "Platform Revamp" - description := new(string) - *description = "Core platform" - team.Description = description - updated, err := repo.Update(ctx, team) - require.NoError(t, err) - require.Equal(t, "Platform Revamp", updated.Name) - require.True(t, beforeCalled) - require.True(t, afterCalled) - }, - }, - { - name: "delete hooks", - run: func(t *testing.T) { - t.Helper() - db := plugintests.SetupRepoDB(t) - plugintests.SeedOrganization(t, db, "org-1", "user-1", "Acme Inc", "acme-inc") - - seedRepo := repositories.NewBunOrganizationTeamRepository(db) - ctx := context.Background() - _, err := seedRepo.Create(ctx, &types.OrganizationTeam{ID: "team-1", OrganizationID: "org-1", Name: "Platform", Slug: "platform"}) - require.NoError(t, err) - - beforeCalled := false - afterCalled := false - hooks := &plugintests.MockOrganizationTeamHooks{ - BeforeDelete: func(team *types.OrganizationTeam) error { - beforeCalled = true - require.Equal(t, "team-1", team.ID) - return nil - }, - AfterDelete: func(team types.OrganizationTeam) error { - afterCalled = true - require.Equal(t, "team-1", team.ID) - return nil - }, - } - - repo := repositories.NewBunOrganizationTeamRepository(db, hooks) - require.NoError(t, repo.Delete(ctx, "team-1")) - found, err := repo.GetByID(ctx, "team-1") - require.NoError(t, err) - require.Nil(t, found) - require.True(t, beforeCalled) - require.True(t, afterCalled) - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - tt.run(t) - }) - } -} diff --git a/plugins/organizations/repositories/interfaces.go b/plugins/organizations/repositories/interfaces.go index e35cb192..2c3bb5c2 100644 --- a/plugins/organizations/repositories/interfaces.go +++ b/plugins/organizations/repositories/interfaces.go @@ -8,47 +8,6 @@ import ( "github.com/Authula/authula/plugins/organizations/types" ) -type OrganizationHookExecutor interface { - BeforeCreateOrganization(organization *types.Organization) error - AfterCreateOrganization(organization types.Organization) error - BeforeUpdateOrganization(organization *types.Organization) error - AfterUpdateOrganization(organization types.Organization) error - BeforeDeleteOrganization(organization *types.Organization) error - AfterDeleteOrganization(organization types.Organization) error -} - -type OrganizationInvitationHookExecutor interface { - BeforeCreateOrganizationInvitation(invitation *types.OrganizationInvitation) error - AfterCreateOrganizationInvitation(invitation types.OrganizationInvitation) error - BeforeUpdateOrganizationInvitation(invitation *types.OrganizationInvitation) error - AfterUpdateOrganizationInvitation(invitation types.OrganizationInvitation) error -} - -type OrganizationMemberHookExecutor interface { - BeforeCreateOrganizationMember(member *types.OrganizationMember) error - AfterCreateOrganizationMember(member types.OrganizationMember) error - BeforeUpdateOrganizationMember(member *types.OrganizationMember) error - AfterUpdateOrganizationMember(member types.OrganizationMember) error - BeforeDeleteOrganizationMember(member *types.OrganizationMember) error - AfterDeleteOrganizationMember(member types.OrganizationMember) error -} - -type OrganizationTeamHookExecutor interface { - BeforeCreateOrganizationTeam(team *types.OrganizationTeam) error - AfterCreateOrganizationTeam(team types.OrganizationTeam) error - BeforeUpdateOrganizationTeam(team *types.OrganizationTeam) error - AfterUpdateOrganizationTeam(team types.OrganizationTeam) error - BeforeDeleteOrganizationTeam(team *types.OrganizationTeam) error - AfterDeleteOrganizationTeam(team types.OrganizationTeam) error -} - -type OrganizationTeamMemberHookExecutor interface { - BeforeCreateOrganizationTeamMember(teamMember *types.OrganizationTeamMember) error - AfterCreateOrganizationTeamMember(teamMember types.OrganizationTeamMember) error - BeforeDeleteOrganizationTeamMember(teamMember *types.OrganizationTeamMember) error - AfterDeleteOrganizationTeamMember(teamMember types.OrganizationTeamMember) error -} - type OrganizationRepository interface { Create(ctx context.Context, organization *types.Organization) (*types.Organization, error) GetByID(ctx context.Context, organizationID string) (*types.Organization, error) diff --git a/plugins/organizations/services/hooks_executor.go b/plugins/organizations/services/hooks_executor.go new file mode 100644 index 00000000..fdba76dd --- /dev/null +++ b/plugins/organizations/services/hooks_executor.go @@ -0,0 +1,198 @@ +package services + +import ( + "context" + + "github.com/Authula/authula/models" + "github.com/Authula/authula/plugins/organizations/types" +) + +type ServiceHookExecutor struct { + config *types.OrganizationsServiceHooksConfig +} + +func NewServiceHookExecutor(config *types.OrganizationsServiceHooksConfig) *ServiceHookExecutor { + return &ServiceHookExecutor{config: config} +} + +func (e *ServiceHookExecutor) BeforeCreateOrganization(ctx context.Context, actor *models.Actor, organization *types.Organization) error { + if e == nil || e.config == nil || e.config.Organizations == nil || e.config.Organizations.BeforeCreate == nil { + return nil + } + return e.config.Organizations.BeforeCreate(ctx, actor, organization) +} + +func (e *ServiceHookExecutor) AfterCreateOrganization(ctx context.Context, actor *models.Actor, organization *types.Organization) error { + if e == nil || e.config == nil || e.config.Organizations == nil || e.config.Organizations.AfterCreate == nil { + return nil + } + return e.config.Organizations.AfterCreate(ctx, actor, organization) +} + +func (e *ServiceHookExecutor) BeforeUpdateOrganization(ctx context.Context, actor *models.Actor, organization *types.Organization) error { + if e == nil || e.config == nil || e.config.Organizations == nil || e.config.Organizations.BeforeUpdate == nil { + return nil + } + return e.config.Organizations.BeforeUpdate(ctx, actor, organization) +} + +func (e *ServiceHookExecutor) AfterUpdateOrganization(ctx context.Context, actor *models.Actor, organization *types.Organization) error { + if e == nil || e.config == nil || e.config.Organizations == nil || e.config.Organizations.AfterUpdate == nil { + return nil + } + return e.config.Organizations.AfterUpdate(ctx, actor, organization) +} + +func (e *ServiceHookExecutor) BeforeDeleteOrganization(ctx context.Context, actor *models.Actor, organization *types.Organization) error { + if e == nil || e.config == nil || e.config.Organizations == nil || e.config.Organizations.BeforeDelete == nil { + return nil + } + return e.config.Organizations.BeforeDelete(ctx, actor, organization) +} + +func (e *ServiceHookExecutor) AfterDeleteOrganization(ctx context.Context, actor *models.Actor, organization *types.Organization) error { + if e == nil || e.config == nil || e.config.Organizations == nil || e.config.Organizations.AfterDelete == nil { + return nil + } + return e.config.Organizations.AfterDelete(ctx, actor, organization) +} + +func (e *ServiceHookExecutor) BeforeCreateOrganizationMember(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error { + if e == nil || e.config == nil || e.config.Members == nil || e.config.Members.BeforeCreate == nil { + return nil + } + return e.config.Members.BeforeCreate(ctx, actor, member) +} + +func (e *ServiceHookExecutor) AfterCreateOrganizationMember(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error { + if e == nil || e.config == nil || e.config.Members == nil || e.config.Members.AfterCreate == nil { + return nil + } + return e.config.Members.AfterCreate(ctx, actor, member) +} + +func (e *ServiceHookExecutor) BeforeUpdateOrganizationMember(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error { + if e == nil || e.config == nil || e.config.Members == nil || e.config.Members.BeforeUpdate == nil { + return nil + } + return e.config.Members.BeforeUpdate(ctx, actor, member) +} + +func (e *ServiceHookExecutor) AfterUpdateOrganizationMember(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error { + if e == nil || e.config == nil || e.config.Members == nil || e.config.Members.AfterUpdate == nil { + return nil + } + return e.config.Members.AfterUpdate(ctx, actor, member) +} + +func (e *ServiceHookExecutor) BeforeDeleteOrganizationMember(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error { + if e == nil || e.config == nil || e.config.Members == nil || e.config.Members.BeforeDelete == nil { + return nil + } + return e.config.Members.BeforeDelete(ctx, actor, member) +} + +func (e *ServiceHookExecutor) AfterDeleteOrganizationMember(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error { + if e == nil || e.config == nil || e.config.Members == nil || e.config.Members.AfterDelete == nil { + return nil + } + return e.config.Members.AfterDelete(ctx, actor, member) +} + +func (e *ServiceHookExecutor) BeforeCreateOrganizationInvitation(ctx context.Context, actor *models.Actor, invitation *types.OrganizationInvitation) error { + if e == nil || e.config == nil || e.config.Invitations == nil || e.config.Invitations.BeforeCreate == nil { + return nil + } + return e.config.Invitations.BeforeCreate(ctx, actor, invitation) +} + +func (e *ServiceHookExecutor) AfterCreateOrganizationInvitation(ctx context.Context, actor *models.Actor, invitation *types.OrganizationInvitation) error { + if e == nil || e.config == nil || e.config.Invitations == nil || e.config.Invitations.AfterCreate == nil { + return nil + } + return e.config.Invitations.AfterCreate(ctx, actor, invitation) +} + +func (e *ServiceHookExecutor) BeforeUpdateOrganizationInvitation(ctx context.Context, actor *models.Actor, invitation *types.OrganizationInvitation) error { + if e == nil || e.config == nil || e.config.Invitations == nil || e.config.Invitations.BeforeUpdate == nil { + return nil + } + return e.config.Invitations.BeforeUpdate(ctx, actor, invitation) +} + +func (e *ServiceHookExecutor) AfterUpdateOrganizationInvitation(ctx context.Context, actor *models.Actor, invitation *types.OrganizationInvitation) error { + if e == nil || e.config == nil || e.config.Invitations == nil || e.config.Invitations.AfterUpdate == nil { + return nil + } + return e.config.Invitations.AfterUpdate(ctx, actor, invitation) +} + +func (e *ServiceHookExecutor) BeforeCreateOrganizationTeam(ctx context.Context, actor *models.Actor, team *types.OrganizationTeam) error { + if e == nil || e.config == nil || e.config.Teams == nil || e.config.Teams.BeforeCreate == nil { + return nil + } + return e.config.Teams.BeforeCreate(ctx, actor, team) +} + +func (e *ServiceHookExecutor) AfterCreateOrganizationTeam(ctx context.Context, actor *models.Actor, team *types.OrganizationTeam) error { + if e == nil || e.config == nil || e.config.Teams == nil || e.config.Teams.AfterCreate == nil { + return nil + } + return e.config.Teams.AfterCreate(ctx, actor, team) +} + +func (e *ServiceHookExecutor) BeforeUpdateOrganizationTeam(ctx context.Context, actor *models.Actor, team *types.OrganizationTeam) error { + if e == nil || e.config == nil || e.config.Teams == nil || e.config.Teams.BeforeUpdate == nil { + return nil + } + return e.config.Teams.BeforeUpdate(ctx, actor, team) +} + +func (e *ServiceHookExecutor) AfterUpdateOrganizationTeam(ctx context.Context, actor *models.Actor, team *types.OrganizationTeam) error { + if e == nil || e.config == nil || e.config.Teams == nil || e.config.Teams.AfterUpdate == nil { + return nil + } + return e.config.Teams.AfterUpdate(ctx, actor, team) +} + +func (e *ServiceHookExecutor) BeforeDeleteOrganizationTeam(ctx context.Context, actor *models.Actor, team *types.OrganizationTeam) error { + if e == nil || e.config == nil || e.config.Teams == nil || e.config.Teams.BeforeDelete == nil { + return nil + } + return e.config.Teams.BeforeDelete(ctx, actor, team) +} + +func (e *ServiceHookExecutor) AfterDeleteOrganizationTeam(ctx context.Context, actor *models.Actor, team *types.OrganizationTeam) error { + if e == nil || e.config == nil || e.config.Teams == nil || e.config.Teams.AfterDelete == nil { + return nil + } + return e.config.Teams.AfterDelete(ctx, actor, team) +} + +func (e *ServiceHookExecutor) BeforeCreateOrganizationTeamMember(ctx context.Context, actor *models.Actor, member *types.OrganizationTeamMember) error { + if e == nil || e.config == nil || e.config.TeamMembers == nil || e.config.TeamMembers.BeforeCreate == nil { + return nil + } + return e.config.TeamMembers.BeforeCreate(ctx, actor, member) +} + +func (e *ServiceHookExecutor) AfterCreateOrganizationTeamMember(ctx context.Context, actor *models.Actor, member *types.OrganizationTeamMember) error { + if e == nil || e.config == nil || e.config.TeamMembers == nil || e.config.TeamMembers.AfterCreate == nil { + return nil + } + return e.config.TeamMembers.AfterCreate(ctx, actor, member) +} + +func (e *ServiceHookExecutor) BeforeDeleteOrganizationTeamMember(ctx context.Context, actor *models.Actor, member *types.OrganizationTeamMember) error { + if e == nil || e.config == nil || e.config.TeamMembers == nil || e.config.TeamMembers.BeforeDelete == nil { + return nil + } + return e.config.TeamMembers.BeforeDelete(ctx, actor, member) +} + +func (e *ServiceHookExecutor) AfterDeleteOrganizationTeamMember(ctx context.Context, actor *models.Actor, member *types.OrganizationTeamMember) error { + if e == nil || e.config == nil || e.config.TeamMembers == nil || e.config.TeamMembers.AfterDelete == nil { + return nil + } + return e.config.TeamMembers.AfterDelete(ctx, actor, member) +} diff --git a/plugins/organizations/services/hooks_executor_test.go b/plugins/organizations/services/hooks_executor_test.go new file mode 100644 index 00000000..a9cd3678 --- /dev/null +++ b/plugins/organizations/services/hooks_executor_test.go @@ -0,0 +1,186 @@ +package services + +import ( + "context" + "errors" + "testing" + + "github.com/Authula/authula/models" + "github.com/Authula/authula/plugins/organizations/types" +) + +func TestServiceHookExecutor_NilConfigIsNoop(t *testing.T) { + t.Parallel() + + executor := NewServiceHookExecutor(nil) + ctx := context.Background() + actor := &models.Actor{ID: "user-1"} + + if err := executor.BeforeCreateOrganization(ctx, actor, &types.Organization{ID: "org-1"}); err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if err := executor.AfterCreateOrganization(ctx, actor, &types.Organization{ID: "org-1"}); err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if err := executor.BeforeDeleteOrganization(ctx, actor, &types.Organization{ID: "org-1"}); err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if err := executor.AfterDeleteOrganization(ctx, actor, &types.Organization{ID: "org-1"}); err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if err := executor.BeforeCreateOrganizationInvitation(ctx, actor, &types.OrganizationInvitation{ID: "inv-1"}); err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if err := executor.AfterCreateOrganizationTeam(ctx, actor, &types.OrganizationTeam{ID: "team-1"}); err != nil { + t.Fatalf("expected nil error, got %v", err) + } +} + +func TestServiceHookExecutor_NilExecutorIsNoop(t *testing.T) { + t.Parallel() + + var executor *ServiceHookExecutor + ctx := context.Background() + actor := &models.Actor{ID: "user-1"} + + if err := executor.BeforeCreateOrganization(ctx, actor, &types.Organization{ID: "org-1"}); err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if err := executor.AfterCreateOrganization(ctx, actor, &types.Organization{ID: "org-1"}); err != nil { + t.Fatalf("expected nil error, got %v", err) + } +} + +func TestServiceHookExecutor_OrganizationCreateHooks(t *testing.T) { + t.Parallel() + + var beforeCalled bool + var afterCalled bool + + executor := NewServiceHookExecutor(&types.OrganizationsServiceHooksConfig{ + Organizations: &types.OrganizationServiceHooksConfig{ + BeforeCreate: func(ctx context.Context, actor *models.Actor, organization *types.Organization) error { + beforeCalled = true + if organization == nil { + return errors.New("organization is nil") + } + if organization.ID != "org-1" { + t.Fatalf("unexpected organization ID: %s", organization.ID) + } + if actor == nil || actor.ID != "user-1" { + t.Fatalf("unexpected actor: %+v", actor) + } + return nil + }, + AfterCreate: func(ctx context.Context, actor *models.Actor, organization *types.Organization) error { + afterCalled = true + if organization.ID != "org-1" { + t.Fatalf("unexpected organization ID: %s", organization.ID) + } + if actor == nil || actor.ID != "user-1" { + t.Fatalf("unexpected actor: %+v", actor) + } + return nil + }, + }, + }) + + ctx := context.Background() + actor := &models.Actor{ID: "user-1"} + organization := &types.Organization{ID: "org-1", Name: "Acme"} + + if err := executor.BeforeCreateOrganization(ctx, actor, organization); err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if err := executor.AfterCreateOrganization(ctx, actor, organization); err != nil { + t.Fatalf("expected nil error, got %v", err) + } + + if !beforeCalled { + t.Fatal("expected BeforeCreate hook to be called") + } + if !afterCalled { + t.Fatal("expected AfterCreate hook to be called") + } +} + +func TestServiceHookExecutor_OrganizationCreateHookError(t *testing.T) { + t.Parallel() + + someErr := errors.New("some error") + executor := NewServiceHookExecutor(&types.OrganizationsServiceHooksConfig{ + Organizations: &types.OrganizationServiceHooksConfig{ + BeforeCreate: func(ctx context.Context, actor *models.Actor, organization *types.Organization) error { + return someErr + }, + }, + }) + + err := executor.BeforeCreateOrganization(context.Background(), &models.Actor{ID: "user-1"}, &types.Organization{ID: "org-1"}) + if !errors.Is(err, someErr) { + t.Fatalf("expected someErr error, got %v", err) + } +} + +func TestServiceHookExecutor_MemberUpdateDeleteHooks(t *testing.T) { + t.Parallel() + + var beforeUpdateCalled bool + var afterUpdateCalled bool + var beforeDeleteCalled bool + var afterDeleteCalled bool + + executor := NewServiceHookExecutor(&types.OrganizationsServiceHooksConfig{ + Members: &types.OrganizationMemberServiceHooksConfig{ + BeforeUpdate: func(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error { + beforeUpdateCalled = true + if member == nil || member.ID != "mem-1" { + t.Fatalf("unexpected member in before update hook: %+v", member) + } + return nil + }, + AfterUpdate: func(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error { + afterUpdateCalled = true + if member.ID != "mem-1" { + t.Fatalf("unexpected member in after update hook: %+v", member) + } + return nil + }, + BeforeDelete: func(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error { + beforeDeleteCalled = true + if member == nil || member.ID != "mem-1" { + t.Fatalf("unexpected member in before delete hook: %+v", member) + } + return nil + }, + AfterDelete: func(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error { + afterDeleteCalled = true + if member.ID != "mem-1" { + t.Fatalf("unexpected member in after delete hook: %+v", member) + } + return nil + }, + }, + }) + + ctx := context.Background() + actor := &models.Actor{ID: "user-1"} + member := &types.OrganizationMember{ID: "mem-1", Role: "member"} + + if err := executor.BeforeUpdateOrganizationMember(ctx, actor, member); err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if err := executor.AfterUpdateOrganizationMember(ctx, actor, member); err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if err := executor.BeforeDeleteOrganizationMember(ctx, actor, member); err != nil { + t.Fatalf("expected nil error, got %v", err) + } + if err := executor.AfterDeleteOrganizationMember(ctx, actor, member); err != nil { + t.Fatalf("expected nil error, got %v", err) + } + + if !beforeUpdateCalled || !afterUpdateCalled || !beforeDeleteCalled || !afterDeleteCalled { + t.Fatal("expected member update and delete hooks to be called") + } +} diff --git a/plugins/organizations/services/organization_invitation_service.go b/plugins/organizations/services/organization_invitation_service.go index 6a7d6675..bd1d20c4 100644 --- a/plugins/organizations/services/organization_invitation_service.go +++ b/plugins/organizations/services/organization_invitation_service.go @@ -40,6 +40,7 @@ type organizationInvitationService struct { orgMemberRepo repositories.OrganizationMemberRepository serviceUtils *ServiceUtils emailTemplateManager *emailtmpl.Manager + hooks *ServiceHookExecutor } func NewOrganizationInvitationService( @@ -56,7 +57,12 @@ func NewOrganizationInvitationService( orgMemberRepo repositories.OrganizationMemberRepository, serviceUtils *ServiceUtils, emailTemplateManager *emailtmpl.Manager, + hooks ...*ServiceHookExecutor, ) *organizationInvitationService { + var hook *ServiceHookExecutor + if len(hooks) > 0 { + hook = hooks[0] + } return &organizationInvitationService{ txRunner: txRunner, globalConfig: globalConfig, @@ -71,6 +77,7 @@ func NewOrganizationInvitationService( orgMemberRepo: orgMemberRepo, serviceUtils: serviceUtils, emailTemplateManager: emailTemplateManager, + hooks: hook, } } @@ -106,6 +113,26 @@ func (s *organizationInvitationService) CreateOrganizationInvitation(ctx context return nil, coreerrors.ErrUnprocessableEntity } + expiresAt := time.Now().UTC().Add(s.pluginConfig.InvitationExpiresIn) + if !expiresAt.After(time.Now().UTC()) { + return nil, coreerrors.ErrUnprocessableEntity + } + invitation := &types.OrganizationInvitation{ + ID: util.GenerateUUID(), + Email: request.Email, + InviterID: actorID, + OrganizationID: organizationID, + Role: role, + Status: types.OrganizationInvitationStatusPending, + ExpiresAt: expiresAt, + } + + if s.hooks != nil { + if err := s.hooks.BeforeCreateOrganizationInvitation(ctx, actor, invitation); err != nil { + return nil, err + } + } + var created *types.OrganizationInvitation err = s.txRunner.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { invitationRepo := s.orgInvitationRepo.WithTx(tx) @@ -130,20 +157,6 @@ func (s *organizationInvitationService) CreateOrganizationInvitation(ctx context } } - expiresAt := time.Now().UTC().Add(s.pluginConfig.InvitationExpiresIn) - if !expiresAt.After(time.Now().UTC()) { - return coreerrors.ErrUnprocessableEntity - } - invitation := &types.OrganizationInvitation{ - ID: util.GenerateUUID(), - Email: request.Email, - InviterID: actorID, - OrganizationID: organizationID, - Role: role, - Status: types.OrganizationInvitationStatusPending, - ExpiresAt: expiresAt, - } - createdInvitation, err := invitationRepo.Create(ctx, invitation) if err != nil { return err @@ -156,6 +169,12 @@ func (s *organizationInvitationService) CreateOrganizationInvitation(ctx context return nil, err } + if s.hooks != nil { + if err := s.hooks.AfterCreateOrganizationInvitation(ctx, actor, created); err != nil { + return nil, err + } + } + s.publishOrganizationInvitationCreatedEvent(created, organization) acceptURL := s.buildOrganizationInvitationAcceptURL(created, request.RedirectURL) @@ -311,11 +330,23 @@ func (s *organizationInvitationService) RevokeOrganizationInvitation(ctx context invitation.Status = types.OrganizationInvitationStatusRevoked + if s.hooks != nil { + if err := s.hooks.BeforeUpdateOrganizationInvitation(ctx, actor, invitation); err != nil { + return nil, err + } + } + updated, err := s.orgInvitationRepo.Update(ctx, invitation) if err != nil { return nil, err } + if s.hooks != nil { + if err := s.hooks.AfterUpdateOrganizationInvitation(ctx, actor, updated); err != nil { + return nil, err + } + } + return updated, nil } func (s *organizationInvitationService) AcceptOrganizationInvitation(ctx context.Context, actor *models.Actor, organizationID string, invitationID string) (*types.OrganizationInvitation, error) { @@ -346,6 +377,12 @@ func (s *organizationInvitationService) AcceptOrganizationInvitation(ctx context return nil, coreerrors.ErrForbidden } + if s.hooks != nil { + if err := s.hooks.BeforeUpdateOrganizationInvitation(ctx, actor, invitation); err != nil { + return nil, err + } + } + accepted, err := s.acceptOrganizationInvitations(ctx, actorID, []types.OrganizationInvitation{*invitation}) if err != nil { return nil, err @@ -354,6 +391,12 @@ func (s *organizationInvitationService) AcceptOrganizationInvitation(ctx context return nil, coreerrors.ErrConflict } + if s.hooks != nil { + if err := s.hooks.AfterUpdateOrganizationInvitation(ctx, actor, &accepted[0]); err != nil { + return nil, err + } + } + return &accepted[0], nil } @@ -390,11 +433,23 @@ func (s *organizationInvitationService) RejectOrganizationInvitation(ctx context invitation.Status = types.OrganizationInvitationStatusRejected + if s.hooks != nil { + if err := s.hooks.BeforeUpdateOrganizationInvitation(ctx, actor, invitation); err != nil { + return nil, err + } + } + updated, err := s.orgInvitationRepo.Update(ctx, invitation) if err != nil { return nil, err } + if s.hooks != nil { + if err := s.hooks.AfterUpdateOrganizationInvitation(ctx, actor, updated); err != nil { + return nil, err + } + } + return updated, nil } diff --git a/plugins/organizations/services/organization_invitation_service_test.go b/plugins/organizations/services/organization_invitation_service_test.go index 6133a028..2d556dbc 100644 --- a/plugins/organizations/services/organization_invitation_service_test.go +++ b/plugins/organizations/services/organization_invitation_service_test.go @@ -215,11 +215,11 @@ func TestOrganizationInvitationService_CreateOrganizationInvitation(t *testing.T invRepo.On("Create", mock.Anything, mock.MatchedBy(func(inv *types.OrganizationInvitation) bool { return inv != nil && inv.OrganizationID == "org-1" && inv.InviterID == "user-1" && inv.Email == "user@example.com" && inv.Role == "member" && inv.Status == types.OrganizationInvitationStatusPending && inv.ExpiresAt.After(expectedExpiresAt.Add(-2*time.Second)) && inv.ExpiresAt.Before(expectedExpiresAt.Add(2*time.Second)) })).Return(&types.OrganizationInvitation{ID: "inv-1", OrganizationID: "org-1", InviterID: "user-1", Email: "user@example.com", Role: "member", Status: types.OrganizationInvitationStatusPending, ExpiresAt: expectedExpiresAt}, nil).Once() - hooks.Before = func(invitation *types.OrganizationInvitation) error { + hooks.BeforeCreate = func(ctx context.Context, actor *models.Actor, invitation *types.OrganizationInvitation) error { require.Equal(t, "user@example.com", invitation.Email) return nil } - hooks.After = func(invitation types.OrganizationInvitation) error { + hooks.AfterCreate = func(ctx context.Context, actor *models.Actor, invitation *types.OrganizationInvitation) error { require.Equal(t, "inv-1", invitation.ID) return nil } diff --git a/plugins/organizations/services/organization_member_service.go b/plugins/organizations/services/organization_member_service.go index 34892662..9a24605e 100644 --- a/plugins/organizations/services/organization_member_service.go +++ b/plugins/organizations/services/organization_member_service.go @@ -27,10 +27,15 @@ type organizationMemberService struct { serviceUtils *ServiceUtils membersLimit *int txRunner organizationMemberTxRunner + hooks *ServiceHookExecutor } -func NewOrganizationMemberService(userService rootservices.UserService, accessControlService rootservices.AccessControlService, orgRepo repositories.OrganizationRepository, orgMemberRepo repositories.OrganizationMemberRepository, membersLimit *int, txRunner organizationMemberTxRunner, serviceUtils *ServiceUtils) *organizationMemberService { - return &organizationMemberService{userService: userService, accessControlService: accessControlService, orgRepo: orgRepo, orgMemberRepo: orgMemberRepo, serviceUtils: serviceUtils, membersLimit: membersLimit, txRunner: txRunner} +func NewOrganizationMemberService(userService rootservices.UserService, accessControlService rootservices.AccessControlService, orgRepo repositories.OrganizationRepository, orgMemberRepo repositories.OrganizationMemberRepository, membersLimit *int, txRunner organizationMemberTxRunner, serviceUtils *ServiceUtils, hooks ...*ServiceHookExecutor) *organizationMemberService { + var hook *ServiceHookExecutor + if len(hooks) > 0 { + hook = hooks[0] + } + return &organizationMemberService{userService: userService, accessControlService: accessControlService, orgRepo: orgRepo, orgMemberRepo: orgMemberRepo, serviceUtils: serviceUtils, membersLimit: membersLimit, txRunner: txRunner, hooks: hook} } func (s *organizationMemberService) AddMember(ctx context.Context, actor *models.Actor, organizationID string, request types.AddOrganizationMemberRequest) (*types.OrganizationMember, error) { @@ -77,6 +82,19 @@ func (s *organizationMemberService) AddMember(ctx context.Context, actor *models return nil, coreerrors.ErrBadRequest } + member := &types.OrganizationMember{ + ID: util.GenerateUUID(), + OrganizationID: organizationID, + UserID: userID, + Role: role, + } + + if s.hooks != nil { + if err := s.hooks.BeforeCreateOrganizationMember(ctx, actor, member); err != nil { + return nil, err + } + } + var created *types.OrganizationMember err = s.txRunner.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { memberRepo := s.orgMemberRepo.WithTx(tx) @@ -84,13 +102,6 @@ func (s *organizationMemberService) AddMember(ctx context.Context, actor *models return err } - member := &types.OrganizationMember{ - ID: util.GenerateUUID(), - OrganizationID: organizationID, - UserID: userID, - Role: role, - } - createdMember, err := memberRepo.Create(ctx, member) if err != nil { return err @@ -103,6 +114,12 @@ func (s *organizationMemberService) AddMember(ctx context.Context, actor *models return nil, err } + if s.hooks != nil { + if err := s.hooks.AfterCreateOrganizationMember(ctx, actor, created); err != nil { + return nil, err + } + } + return created, nil } @@ -174,11 +191,23 @@ func (s *organizationMemberService) UpdateMember(ctx context.Context, actor *mod member.Role = role + if s.hooks != nil { + if err := s.hooks.BeforeUpdateOrganizationMember(ctx, actor, member); err != nil { + return nil, err + } + } + updated, err := s.orgMemberRepo.Update(ctx, member) if err != nil { return nil, err } + if s.hooks != nil { + if err := s.hooks.AfterUpdateOrganizationMember(ctx, actor, updated); err != nil { + return nil, err + } + } + return updated, nil } @@ -195,10 +224,22 @@ func (s *organizationMemberService) RemoveMember(ctx context.Context, actor *mod return coreerrors.ErrNotFound } + if s.hooks != nil { + if err := s.hooks.BeforeDeleteOrganizationMember(ctx, actor, member); err != nil { + return err + } + } + if err := s.orgMemberRepo.Delete(ctx, member.ID); err != nil { return err } + if s.hooks != nil { + if err := s.hooks.AfterDeleteOrganizationMember(ctx, actor, member); err != nil { + return err + } + } + return nil } diff --git a/plugins/organizations/services/organization_service.go b/plugins/organizations/services/organization_service.go index 3fdd2235..54ad6039 100644 --- a/plugins/organizations/services/organization_service.go +++ b/plugins/organizations/services/organization_service.go @@ -26,6 +26,7 @@ type organizationService struct { accessControlService rootservices.AccessControlService organizationsLimit *int txRunner organizationTxRunner + hooks *ServiceHookExecutor } type organizationTxRunner interface { @@ -39,7 +40,12 @@ func NewOrganizationService( accessControlService rootservices.AccessControlService, organizationsLimit *int, txRunner organizationTxRunner, + hooks ...*ServiceHookExecutor, ) *organizationService { + var hook *ServiceHookExecutor + if len(hooks) > 0 { + hook = hooks[0] + } return &organizationService{ orgRepo: orgRepo, orgMemberRepo: orgMemberRepo, @@ -47,6 +53,7 @@ func NewOrganizationService( accessControlService: accessControlService, organizationsLimit: organizationsLimit, txRunner: txRunner, + hooks: hook, } } @@ -95,6 +102,12 @@ func (s *organizationService) CreateOrganization(ctx context.Context, actor *mod organization.Metadata = make(map[string]any) } + if s.hooks != nil { + if err := s.hooks.BeforeCreateOrganization(ctx, actor, organization); err != nil { + return nil, err + } + } + var created *types.Organization createFn := func(ctx context.Context, orgRepo repositories.OrganizationRepository, memberRepo repositories.OrganizationMemberRepository) error { if err := s.ensureOrganizationLimit(ctx, actor, orgRepo, memberRepo); err != nil { @@ -129,6 +142,12 @@ func (s *organizationService) CreateOrganization(ctx context.Context, actor *mod return nil, err } + if s.hooks != nil { + if err := s.hooks.AfterCreateOrganization(ctx, actor, created); err != nil { + return nil, err + } + } + return created, nil } @@ -286,11 +305,23 @@ func (s *organizationService) UpdateOrganization(ctx context.Context, actor *mod organization.Metadata = make(map[string]any) } + if s.hooks != nil { + if err := s.hooks.BeforeUpdateOrganization(ctx, actor, organization); err != nil { + return nil, err + } + } + updated, err := s.orgRepo.Update(ctx, organization) if err != nil { return nil, err } + if s.hooks != nil { + if err := s.hooks.AfterUpdateOrganization(ctx, actor, updated); err != nil { + return nil, err + } + } + return updated, nil } @@ -303,15 +334,27 @@ func (s *organizationService) ExistsByID(ctx context.Context, organizationID str } func (s *organizationService) DeleteOrganization(ctx context.Context, actor *models.Actor, organizationID string) error { - _, err := s.serviceUtils.authorizeOwner(ctx, actor, organizationID) + organization, err := s.serviceUtils.authorizeOwner(ctx, actor, organizationID) if err != nil { return err } + if s.hooks != nil { + if err := s.hooks.BeforeDeleteOrganization(ctx, actor, organization); err != nil { + return err + } + } + if err := s.orgRepo.Delete(ctx, organizationID); err != nil { return err } + if s.hooks != nil { + if err := s.hooks.AfterDeleteOrganization(ctx, actor, organization); err != nil { + return err + } + } + return nil } diff --git a/plugins/organizations/services/organization_team_member_service.go b/plugins/organizations/services/organization_team_member_service.go index 5efa8688..973f7831 100644 --- a/plugins/organizations/services/organization_team_member_service.go +++ b/plugins/organizations/services/organization_team_member_service.go @@ -16,6 +16,7 @@ type organizationTeamMemberService struct { orgTeamRepo repositories.OrganizationTeamRepository orgTeamMemberRepo repositories.OrganizationTeamMemberRepository serviceUtils *ServiceUtils + hooks *ServiceHookExecutor } func NewOrganizationTeamMemberService( @@ -24,8 +25,13 @@ func NewOrganizationTeamMemberService( teamRepo repositories.OrganizationTeamRepository, orgTeamMemberRepo repositories.OrganizationTeamMemberRepository, serviceUtils *ServiceUtils, + hooks ...*ServiceHookExecutor, ) *organizationTeamMemberService { - return &organizationTeamMemberService{orgRepo: orgRepo, orgMemberRepo: orgMemberRepo, orgTeamRepo: teamRepo, orgTeamMemberRepo: orgTeamMemberRepo, serviceUtils: serviceUtils} + var hook *ServiceHookExecutor + if len(hooks) > 0 { + hook = hooks[0] + } + return &organizationTeamMemberService{orgRepo: orgRepo, orgMemberRepo: orgMemberRepo, orgTeamRepo: teamRepo, orgTeamMemberRepo: orgTeamMemberRepo, serviceUtils: serviceUtils, hooks: hook} } func (s *organizationTeamMemberService) AddTeamMember(ctx context.Context, actor *models.Actor, organizationID string, teamID string, request types.AddOrganizationTeamMemberRequest) (*types.OrganizationTeamMember, error) { @@ -70,11 +76,23 @@ func (s *organizationTeamMemberService) AddTeamMember(ctx context.Context, actor MemberID: orgMember.ID, } + if s.hooks != nil { + if err := s.hooks.BeforeCreateOrganizationTeamMember(ctx, actor, teamMember); err != nil { + return nil, err + } + } + created, err := s.orgTeamMemberRepo.Create(ctx, teamMember) if err != nil { return nil, err } + if s.hooks != nil { + if err := s.hooks.AfterCreateOrganizationTeamMember(ctx, actor, created); err != nil { + return nil, err + } + } + return created, nil } @@ -167,9 +185,21 @@ func (s *organizationTeamMemberService) RemoveTeamMember(ctx context.Context, ac return coreerrors.ErrNotFound } + if s.hooks != nil { + if err := s.hooks.BeforeDeleteOrganizationTeamMember(ctx, actor, teamMember); err != nil { + return err + } + } + if err := s.orgTeamMemberRepo.DeleteByTeamIDAndMemberID(ctx, teamID, orgMember.ID); err != nil { return err } + if s.hooks != nil { + if err := s.hooks.AfterDeleteOrganizationTeamMember(ctx, actor, teamMember); err != nil { + return err + } + } + return nil } diff --git a/plugins/organizations/services/organization_team_service.go b/plugins/organizations/services/organization_team_service.go index ddbe2678..3e74b734 100644 --- a/plugins/organizations/services/organization_team_service.go +++ b/plugins/organizations/services/organization_team_service.go @@ -20,6 +20,7 @@ type organizationTeamService struct { orgTeamMemberRepo repositories.OrganizationTeamMemberRepository serviceUtils *ServiceUtils txRunner organizationTeamTxRunner + hooks *ServiceHookExecutor } type organizationTeamTxRunner interface { @@ -33,8 +34,13 @@ func NewOrganizationTeamService( orgTeamMemberRepo repositories.OrganizationTeamMemberRepository, serviceUtils *ServiceUtils, txRunner organizationTeamTxRunner, + hooks ...*ServiceHookExecutor, ) *organizationTeamService { - return &organizationTeamService{orgRepo: orgRepo, orgTeamRepo: orgTeamRepo, orgMemberRepo: orgMemberRepo, orgTeamMemberRepo: orgTeamMemberRepo, serviceUtils: serviceUtils, txRunner: txRunner} + var hook *ServiceHookExecutor + if len(hooks) > 0 { + hook = hooks[0] + } + return &organizationTeamService{orgRepo: orgRepo, orgTeamRepo: orgTeamRepo, orgMemberRepo: orgMemberRepo, orgTeamMemberRepo: orgTeamMemberRepo, serviceUtils: serviceUtils, txRunner: txRunner, hooks: hook} } func (s *organizationTeamService) CreateTeam(ctx context.Context, actor *models.Actor, organizationID string, request types.CreateOrganizationTeamRequest) (*types.OrganizationTeam, error) { @@ -78,6 +84,12 @@ func (s *organizationTeamService) CreateTeam(ctx context.Context, actor *models. team.Metadata = make(map[string]any) } + if s.hooks != nil { + if err := s.hooks.BeforeCreateOrganizationTeam(ctx, actor, team); err != nil { + return nil, err + } + } + var created *types.OrganizationTeam createFn := func(ctx context.Context, memberRepo repositories.OrganizationMemberRepository, teamRepo repositories.OrganizationTeamRepository, teamMemberRepo repositories.OrganizationTeamMemberRepository) error { createdTeam, err := teamRepo.Create(ctx, team) @@ -114,6 +126,13 @@ func (s *organizationTeamService) CreateTeam(ctx context.Context, actor *models. if err != nil { return nil, err } + + if s.hooks != nil { + if err := s.hooks.AfterCreateOrganizationTeam(ctx, actor, created); err != nil { + return nil, err + } + } + return created, nil } @@ -121,6 +140,12 @@ func (s *organizationTeamService) CreateTeam(ctx context.Context, actor *models. return nil, err } + if s.hooks != nil { + if err := s.hooks.AfterCreateOrganizationTeam(ctx, actor, created); err != nil { + return nil, err + } + } + return created, nil } @@ -199,11 +224,23 @@ func (s *organizationTeamService) UpdateTeam(ctx context.Context, actor *models. team.Metadata = make(map[string]any) } + if s.hooks != nil { + if err := s.hooks.BeforeUpdateOrganizationTeam(ctx, actor, team); err != nil { + return nil, err + } + } + updated, err := s.orgTeamRepo.Update(ctx, team) if err != nil { return nil, err } + if s.hooks != nil { + if err := s.hooks.AfterUpdateOrganizationTeam(ctx, actor, updated); err != nil { + return nil, err + } + } + return updated, nil } @@ -224,9 +261,21 @@ func (s *organizationTeamService) DeleteTeam(ctx context.Context, actor *models. return coreerrors.ErrNotFound } + if s.hooks != nil { + if err := s.hooks.BeforeDeleteOrganizationTeam(ctx, actor, team); err != nil { + return err + } + } + if err := s.orgTeamRepo.Delete(ctx, teamID); err != nil { return err } + if s.hooks != nil { + if err := s.hooks.AfterDeleteOrganizationTeam(ctx, actor, team); err != nil { + return err + } + } + return nil } diff --git a/plugins/organizations/tests/hooks.go b/plugins/organizations/tests/hooks.go index 1fbaff91..168b5c5c 100644 --- a/plugins/organizations/tests/hooks.go +++ b/plugins/organizations/tests/hooks.go @@ -1,228 +1,47 @@ package tests import ( + "context" + + "github.com/Authula/authula/models" "github.com/Authula/authula/plugins/organizations/types" ) type MockOrganizationHooks struct { - BeforeCreate func(*types.Organization) error - AfterCreate func(types.Organization) error - BeforeUpdate func(*types.Organization) error - AfterUpdate func(types.Organization) error - BeforeDelete func(*types.Organization) error - AfterDelete func(types.Organization) error -} - -func (h *MockOrganizationHooks) BeforeCreateOrganization(organization *types.Organization) error { - if h.BeforeCreate == nil { - return nil - } - return h.BeforeCreate(organization) -} - -func (h *MockOrganizationHooks) AfterCreateOrganization(organization types.Organization) error { - if h.AfterCreate == nil { - return nil - } - return h.AfterCreate(organization) -} - -func (h *MockOrganizationHooks) BeforeUpdateOrganization(organization *types.Organization) error { - if h.BeforeUpdate == nil { - return nil - } - return h.BeforeUpdate(organization) -} - -func (h *MockOrganizationHooks) AfterUpdateOrganization(organization types.Organization) error { - if h.AfterUpdate == nil { - return nil - } - return h.AfterUpdate(organization) -} - -func (h *MockOrganizationHooks) BeforeDeleteOrganization(organization *types.Organization) error { - if h.BeforeDelete == nil { - return nil - } - return h.BeforeDelete(organization) -} - -func (h *MockOrganizationHooks) AfterDeleteOrganization(organization types.Organization) error { - if h.AfterDelete == nil { - return nil - } - return h.AfterDelete(organization) + BeforeCreate func(ctx context.Context, actor *models.Actor, organization *types.Organization) error + AfterCreate func(ctx context.Context, actor *models.Actor, organization *types.Organization) error + BeforeUpdate func(ctx context.Context, actor *models.Actor, organization *types.Organization) error + AfterUpdate func(ctx context.Context, actor *models.Actor, organization *types.Organization) error + BeforeDelete func(ctx context.Context, actor *models.Actor, organization *types.Organization) error + AfterDelete func(ctx context.Context, actor *models.Actor, organization *types.Organization) error } type MockOrganizationMemberHooks struct { - Before func(*types.OrganizationMember) error - After func(types.OrganizationMember) error - BeforeUpdate func(*types.OrganizationMember) error - AfterUpdate func(types.OrganizationMember) error - BeforeDelete func(*types.OrganizationMember) error - AfterDelete func(types.OrganizationMember) error -} - -func (h *MockOrganizationMemberHooks) BeforeCreateOrganizationMember(member *types.OrganizationMember) error { - if h.Before == nil { - return nil - } - return h.Before(member) -} - -func (h *MockOrganizationMemberHooks) AfterCreateOrganizationMember(member types.OrganizationMember) error { - if h.After == nil { - return nil - } - return h.After(member) -} - -func (h *MockOrganizationMemberHooks) BeforeUpdateOrganizationMember(member *types.OrganizationMember) error { - if h.BeforeUpdate == nil { - return nil - } - return h.BeforeUpdate(member) -} - -func (h *MockOrganizationMemberHooks) AfterUpdateOrganizationMember(member types.OrganizationMember) error { - if h.AfterUpdate == nil { - return nil - } - return h.AfterUpdate(member) -} - -func (h *MockOrganizationMemberHooks) BeforeDeleteOrganizationMember(member *types.OrganizationMember) error { - if h.BeforeDelete == nil { - return nil - } - return h.BeforeDelete(member) -} - -func (h *MockOrganizationMemberHooks) AfterDeleteOrganizationMember(member types.OrganizationMember) error { - if h.AfterDelete == nil { - return nil - } - return h.AfterDelete(member) + BeforeCreate func(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error + AfterCreate func(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error + BeforeUpdate func(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error + AfterUpdate func(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error + BeforeDelete func(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error + AfterDelete func(ctx context.Context, actor *models.Actor, member *types.OrganizationMember) error } type MockOrganizationInvitationHooks struct { - Before func(*types.OrganizationInvitation) error - After func(types.OrganizationInvitation) error - BeforeUpdate func(*types.OrganizationInvitation) error - AfterUpdate func(types.OrganizationInvitation) error -} - -func (h *MockOrganizationInvitationHooks) BeforeCreateOrganizationInvitation(invitation *types.OrganizationInvitation) error { - if h.Before == nil { - return nil - } - return h.Before(invitation) -} - -func (h *MockOrganizationInvitationHooks) AfterCreateOrganizationInvitation(invitation types.OrganizationInvitation) error { - if h.After == nil { - return nil - } - return h.After(invitation) -} - -func (h *MockOrganizationInvitationHooks) BeforeUpdateOrganizationInvitation(invitation *types.OrganizationInvitation) error { - if h.BeforeUpdate == nil { - return nil - } - return h.BeforeUpdate(invitation) -} - -func (h *MockOrganizationInvitationHooks) AfterUpdateOrganizationInvitation(invitation types.OrganizationInvitation) error { - if h.AfterUpdate == nil { - return nil - } - return h.AfterUpdate(invitation) + BeforeCreate func(ctx context.Context, actor *models.Actor, invitation *types.OrganizationInvitation) error + AfterCreate func(ctx context.Context, actor *models.Actor, invitation *types.OrganizationInvitation) error + BeforeUpdate func(ctx context.Context, actor *models.Actor, invitation *types.OrganizationInvitation) error + AfterUpdate func(ctx context.Context, actor *models.Actor, invitation *types.OrganizationInvitation) error } type MockOrganizationTeamHooks struct { - BeforeCreate func(*types.OrganizationTeam) error - AfterCreate func(types.OrganizationTeam) error - BeforeUpdate func(*types.OrganizationTeam) error - AfterUpdate func(types.OrganizationTeam) error - BeforeDelete func(*types.OrganizationTeam) error - AfterDelete func(types.OrganizationTeam) error -} - -func (h *MockOrganizationTeamHooks) BeforeCreateOrganizationTeam(team *types.OrganizationTeam) error { - if h.BeforeCreate == nil { - return nil - } - return h.BeforeCreate(team) -} - -func (h *MockOrganizationTeamHooks) AfterCreateOrganizationTeam(team types.OrganizationTeam) error { - if h.AfterCreate == nil { - return nil - } - return h.AfterCreate(team) -} - -func (h *MockOrganizationTeamHooks) BeforeUpdateOrganizationTeam(team *types.OrganizationTeam) error { - if h.BeforeUpdate == nil { - return nil - } - return h.BeforeUpdate(team) -} - -func (h *MockOrganizationTeamHooks) AfterUpdateOrganizationTeam(team types.OrganizationTeam) error { - if h.AfterUpdate == nil { - return nil - } - return h.AfterUpdate(team) -} - -func (h *MockOrganizationTeamHooks) BeforeDeleteOrganizationTeam(team *types.OrganizationTeam) error { - if h.BeforeDelete == nil { - return nil - } - return h.BeforeDelete(team) -} - -func (h *MockOrganizationTeamHooks) AfterDeleteOrganizationTeam(team types.OrganizationTeam) error { - if h.AfterDelete == nil { - return nil - } - return h.AfterDelete(team) + BeforeCreate func(ctx context.Context, actor *models.Actor, team *types.OrganizationTeam) error + AfterCreate func(ctx context.Context, actor *models.Actor, team *types.OrganizationTeam) error + BeforeUpdate func(ctx context.Context, actor *models.Actor, team *types.OrganizationTeam) error + AfterUpdate func(ctx context.Context, actor *models.Actor, team *types.OrganizationTeam) error + BeforeDelete func(ctx context.Context, actor *models.Actor, team *types.OrganizationTeam) error + AfterDelete func(ctx context.Context, actor *models.Actor, team *types.OrganizationTeam) error } type MockOrganizationTeamMemberHooks struct { - BeforeCreate func(*types.OrganizationTeamMember) error - AfterCreate func(types.OrganizationTeamMember) error - BeforeDelete func(*types.OrganizationTeamMember) error - AfterDelete func(types.OrganizationTeamMember) error -} - -func (h *MockOrganizationTeamMemberHooks) BeforeCreateOrganizationTeamMember(teamMember *types.OrganizationTeamMember) error { - if h.BeforeCreate == nil { - return nil - } - return h.BeforeCreate(teamMember) -} - -func (h *MockOrganizationTeamMemberHooks) AfterCreateOrganizationTeamMember(teamMember types.OrganizationTeamMember) error { - if h.AfterCreate == nil { - return nil - } - return h.AfterCreate(teamMember) -} - -func (h *MockOrganizationTeamMemberHooks) BeforeDeleteOrganizationTeamMember(teamMember *types.OrganizationTeamMember) error { - if h.BeforeDelete == nil { - return nil - } - return h.BeforeDelete(teamMember) -} - -func (h *MockOrganizationTeamMemberHooks) AfterDeleteOrganizationTeamMember(teamMember types.OrganizationTeamMember) error { - if h.AfterDelete == nil { - return nil - } - return h.AfterDelete(teamMember) + BeforeCreate func(ctx context.Context, actor *models.Actor, member *types.OrganizationTeamMember) error + AfterCreate func(ctx context.Context, actor *models.Actor, member *types.OrganizationTeamMember) error } diff --git a/plugins/organizations/tests/repositories.go b/plugins/organizations/tests/repositories.go index 0440322d..b10e32c6 100644 --- a/plugins/organizations/tests/repositories.go +++ b/plugins/organizations/tests/repositories.go @@ -14,8 +14,6 @@ import ( type MockOrganizationRepository struct { mock.Mock - Hooks repositories.OrganizationHookExecutor - byID map[string]*types.Organization } func mockResultIsNil(value any) bool { @@ -31,27 +29,12 @@ func mockResultIsNil(value any) bool { } } -func (m *MockOrganizationRepository) SetOrganizationHooks(hooks repositories.OrganizationHookExecutor) { - m.Hooks = hooks -} - func (m *MockOrganizationRepository) Create(ctx context.Context, organization *types.Organization) (*types.Organization, error) { - if m.Hooks != nil { - if err := m.Hooks.BeforeCreateOrganization(organization); err != nil { - return nil, err - } - } args := m.Called(ctx, organization) if mockResultIsNil(args.Get(0)) { return nil, args.Error(1) } - created := args.Get(0).(*types.Organization) - if m.Hooks != nil { - if err := m.Hooks.AfterCreateOrganization(*created); err != nil { - return nil, err - } - } - return created, args.Error(1) + return args.Get(0).(*types.Organization), args.Error(1) } func (m *MockOrganizationRepository) GetByID(ctx context.Context, organizationID string) (*types.Organization, error) { @@ -59,12 +42,7 @@ func (m *MockOrganizationRepository) GetByID(ctx context.Context, organizationID if args.Get(0) == nil { return nil, args.Error(1) } - organization := args.Get(0).(*types.Organization) - if m.byID == nil { - m.byID = map[string]*types.Organization{} - } - m.byID[organizationID] = organization - return organization, args.Error(1) + return args.Get(0).(*types.Organization), args.Error(1) } func (m *MockOrganizationRepository) GetBySlug(ctx context.Context, slug string) (*types.Organization, error) { @@ -84,45 +62,14 @@ func (m *MockOrganizationRepository) GetAllByOwnerID(ctx context.Context, ownerI } func (m *MockOrganizationRepository) Update(ctx context.Context, organization *types.Organization) (*types.Organization, error) { - if m.Hooks != nil { - if err := m.Hooks.BeforeUpdateOrganization(organization); err != nil { - return nil, err - } - } args := m.Called(ctx, organization) if mockResultIsNil(args.Get(0)) { return nil, args.Error(1) } - updated := args.Get(0).(*types.Organization) - if m.Hooks != nil { - if err := m.Hooks.AfterUpdateOrganization(*updated); err != nil { - return nil, err - } - } - return updated, args.Error(1) + return args.Get(0).(*types.Organization), args.Error(1) } func (m *MockOrganizationRepository) Delete(ctx context.Context, organizationID string) error { - if m.Hooks != nil { - organization := m.byID[organizationID] - if organization == nil { - var err error - organization, err = m.GetByID(ctx, organizationID) - if err != nil { - return err - } - } - if organization == nil { - return nil - } - if err := m.Hooks.BeforeDeleteOrganization(organization); err != nil { - return err - } - if err := m.Called(ctx, organizationID).Error(0); err != nil { - return err - } - return m.Hooks.AfterDeleteOrganization(*organization) - } return m.Called(ctx, organizationID).Error(0) } @@ -132,31 +79,14 @@ func (m *MockOrganizationRepository) WithTx(_ bun.IDB) repositories.Organization type MockOrganizationMemberRepository struct { mock.Mock - Hooks repositories.OrganizationMemberHookExecutor - byID map[string]*types.OrganizationMember -} - -func (m *MockOrganizationMemberRepository) SetOrganizationMemberHooks(hooks repositories.OrganizationMemberHookExecutor) { - m.Hooks = hooks } func (m *MockOrganizationMemberRepository) Create(ctx context.Context, member *types.OrganizationMember) (*types.OrganizationMember, error) { - if m.Hooks != nil { - if err := m.Hooks.BeforeCreateOrganizationMember(member); err != nil { - return nil, err - } - } args := m.Called(ctx, member) if mockResultIsNil(args.Get(0)) { return nil, args.Error(1) } - created := args.Get(0).(*types.OrganizationMember) - if m.Hooks != nil { - if err := m.Hooks.AfterCreateOrganizationMember(*created); err != nil { - return nil, err - } - } - return created, args.Error(1) + return args.Get(0).(*types.OrganizationMember), args.Error(1) } func (m *MockOrganizationMemberRepository) CountByOrganizationID(ctx context.Context, organizationID string) (int, error) { @@ -198,54 +128,18 @@ func (m *MockOrganizationMemberRepository) GetByID(ctx context.Context, memberID if args.Get(0) == nil { return nil, args.Error(1) } - member := args.Get(0).(*types.OrganizationMember) - if m.byID == nil { - m.byID = map[string]*types.OrganizationMember{} - } - m.byID[memberID] = member - return member, args.Error(1) + return args.Get(0).(*types.OrganizationMember), args.Error(1) } func (m *MockOrganizationMemberRepository) Update(ctx context.Context, member *types.OrganizationMember) (*types.OrganizationMember, error) { - if m.Hooks != nil { - if err := m.Hooks.BeforeUpdateOrganizationMember(member); err != nil { - return nil, err - } - } args := m.Called(ctx, member) if mockResultIsNil(args.Get(0)) { return nil, args.Error(1) } - updated := args.Get(0).(*types.OrganizationMember) - if m.Hooks != nil { - if err := m.Hooks.AfterUpdateOrganizationMember(*updated); err != nil { - return nil, err - } - } - return updated, args.Error(1) + return args.Get(0).(*types.OrganizationMember), args.Error(1) } func (m *MockOrganizationMemberRepository) Delete(ctx context.Context, memberID string) error { - if m.Hooks != nil { - member := m.byID[memberID] - if member == nil { - var err error - member, err = m.GetByID(ctx, memberID) - if err != nil { - return err - } - } - if member == nil { - return nil - } - if err := m.Hooks.BeforeDeleteOrganizationMember(member); err != nil { - return err - } - if err := m.Called(ctx, memberID).Error(0); err != nil { - return err - } - return m.Hooks.AfterDeleteOrganizationMember(*member) - } return m.Called(ctx, memberID).Error(0) } @@ -255,30 +149,14 @@ func (m *MockOrganizationMemberRepository) WithTx(_ bun.IDB) repositories.Organi type MockOrganizationInvitationRepository struct { mock.Mock - Hooks repositories.OrganizationInvitationHookExecutor -} - -func (m *MockOrganizationInvitationRepository) SetOrganizationInvitationHooks(hooks repositories.OrganizationInvitationHookExecutor) { - m.Hooks = hooks } func (m *MockOrganizationInvitationRepository) Create(ctx context.Context, invitation *types.OrganizationInvitation) (*types.OrganizationInvitation, error) { - if m.Hooks != nil { - if err := m.Hooks.BeforeCreateOrganizationInvitation(invitation); err != nil { - return nil, err - } - } args := m.Called(ctx, invitation) if mockResultIsNil(args.Get(0)) { return nil, args.Error(1) } - created := args.Get(0).(*types.OrganizationInvitation) - if m.Hooks != nil { - if err := m.Hooks.AfterCreateOrganizationInvitation(*created); err != nil { - return nil, err - } - } - return created, args.Error(1) + return args.Get(0).(*types.OrganizationInvitation), args.Error(1) } func (m *MockOrganizationInvitationRepository) GetByID(ctx context.Context, invitationID string) (*types.OrganizationInvitation, error) { @@ -318,22 +196,11 @@ func (m *MockOrganizationInvitationRepository) GetAllPendingByEmail(ctx context. } func (m *MockOrganizationInvitationRepository) Update(ctx context.Context, invitation *types.OrganizationInvitation) (*types.OrganizationInvitation, error) { - if m.Hooks != nil { - if err := m.Hooks.BeforeUpdateOrganizationInvitation(invitation); err != nil { - return nil, err - } - } args := m.Called(ctx, invitation) if mockResultIsNil(args.Get(0)) { return nil, args.Error(1) } - updated := args.Get(0).(*types.OrganizationInvitation) - if m.Hooks != nil { - if err := m.Hooks.AfterUpdateOrganizationInvitation(*updated); err != nil { - return nil, err - } - } - return updated, args.Error(1) + return args.Get(0).(*types.OrganizationInvitation), args.Error(1) } func (m *MockOrganizationInvitationRepository) CountByOrganizationIDAndEmail(ctx context.Context, organizationID, email string) (int, error) { @@ -347,31 +214,14 @@ func (m *MockOrganizationInvitationRepository) WithTx(_ bun.IDB) repositories.Or type MockOrganizationTeamRepository struct { mock.Mock - Hooks repositories.OrganizationTeamHookExecutor - byID map[string]*types.OrganizationTeam -} - -func (m *MockOrganizationTeamRepository) SetOrganizationTeamHooks(hooks repositories.OrganizationTeamHookExecutor) { - m.Hooks = hooks } func (m *MockOrganizationTeamRepository) Create(ctx context.Context, team *types.OrganizationTeam) (*types.OrganizationTeam, error) { - if m.Hooks != nil { - if err := m.Hooks.BeforeCreateOrganizationTeam(team); err != nil { - return nil, err - } - } args := m.Called(ctx, team) if mockResultIsNil(args.Get(0)) { return nil, args.Error(1) } - created := args.Get(0).(*types.OrganizationTeam) - if m.Hooks != nil { - if err := m.Hooks.AfterCreateOrganizationTeam(*created); err != nil { - return nil, err - } - } - return created, args.Error(1) + return args.Get(0).(*types.OrganizationTeam), args.Error(1) } func (m *MockOrganizationTeamRepository) GetByID(ctx context.Context, teamID string) (*types.OrganizationTeam, error) { @@ -379,12 +229,7 @@ func (m *MockOrganizationTeamRepository) GetByID(ctx context.Context, teamID str if args.Get(0) == nil { return nil, args.Error(1) } - team := args.Get(0).(*types.OrganizationTeam) - if m.byID == nil { - m.byID = map[string]*types.OrganizationTeam{} - } - m.byID[teamID] = team - return team, args.Error(1) + return args.Get(0).(*types.OrganizationTeam), args.Error(1) } func (m *MockOrganizationTeamRepository) GetByOrganizationIDAndSlug(ctx context.Context, organizationID, slug string) (*types.OrganizationTeam, error) { @@ -404,45 +249,14 @@ func (m *MockOrganizationTeamRepository) GetAllByOrganizationID(ctx context.Cont } func (m *MockOrganizationTeamRepository) Update(ctx context.Context, team *types.OrganizationTeam) (*types.OrganizationTeam, error) { - if m.Hooks != nil { - if err := m.Hooks.BeforeUpdateOrganizationTeam(team); err != nil { - return nil, err - } - } args := m.Called(ctx, team) if mockResultIsNil(args.Get(0)) { return nil, args.Error(1) } - updated := args.Get(0).(*types.OrganizationTeam) - if m.Hooks != nil { - if err := m.Hooks.AfterUpdateOrganizationTeam(*updated); err != nil { - return nil, err - } - } - return updated, args.Error(1) + return args.Get(0).(*types.OrganizationTeam), args.Error(1) } func (m *MockOrganizationTeamRepository) Delete(ctx context.Context, teamID string) error { - if m.Hooks != nil { - team := m.byID[teamID] - if team == nil { - var err error - team, err = m.GetByID(ctx, teamID) - if err != nil { - return err - } - } - if team == nil { - return nil - } - if err := m.Hooks.BeforeDeleteOrganizationTeam(team); err != nil { - return err - } - if err := m.Called(ctx, teamID).Error(0); err != nil { - return err - } - return m.Hooks.AfterDeleteOrganizationTeam(*team) - } return m.Called(ctx, teamID).Error(0) } @@ -452,31 +266,14 @@ func (m *MockOrganizationTeamRepository) WithTx(_ bun.IDB) repositories.Organiza type MockOrganizationTeamMemberRepository struct { mock.Mock - Hooks repositories.OrganizationTeamMemberHookExecutor - byKey map[string]*types.OrganizationTeamMember -} - -func (m *MockOrganizationTeamMemberRepository) SetOrganizationTeamMemberHooks(hooks repositories.OrganizationTeamMemberHookExecutor) { - m.Hooks = hooks } func (m *MockOrganizationTeamMemberRepository) Create(ctx context.Context, teamMember *types.OrganizationTeamMember) (*types.OrganizationTeamMember, error) { - if m.Hooks != nil { - if err := m.Hooks.BeforeCreateOrganizationTeamMember(teamMember); err != nil { - return nil, err - } - } args := m.Called(ctx, teamMember) if mockResultIsNil(args.Get(0)) { return nil, args.Error(1) } - created := args.Get(0).(*types.OrganizationTeamMember) - if m.Hooks != nil { - if err := m.Hooks.AfterCreateOrganizationTeamMember(*created); err != nil { - return nil, err - } - } - return created, args.Error(1) + return args.Get(0).(*types.OrganizationTeamMember), args.Error(1) } func (m *MockOrganizationTeamMemberRepository) GetByID(ctx context.Context, teamMemberID string) (*types.OrganizationTeamMember, error) { @@ -492,12 +289,7 @@ func (m *MockOrganizationTeamMemberRepository) GetByTeamIDAndMemberID(ctx contex if args.Get(0) == nil { return nil, args.Error(1) } - teamMember := args.Get(0).(*types.OrganizationTeamMember) - if m.byKey == nil { - m.byKey = map[string]*types.OrganizationTeamMember{} - } - m.byKey[teamID+":"+memberID] = teamMember - return teamMember, args.Error(1) + return args.Get(0).(*types.OrganizationTeamMember), args.Error(1) } func (m *MockOrganizationTeamMemberRepository) GetAllByTeamID(ctx context.Context, teamID string, page int, limit int) ([]types.OrganizationTeamMember, error) { @@ -509,27 +301,6 @@ func (m *MockOrganizationTeamMemberRepository) GetAllByTeamID(ctx context.Contex } func (m *MockOrganizationTeamMemberRepository) DeleteByTeamIDAndMemberID(ctx context.Context, teamID, memberID string) error { - if m.Hooks != nil { - key := teamID + ":" + memberID - teamMember := m.byKey[key] - if teamMember == nil { - var err error - teamMember, err = m.GetByTeamIDAndMemberID(ctx, teamID, memberID) - if err != nil { - return err - } - } - if teamMember == nil { - return nil - } - if err := m.Hooks.BeforeDeleteOrganizationTeamMember(teamMember); err != nil { - return err - } - if err := m.Called(ctx, teamID, memberID).Error(0); err != nil { - return err - } - return m.Hooks.AfterDeleteOrganizationTeamMember(*teamMember) - } return m.Called(ctx, teamID, memberID).Error(0) } diff --git a/plugins/organizations/types/config.go b/plugins/organizations/types/config.go index 345f5f62..a371550e 100644 --- a/plugins/organizations/types/config.go +++ b/plugins/organizations/types/config.go @@ -1,6 +1,7 @@ package types import ( + "context" "time" "github.com/Authula/authula/models" @@ -14,7 +15,7 @@ type OrganizationsPluginConfig struct { InvitationExpiresIn time.Duration `json:"invitation_expires_in" toml:"invitation_expires_in"` RequireEmailVerifiedOnInvitation bool `json:"require_email_verified_on_invitation" toml:"require_email_verified_on_invitation"` - DatabaseHooks *OrganizationsDatabaseHooksConfig `json:"-" toml:"-"` + ServiceHooks *OrganizationsServiceHooksConfig `json:"-" toml:"-"` SendOrganizationInvitationEmail func(params SendOrganizationInvitationEmailParams, reqCtx *models.RequestContext) error `json:"-" toml:"-"` } @@ -30,55 +31,53 @@ func (config *OrganizationsPluginConfig) ApplyDefaults() { } } -type OrganizationsDatabaseHooksConfig struct { - Organizations *OrganizationDatabaseHooksConfig - Members *OrganizationMemberDatabaseHooksConfig - Invitations *OrganizationInvitationDatabaseHooksConfig - Teams *OrganizationTeamDatabaseHooksConfig - TeamMembers *OrganizationTeamMemberDatabaseHooksConfig +type OrganizationsServiceHooksConfig struct { + Organizations *OrganizationServiceHooksConfig + Members *OrganizationMemberServiceHooksConfig + Invitations *OrganizationInvitationServiceHooksConfig + Teams *OrganizationTeamServiceHooksConfig + TeamMembers *OrganizationTeamMemberServiceHooksConfig } -type OrganizationDatabaseHooksConfig struct { - BeforeCreate func(organization *Organization) error - AfterCreate func(organization Organization) error - BeforeUpdate func(organization *Organization) error - AfterUpdate func(organization Organization) error - BeforeDelete func(organization *Organization) error - AfterDelete func(organization Organization) error +type OrganizationServiceHooksConfig struct { + BeforeCreate func(ctx context.Context, actor *models.Actor, organization *Organization) error + AfterCreate func(ctx context.Context, actor *models.Actor, organization *Organization) error + BeforeUpdate func(ctx context.Context, actor *models.Actor, organization *Organization) error + AfterUpdate func(ctx context.Context, actor *models.Actor, organization *Organization) error + BeforeDelete func(ctx context.Context, actor *models.Actor, organization *Organization) error + AfterDelete func(ctx context.Context, actor *models.Actor, organization *Organization) error } -type OrganizationMemberDatabaseHooksConfig struct { - BeforeCreate func(member *OrganizationMember) error - AfterCreate func(member OrganizationMember) error - BeforeUpdate func(member *OrganizationMember) error - AfterUpdate func(member OrganizationMember) error - BeforeDelete func(member *OrganizationMember) error - AfterDelete func(member OrganizationMember) error +type OrganizationMemberServiceHooksConfig struct { + BeforeCreate func(ctx context.Context, actor *models.Actor, member *OrganizationMember) error + AfterCreate func(ctx context.Context, actor *models.Actor, member *OrganizationMember) error + BeforeUpdate func(ctx context.Context, actor *models.Actor, member *OrganizationMember) error + AfterUpdate func(ctx context.Context, actor *models.Actor, member *OrganizationMember) error + BeforeDelete func(ctx context.Context, actor *models.Actor, member *OrganizationMember) error + AfterDelete func(ctx context.Context, actor *models.Actor, member *OrganizationMember) error } -type OrganizationInvitationDatabaseHooksConfig struct { - BeforeCreate func(invitation *OrganizationInvitation) error - AfterCreate func(invitation OrganizationInvitation) error - BeforeUpdate func(invitation *OrganizationInvitation) error - AfterUpdate func(invitation OrganizationInvitation) error - BeforeDelete func(invitation *OrganizationInvitation) error - AfterDelete func(invitation OrganizationInvitation) error +type OrganizationInvitationServiceHooksConfig struct { + BeforeCreate func(ctx context.Context, actor *models.Actor, invitation *OrganizationInvitation) error + AfterCreate func(ctx context.Context, actor *models.Actor, invitation *OrganizationInvitation) error + BeforeUpdate func(ctx context.Context, actor *models.Actor, invitation *OrganizationInvitation) error + AfterUpdate func(ctx context.Context, actor *models.Actor, invitation *OrganizationInvitation) error } -type OrganizationTeamDatabaseHooksConfig struct { - BeforeCreate func(team *OrganizationTeam) error - AfterCreate func(team OrganizationTeam) error - BeforeUpdate func(team *OrganizationTeam) error - AfterUpdate func(team OrganizationTeam) error - BeforeDelete func(team *OrganizationTeam) error - AfterDelete func(team OrganizationTeam) error +type OrganizationTeamServiceHooksConfig struct { + BeforeCreate func(ctx context.Context, actor *models.Actor, team *OrganizationTeam) error + AfterCreate func(ctx context.Context, actor *models.Actor, team *OrganizationTeam) error + BeforeUpdate func(ctx context.Context, actor *models.Actor, team *OrganizationTeam) error + AfterUpdate func(ctx context.Context, actor *models.Actor, team *OrganizationTeam) error + BeforeDelete func(ctx context.Context, actor *models.Actor, team *OrganizationTeam) error + AfterDelete func(ctx context.Context, actor *models.Actor, team *OrganizationTeam) error } -type OrganizationTeamMemberDatabaseHooksConfig struct { - BeforeCreate func(member *OrganizationTeamMember) error - AfterCreate func(member OrganizationTeamMember) error - BeforeDelete func(member *OrganizationTeamMember) error - AfterDelete func(member OrganizationTeamMember) error +type OrganizationTeamMemberServiceHooksConfig struct { + BeforeCreate func(ctx context.Context, actor *models.Actor, member *OrganizationTeamMember) error + AfterCreate func(ctx context.Context, actor *models.Actor, member *OrganizationTeamMember) error + BeforeDelete func(ctx context.Context, actor *models.Actor, member *OrganizationTeamMember) error + AfterDelete func(ctx context.Context, actor *models.Actor, member *OrganizationTeamMember) error } type SendOrganizationInvitationEmailParams struct {