Skip to content

Commit 7d0b6f5

Browse files
OrriMandarinCopilot
andcommitted
feat(continuous_screening): handle whitelisting (#1338)
* feat(continuous_monitoring): update matches status and add in whitelist * test: Add unit tests for UpdateContinuousScreeningMatchStatus * feat(continuous_monitoring): add whitelist list in opensanction query * fix test * Update usecases/continuous_screening/screening.go Co-authored-by: Copilot <[email protected]> * chore: minor change --------- Co-authored-by: Copilot <[email protected]>
1 parent a287c31 commit 7d0b6f5

30 files changed

+1300
-74
lines changed

api/handle_continuous_screening.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,33 @@ func handleListContinuousScreeningsForOrg(uc usecases.Usecases) func(c *gin.Cont
201201
)
202202
}
203203
}
204+
205+
func handleUpdateContinuousScreeningMatchStatus(uc usecases.Usecases) func(c *gin.Context) {
206+
return func(c *gin.Context) {
207+
ctx := c.Request.Context()
208+
matchId := c.Param("id")
209+
210+
var payload dto.ScreeningMatchUpdateDto
211+
if presentError(ctx, c, c.ShouldBindJSON(&payload)) {
212+
return
213+
}
214+
215+
creds, ok := utils.CredentialsFromCtx(ctx)
216+
if !ok {
217+
presentError(ctx, c, models.ErrUnknownUser)
218+
return
219+
}
220+
update, err := dto.AdaptScreeningMatchUpdateInputDto(matchId, creds.ActorIdentity.UserId, payload)
221+
if presentError(ctx, c, err) {
222+
return
223+
}
224+
225+
uc := usecasesWithCreds(ctx, uc).NewContinuousScreeningUsecase()
226+
match, err := uc.UpdateContinuousScreeningMatchStatus(ctx, update)
227+
if presentError(ctx, c, err) {
228+
return
229+
}
230+
231+
c.JSON(http.StatusOK, dto.AdaptContinuousScreeningMatchDto(match))
232+
}
233+
}

api/routes.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ func addRoutes(r *gin.Engine, conf Configuration, uc usecases.Usecases, auth uti
205205
)
206206
router.POST("/continuous-screenings/objects", tom,
207207
handleInsertContinuousScreeningObject(uc))
208+
router.PATCH("/continuous-screenings/matches/:id", tom,
209+
handleUpdateContinuousScreeningMatchStatus(uc))
208210

209211
router.GET("/scenario-publications", tom, handleListScenarioPublications(uc))
210212
router.POST("/scenario-publications", tom, handleCreateScenarioPublication(uc))

mocks/case_usecase.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,8 @@ func (m *CaseEditor) CreateCase(
2323
args := m.Called(ctx, tx, userId, createCaseAttributes, fromEndUser)
2424
return args.Get(0).(models.Case), args.Error(1)
2525
}
26+
27+
func (m *CaseEditor) PerformCaseActionSideEffects(ctx context.Context, tx repositories.Transaction, caseModel models.Case) error {
28+
args := m.Called(ctx, tx, caseModel)
29+
return args.Error(0)
30+
}

mocks/continuous_screening_repository.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,55 @@ func (m *ContinuousScreeningRepository) ListContinuousScreeningsForOrg(
120120
return args.Get(0).([]models.ContinuousScreeningWithMatches), args.Error(1)
121121
}
122122

