diff --git a/api/handle_sanction_checks.go b/api/handle_sanction_checks.go index 2709c231d..4fafe8733 100644 --- a/api/handle_sanction_checks.go +++ b/api/handle_sanction_checks.go @@ -7,6 +7,7 @@ import ( "github.com/checkmarble/marble-backend/models" "github.com/checkmarble/marble-backend/pure_utils" "github.com/checkmarble/marble-backend/usecases" + "github.com/checkmarble/marble-backend/utils" "github.com/gin-gonic/gin" ) @@ -38,18 +39,28 @@ func handleUpdateSanctionCheckMatchStatus(uc usecases.Usecases) func(c *gin.Cont ctx := c.Request.Context() matchId := c.Param("id") - var update dto.SanctionCheckMatchUpdateDto + var payload dto.SanctionCheckMatchUpdateDto - if presentError(ctx, c, c.ShouldBindJSON(&update)) || - presentError(ctx, c, update.Validate()) { + if presentError(ctx, c, c.ShouldBindJSON(&payload)) { + return + } + + creds, ok := utils.CredentialsFromCtx(ctx) + + if !ok { + presentError(ctx, c, models.ErrUnknownUser) + return + } + + update, err := dto.AdaptSanctionCheckMatchUpdateInputDto(matchId, creds.ActorIdentity.UserId, payload) + + if presentError(ctx, c, err) { return } uc := usecasesWithCreds(ctx, uc).NewSanctionCheckUsecase() - match, err := uc.UpdateMatchStatus(ctx, matchId, models.SanctionCheckMatchUpdate{ - Status: update.Status, - }) + match, err := uc.UpdateMatchStatus(ctx, update) if presentError(ctx, c, err) { return @@ -87,12 +98,21 @@ func handleCreateSanctionCheckMatchComment(uc usecases.Usecases) func(c *gin.Con return } + creds, ok := utils.CredentialsFromCtx(ctx) + + if !ok { + presentError(ctx, c, models.ErrUnknownUser) + return + } + uc := usecasesWithCreds(ctx, uc).NewSanctionCheckUsecase() + comment, err := dto.AdaptSanctionCheckMatchCommentInputDto(matchId, creds.ActorIdentity.UserId, payload) + + if presentError(ctx, c, err) { + return + } - comment, err := uc.MatchAddComment(ctx, matchId, models.SanctionCheckMatchComment{ - MatchId: matchId, - Comment: payload.Comment, - }) + comment, err = uc.MatchAddComment(ctx, matchId, comment) if presentError(ctx, c, err) { return diff --git a/dto/sanction_check_dto.go b/dto/sanction_check_dto.go index a21dd6198..64ed231b5 100644 --- a/dto/sanction_check_dto.go +++ b/dto/sanction_check_dto.go @@ -92,13 +92,19 @@ type SanctionCheckMatchUpdateDto struct { Status string `json:"status"` } -func (dto SanctionCheckMatchUpdateDto) Validate() error { +func AdaptSanctionCheckMatchUpdateInputDto(matchId string, reviewerId models.UserId, + dto SanctionCheckMatchUpdateDto, +) (models.SanctionCheckMatchUpdate, error) { if !slices.Contains(ValidSanctionCheckMatchStatuses, dto.Status) { - return errors.Wrap(models.BadParameterError, - "invalid status for sanction check match") + return models.SanctionCheckMatchUpdate{}, + errors.Wrap(models.BadParameterError, "invalid status for sanction check match") } - return nil + return models.SanctionCheckMatchUpdate{ + MatchId: matchId, + ReviewerId: reviewerId, + Status: dto.Status, + }, nil } type SanctionCheckMatchCommentDto struct { @@ -108,6 +114,18 @@ type SanctionCheckMatchCommentDto struct { CreatedAt time.Time `json:"created_at"` } +func AdaptSanctionCheckMatchCommentInputDto(matchId string, commenterId models.UserId, + m SanctionCheckMatchCommentDto, +) (models.SanctionCheckMatchComment, error) { + match := models.SanctionCheckMatchComment{ + MatchId: matchId, + CommenterId: commenterId, + Comment: m.Comment, + } + + return match, nil +} + func AdaptSanctionCheckMatchCommentDto(m models.SanctionCheckMatchComment) SanctionCheckMatchCommentDto { match := SanctionCheckMatchCommentDto{ Id: m.Id, diff --git a/models/sanction_check.go b/models/sanction_check.go index 1b3c4967c..914cbf3a2 100644 --- a/models/sanction_check.go +++ b/models/sanction_check.go @@ -39,6 +39,7 @@ type SanctionCheckMatch struct { } type SanctionCheckMatchUpdate struct { + MatchId string ReviewerId UserId Status string } diff --git a/usecases/sanction_check_usecase.go b/usecases/sanction_check_usecase.go index b98d5ce9c..71e2896be 100644 --- a/usecases/sanction_check_usecase.go +++ b/usecases/sanction_check_usecase.go @@ -4,9 +4,9 @@ import ( "context" "github.com/checkmarble/marble-backend/models" + "github.com/checkmarble/marble-backend/pure_utils" "github.com/checkmarble/marble-backend/repositories" "github.com/checkmarble/marble-backend/usecases/executor_factory" - "github.com/checkmarble/marble-backend/utils" "github.com/pkg/errors" ) @@ -31,6 +31,11 @@ type SanctionCheckOrganizationRepository interface { GetOrganizationById(ctx context.Context, exec repositories.Executor, organizationId string) (models.Organization, error) } +type SanctionCheckInboxRepository interface { + ListInboxes(ctx context.Context, exec repositories.Executor, organizationId string, + inboxIds []string, withCaseCount bool) ([]models.Inbox, error) +} + type SanctionCheckRepository interface { ListSanctionChecksForDecision(context.Context, repositories.Executor, string) ([]models.SanctionCheck, error) GetSanctionCheck(context.Context, repositories.Executor, string) (models.SanctionCheck, error) @@ -54,6 +59,7 @@ type SanctionCheckUsecase struct { organizationRepository SanctionCheckOrganizationRepository decisionRepository SanctionCheckDecisionRepository + inboxRepository SanctionCheckInboxRepository openSanctionsProvider SanctionCheckProvider repository SanctionCheckRepository executorFactory executor_factory.ExecutorFactory @@ -121,21 +127,14 @@ func (uc SanctionCheckUsecase) InsertResults(ctx context.Context, return uc.repository.InsertSanctionCheck(ctx, exec, decision) } -func (uc SanctionCheckUsecase) UpdateMatchStatus(ctx context.Context, matchId string, +func (uc SanctionCheckUsecase) UpdateMatchStatus(ctx context.Context, update models.SanctionCheckMatchUpdate, ) (models.SanctionCheckMatch, error) { - match, err := uc.enforceCanReadOrUpdateMatchCase(ctx, matchId) + match, err := uc.enforceCanReadOrUpdateMatchCase(ctx, update.MatchId) if err != nil { return models.SanctionCheckMatch{}, err } - creds, ok := utils.CredentialsFromCtx(ctx) - if !ok { - return models.SanctionCheckMatch{}, errors.Wrap(models.UnAuthorizedError, "could not retrieve user identity") - } - - update.ReviewerId = creds.ActorIdentity.UserId - // TODO: should we also have some form of automatic cascade between matches? Such as "if we have a confirmed hit, all other matches are no hit"? return uc.repository.UpdateSanctionCheckMatchStatus(ctx, uc.executorFactory.NewExecutor(), match, update) @@ -156,14 +155,6 @@ func (uc SanctionCheckUsecase) MatchAddComment(ctx context.Context, matchId stri return models.SanctionCheckMatchComment{}, err } - creds, ok := utils.CredentialsFromCtx(ctx) - if !ok { - return models.SanctionCheckMatchComment{}, - errors.Wrap(models.UnAuthorizedError, "could not get principal ID from credentials") - } - - comment.CommenterId = creds.ActorIdentity.UserId - return uc.repository.AddSanctionCheckMatchComment(ctx, uc.executorFactory.NewExecutor(), comment) } @@ -191,9 +182,17 @@ func (uc SanctionCheckUsecase) enforceCanReadOrUpdateMatchCase(ctx context.Conte "this sanction check is not linked to a case") } - inboxes := []string{decision[0].Case.InboxId} + inboxes, err := uc.inboxRepository.ListInboxes(ctx, uc.executorFactory.NewExecutor(), decision[0].OrganizationId, nil, false) + if err != nil { + return models.SanctionCheckMatch{}, errors.Wrap(err, + "could not retrieve organization inboxes") + } + + inboxIds := pure_utils.Map(inboxes, func(inbox models.Inbox) string { + return inbox.Id + }) - if err := uc.enforceSecurityCase.ReadOrUpdateCase(*decision[0].Case, inboxes); err != nil { + if err := uc.enforceSecurityCase.ReadOrUpdateCase(*decision[0].Case, inboxIds); err != nil { return models.SanctionCheckMatch{}, err } diff --git a/usecases/usecases_with_creds.go b/usecases/usecases_with_creds.go index f099bdf08..9d7bd600e 100644 --- a/usecases/usecases_with_creds.go +++ b/usecases/usecases_with_creds.go @@ -123,6 +123,7 @@ func (usecases *UsecasesWithCreds) NewSanctionCheckUsecase() SanctionCheckUsecas enforceSecurityCase: usecases.NewEnforceCaseSecurity(), organizationRepository: usecases.Repositories.OrganizationRepository, decisionRepository: &usecases.Repositories.MarbleDbRepository, + inboxRepository: &usecases.Repositories.MarbleDbRepository, openSanctionsProvider: usecases.Repositories.OpenSanctionsRepository, repository: &usecases.Repositories.MarbleDbRepository, executorFactory: usecases.NewExecutorFactory(),