123+
func (m *ContinuousScreeningRepository) GetContinuousScreeningWithMatchesById(
124+
ctx context.Context,
125+
exec repositories.Executor,
126+
id uuid.UUID,
127+
) (models.ContinuousScreeningWithMatches, error) {
128+
args := m.Called(ctx, exec, id)
129+
return args.Get(0).(models.ContinuousScreeningWithMatches), args.Error(1)
130+
}
131+
132+
func (m *ContinuousScreeningRepository) GetContinuousScreeningMatch(
133+
ctx context.Context,
134+
exec repositories.Executor,
135+
id uuid.UUID,
136+
) (models.ContinuousScreeningMatch, error) {
137+
args := m.Called(ctx, exec, id)
138+
return args.Get(0).(models.ContinuousScreeningMatch), args.Error(1)
139+
}
140+
141+
func (m *ContinuousScreeningRepository) UpdateContinuousScreeningStatus(
142+
ctx context.Context,
143+
exec repositories.Executor,
144+
id uuid.UUID,
145+
newStatus models.ScreeningStatus,
146+
) (models.ContinuousScreening, error) {
147+
args := m.Called(ctx, exec, id, newStatus)
148+
return args.Get(0).(models.ContinuousScreening), args.Error(1)
149+
}
150+
151+
func (m *ContinuousScreeningRepository) UpdateContinuousScreeningMatchStatus(
152+
ctx context.Context,
153+
exec repositories.Executor,
154+
id uuid.UUID,
155+
status models.ScreeningMatchStatus,
156+
reviewerId *uuid.UUID,
157+
) (models.ContinuousScreeningMatch, error) {
158+
args := m.Called(ctx, exec, id, status, reviewerId)
159+
return args.Get(0).(models.ContinuousScreeningMatch), args.Error(1)
160+
}
161+
162+
func (m *ContinuousScreeningRepository) SearchScreeningMatchWhitelist(
163+
ctx context.Context,
164+
exec repositories.Executor,
165+
orgId string,
166+
counterpartyId, entityId *string,
167+
) ([]models.ScreeningWhitelist, error) {
168+
args := m.Called(ctx, exec, orgId, counterpartyId, entityId)
169+
return args.Get(0).([]models.ScreeningWhitelist), args.Error(1)
170+
}
171+
123172
func (m *ContinuousScreeningRepository) GetInboxById(
124173
ctx context.Context,
125174
exec repositories.Executor,
@@ -129,6 +178,46 @@ func (m *ContinuousScreeningRepository) GetInboxById(
129178
return args.Get(0).(models.Inbox), args.Error(1)
130179
}
131180

181+
func (m *ContinuousScreeningRepository) ListInboxes(
182+
ctx context.Context,
183+
exec repositories.Executor,
184+
orgId string,
185+
withCaseCount bool,
186+
) ([]models.Inbox, error) {
187+
args := m.Called(ctx, exec, orgId, withCaseCount)
188+
return args.Get(0).([]models.Inbox), args.Error(1)
189+
}
190+
191+
func (m *ContinuousScreeningRepository) GetCaseById(
192+
ctx context.Context,
193+
exec repositories.Executor,
194+
caseId string,
195+
) (models.Case, error) {
196+
args := m.Called(ctx, exec, caseId)
197+
return args.Get(0).(models.Case), args.Error(1)
198+
}
199+
200+
func (m *ContinuousScreeningRepository) CreateCaseEvent(
201+
ctx context.Context,
202+
exec repositories.Executor,
203+
createCaseEventAttributes models.CreateCaseEventAttributes,
204+
) error {
205+
args := m.Called(ctx, exec, createCaseEventAttributes)
206+
return args.Error(0)
207+
}
208+
209+
func (m *ContinuousScreeningRepository) AddScreeningMatchWhitelist(
210+
ctx context.Context,
211+
exec repositories.Executor,
212+
orgId string,
213+
counterpartyId string,
214+
entityId string,
215+
reviewerId *models.UserId,
216+
) error {
217+
args := m.Called(ctx, exec, orgId, counterpartyId, entityId, reviewerId)
218+
return args.Error(0)
219+
}
220+
132221
type ContinuousScreeningClientDbRepository struct {
133222
mock.Mock
134223
}

mocks/continuous_screening_usecase.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,14 @@ func (m *ContinuousScreeningUsecase) GetIngestedObject(
3636

3737
func (m *ContinuousScreeningUsecase) DoScreening(
3838
ctx context.Context,
39+
exec repositories.Executor,
3940
ingestedObject models.DataModelObject,
4041
mapping models.ContinuousScreeningDataModelMapping,
4142
config models.ContinuousScreeningConfig,
43+
objectType string,
44+
objectId string,
4245
) (models.ScreeningWithMatches, error) {
43-
args := m.Called(ctx, ingestedObject, mapping, config)
46+
args := m.Called(ctx, exec, ingestedObject, mapping, config, objectType, objectId)
4447
return args.Get(0).(models.ScreeningWithMatches), args.Error(1)
4548
}
4649

mocks/enforce_security.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package mocks
22

33
import (
4+
"context"
5+
46
"github.com/google/uuid"
57
"github.com/stretchr/testify/mock"
68

@@ -222,3 +224,33 @@ func (e *EnforceSecurity) ReadContinuousScreeningHit(hit models.ContinuousScreen
222224
args := e.Called(hit)
223225
return args.Error(0)
224226
}
227+
228+
func (e *EnforceSecurity) WriteContinuousScreeningHit(orgId uuid.UUID) error {
229+
args := e.Called(orgId)
230+
return args.Error(0)
231+
}
232+
233+
func (e *EnforceSecurity) ReadOrUpdateCase(c models.CaseMetadata, availableInboxIds []uuid.UUID) error {
234+
args := e.Called(c, availableInboxIds)
235+
return args.Error(0)
236+
}
237+
238+
func (e *EnforceSecurity) CreateCase(input models.CreateCaseAttributes, availableInboxIds []uuid.UUID) error {
239+
args := e.Called(input, availableInboxIds)
240+
return args.Error(0)
241+
}
242+
243+
func (e *EnforceSecurity) ReadWhitelist(ctx context.Context) error {
244+
args := e.Called(ctx)
245+
return args.Error(0)
246+
}
247+
248+
func (e *EnforceSecurity) WriteWhitelist(ctx context.Context) error {
249+
args := e.Called(ctx)
250+
return args.Error(0)
251+
}
252+
253+
func (e *EnforceSecurity) PerformFreeformSearch(ctx context.Context) error {
254+
args := e.Called(ctx)
255+
return args.Error(0)
256+
}

mocks/inboxes_repository.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,68 +17,68 @@ type InboxRepository struct {
1717
func (r *InboxRepository) ListInboxes(ctx context.Context, exec repositories.Executor,
1818
organizationId string, inboxIds []uuid.UUID, withCaseCount bool,
1919
) ([]models.Inbox, error) {
20-
args := r.Called(exec, organizationId, inboxIds)
20+
args := r.Called(ctx, exec, organizationId, inboxIds, withCaseCount)
2121
return args.Get(0).([]models.Inbox), args.Error(1)
2222
}
2323

2424
func (r *InboxRepository) GetInboxById(ctx context.Context, exec repositories.Executor, inboxId uuid.UUID) (models.Inbox, error) {
25-
args := r.Called(exec, inboxId)
25+
args := r.Called(ctx, exec, inboxId)
2626
return args.Get(0).(models.Inbox), args.Error(1)
2727
}
2828

2929
func (r *InboxRepository) CreateInbox(ctx context.Context, exec repositories.Executor,
3030
input models.CreateInboxInput, newInboxId uuid.UUID,
3131
) error {
32-
args := r.Called(exec, input, newInboxId)
32+
args := r.Called(ctx, exec, input, newInboxId)
3333
return args.Error(0)
3434
}
3535

3636
func (r *InboxRepository) UpdateInbox(ctx context.Context, exec repositories.Executor,
3737
inboxId uuid.UUID, name *string, escalationInboxId *uuid.UUID, autoAssignEnabled *bool,
3838
) error {
39-
args := r.Called(exec, inboxId, name, escalationInboxId, autoAssignEnabled)
39+
args := r.Called(ctx, exec, inboxId, name, escalationInboxId, autoAssignEnabled)
4040
return args.Error(0)
4141
}
4242

4343
func (r *InboxRepository) SoftDeleteInbox(ctx context.Context, exec repositories.Executor, inboxId uuid.UUID) error {
44-
args := r.Called(exec, inboxId)
44+
args := r.Called(ctx, exec, inboxId)
4545
return args.Error(0)
4646
}
4747

4848
func (r *InboxRepository) ListOrganizationCases(ctx context.Context, exec repositories.Executor,
4949
filters models.CaseFilters, pagination models.PaginationAndSorting,
5050
) ([]models.Case, error) {
51-
args := r.Called(exec, filters, pagination)
51+
args := r.Called(ctx, exec, filters, pagination)
5252
return args.Get(0).([]models.Case), args.Error(1)
5353
}
5454

5555
func (r *InboxRepository) ListInboxUsers(ctx context.Context, exec repositories.Executor,
5656
filters models.InboxUserFilterInput,
5757
) ([]models.InboxUser, error) {
58-
args := r.Called(exec, filters)
58+
args := r.Called(ctx, exec, filters)
5959
return args.Get(0).([]models.InboxUser), args.Error(1)
6060
}
6161

6262
func (r *InboxRepository) GetInboxUserById(ctx context.Context, exec repositories.Executor, inboxUserId uuid.UUID) (models.InboxUser, error) {
63-
args := r.Called(exec, inboxUserId)
63+
args := r.Called(ctx, exec, inboxUserId)
6464
return args.Get(0).(models.InboxUser), args.Error(1)
6565
}
6666

6767
func (r *InboxRepository) CreateInboxUser(ctx context.Context, exec repositories.Executor,
6868
input models.CreateInboxUserInput, newInboxUserId uuid.UUID,
6969
) error {
70-
args := r.Called(exec, input, newInboxUserId)
70+
args := r.Called(ctx, exec, input, newInboxUserId)
7171
return args.Error(0)
7272
}
7373

7474
func (r *InboxRepository) UpdateInboxUser(ctx context.Context, exec repositories.Executor,
7575
inboxUserId uuid.UUID, role *models.InboxUserRole, autoAssignable *bool,
7676
) error {
77-
args := r.Called(exec, inboxUserId, role, autoAssignable)
77+
args := r.Called(ctx, exec, inboxUserId, role, autoAssignable)
7878
return args.Error(0)
7979
}
8080

8181
func (r *InboxRepository) DeleteInboxUser(ctx context.Context, exec repositories.Executor, inboxUserId uuid.UUID) error {
82-
args := r.Called(exec, inboxUserId)
82+
args := r.Called(ctx, exec, inboxUserId)
8383
return args.Error(0)
8484
}

models/opensanctions.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ type OpenSanctionsQuery struct {
3939
InitialQuery []OpenSanctionsCheckQuery
4040
Config ScreeningConfig
4141
OrgConfig OrganizationOpenSanctionsConfig
42+
// cf: `exclude_entity_ids` in the OpenSanctions query
43+
WhitelistedEntityIds []string
4244
}
4345

4446
type OpenSanctionsCheckQuery struct {

models/permission.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const (
6262
CONTINUOUS_SCREENING_CONFIG_READ
6363
CONTINUOUS_SCREENING_CONFIG_WRITE
6464
CONTINUOUS_SCREENING_HIT_READ
65+
CONTINUOUS_SCREENING_HIT_WRITE
6566
CONTINUOUS_SCREENING_OBJECT_WRITE
6667
ANNOTATION_DELETE
6768
)
@@ -123,6 +124,7 @@ func (r Permission) String() (string, error) {
123124
"CONTINUOUS_SCREENING_CONFIG_READ",
124125
"CONTINUOUS_SCREENING_CONFIG_WRITE",
125126
"CONTINUOUS_SCREENING_HIT_READ",
127+
"CONTINUOUS_SCREENING_HIT_WRITE",
126128
"CONTINUOUS_SCREENING_OBJECT_WRITE",
127129
"ANNOTATION_DELETE",
128130
}

models/role_permission.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ var VIEWER_PERMISSIONS = []Permission{
1919
CONTINUOUS_SCREENING_CONFIG_READ,
2020
CONTINUOUS_SCREENING_CONFIG_WRITE,
2121
CONTINUOUS_SCREENING_HIT_READ,
22+
CONTINUOUS_SCREENING_HIT_WRITE,
2223
CONTINUOUS_SCREENING_OBJECT_WRITE,
2324
}
2425

@@ -71,6 +72,7 @@ var ROLES_PERMISSIOMS = map[Role][]Permission{
7172
CONTINUOUS_SCREENING_CONFIG_READ,
7273
CONTINUOUS_SCREENING_CONFIG_WRITE,
7374
CONTINUOUS_SCREENING_HIT_READ,
75+
CONTINUOUS_SCREENING_HIT_WRITE,
7476
CONTINUOUS_SCREENING_OBJECT_WRITE,
7577
},
7678
TRANSFER_CHECK_API_CLIENT: {

0 commit comments

Comments
 (0)