Skip to content

Commit cf8c27c

Browse files
ILLDEV-464 Log duplicate check results (#694)
* ILLDEV-464 Log duplicate check results * ILLDEV-464 Fix copilot comments
1 parent 712d3b9 commit cf8c27c

5 files changed

Lines changed: 76 additions & 31 deletions

File tree

broker/events/eventmodels.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package events
22

33
import (
4+
"github.com/indexdata/crosslink/broker/catalog"
45
pr_db "github.com/indexdata/crosslink/broker/patron_request/db"
56
"github.com/indexdata/crosslink/httpclient"
67
"github.com/indexdata/crosslink/iso18626"
@@ -160,3 +161,12 @@ func NewProblemResult(kind string, details string) (EventStatus, *EventResult) {
160161
}
161162

162163
const MUST_LOCATE = "mustLocate"
164+
165+
type DuplicateCheck struct {
166+
Enabled bool `json:"enabled"`
167+
LookupParams *catalog.LookupParams `json:"lookupParams"`
168+
WindowHours *int `json:"windowHours"`
169+
CutoffTime *string `json:"cutoffTime"`
170+
Duplicate bool `json:"duplicate"`
171+
MatchedTransactionId *string `json:"matchedTransactionId"`
172+
}

broker/handler/iso18626-handler.go

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ var lookupQueryBuilder = utils.Must(catalog.NewQueryBuilderGen(&directory.QueryC
4040
Type: new(directory.Cql),
4141
}))
4242

43-
var queryTimeFormat = "2006-01-02 15:04:05"
43+
const queryTimeFormat = "2006-01-02 15:04:05"
44+
const duplicateCheckKey = "duplicateCheck"
4445

4546
type ErrorValue string
4647

@@ -79,7 +80,7 @@ var ErrDuplicateRequest = errors.New(string(ReqIsDuplicate))
7980
var waitingReqs = map[string]RequestWait{}
8081

8182
type Iso18626HandlerInterface interface {
82-
HandleRequest(ctx common.ExtendedContext, illMessage *iso18626.ISO18626Message, w http.ResponseWriter)
83+
HandleRequest(ctx common.ExtendedContext, illMessage *iso18626.ISO18626Message, w http.ResponseWriter) map[string]any
8384
HandleRequestingAgencyMessage(ctx common.ExtendedContext, illMessage *iso18626.ISO18626Message, w http.ResponseWriter)
8485
HandleSupplyingAgencyMessage(ctx common.ExtendedContext, illMessage *iso18626.ISO18626Message, w http.ResponseWriter)
8586
}
@@ -149,9 +150,10 @@ func Iso18626PostHandler(repo ill_db.IllRepo, eventBus events.EventBus, dirAdapt
149150
}
150151
}
151152

152-
func handleNewRequest(ctx common.ExtendedContext, request *iso18626.Request, repo ill_db.IllRepo, requesterSymbol pgtype.Text, peers []ill_db.Peer) (string, error) {
153-
if err := checkDuplicateRequest(ctx, request, repo, requesterSymbol.String, peers[0]); err != nil {
154-
return "", err
153+
func handleNewRequest(ctx common.ExtendedContext, request *iso18626.Request, repo ill_db.IllRepo, requesterSymbol pgtype.Text, peers []ill_db.Peer) (string, map[string]any, error) {
154+
resultMap, err := checkDuplicateRequest(ctx, request, repo, requesterSymbol.String, peers[0])
155+
if err != nil {
156+
return "", resultMap, err
155157
}
156158

157159
supplierSymbol := createPgText(request.Header.SupplyingAgencyId.AgencyIdType.Text + ":" + request.Header.SupplyingAgencyId.AgencyIdValue)
@@ -174,7 +176,7 @@ func handleNewRequest(ctx common.ExtendedContext, request *iso18626.Request, rep
174176
Time: request.Header.Timestamp.Time,
175177
Valid: true,
176178
}
177-
_, err := repo.SaveIllTransaction(ctx, ill_db.SaveIllTransactionParams{
179+
_, err = repo.SaveIllTransaction(ctx, ill_db.SaveIllTransactionParams{
178180
ID: id,
179181
Timestamp: timestamp,
180182
RequesterSymbol: requesterSymbol,
@@ -185,62 +187,71 @@ func handleNewRequest(ctx common.ExtendedContext, request *iso18626.Request, rep
185187
SupplierRequestID: supplierRequestId,
186188
IllTransactionData: illTransactionData,
187189
})
188-
return id, err
190+
return id, resultMap, err
189191
}
190192

191-
func checkDuplicateRequest(ctx common.ExtendedContext, request *iso18626.Request, repo ill_db.IllRepo, requesterSymbol string, peer ill_db.Peer) error {
193+
func checkDuplicateRequest(ctx common.ExtendedContext, request *iso18626.Request, repo ill_db.IllRepo, requesterSymbol string, peer ill_db.Peer) (map[string]any, error) {
194+
resultMap := map[string]any{}
195+
duplicateCheck := events.DuplicateCheck{}
196+
resultMap[duplicateCheckKey] = &duplicateCheck
192197
windowHours := peer.CustomData.DuplicateCheckWindowHours
198+
duplicateCheck.WindowHours = windowHours
193199
if windowHours == nil || *windowHours <= 0 {
194-
return nil
200+
duplicateCheck.Enabled = false
201+
return resultMap, nil
195202
}
203+
duplicateCheck.Enabled = true
196204

197205
patronId := ""
198206
if request.PatronInfo != nil {
199207
patronId = request.PatronInfo.PatronId
200208
}
201209

202210
if patronId == "" {
203-
return nil
211+
return resultMap, nil
204212
}
205213

206214
lookupParams := catalog.LookupParamsFromBibliographicInfo(request.BibliographicInfo, request.ServiceInfo)
207-
215+
duplicateCheck.LookupParams = &lookupParams
208216
if lookupParams.ServiceType == "" {
209-
return nil
217+
return resultMap, nil
210218
}
211219

212220
cqlList, _, err := lookupQueryBuilder.Build(lookupParams)
213221
if err != nil {
214222
ctx.Logger().Warn("failed build lookup query", "error", err)
215-
return nil
223+
return resultMap, nil
216224
}
217225

218226
lookupCql := strings.Join(cqlList, " or ")
219227
qb, err := cqlbuilder.NewQueryFromString("(" + lookupCql + ")")
220228
if err != nil {
221229
ctx.Logger().Warn("failed to build duplicate check query", "error", err)
222-
return nil
230+
return resultMap, err
223231
}
224232
formattedTime := time.Now().Add(-time.Duration(*windowHours) * time.Hour).Format(queryTimeFormat)
233+
duplicateCheck.CutoffTime = &formattedTime
225234
query, err := qb.And().Search("requester_symbol").Term(requesterSymbol).
226235
And().Search("patron_id").Term(patronId).
227236
And().Search("timestamp").Rel(">=").Term(formattedTime).
228237
And().Search("service_type").Term(lookupParams.ServiceType).
229238
Build()
230239
if err != nil {
231240
ctx.Logger().Warn("failed to build duplicate check query", "error", err)
232-
return nil
241+
return resultMap, err
233242
}
234243
cql := query.String()
235244
trans, _, err := repo.ListIllTransactions(ctx, ill_db.ListIllTransactionsParams{Limit: 1, Offset: 0}, &cql, []string{requesterSymbol})
236245
if err != nil {
237246
ctx.Logger().Warn("failed to check for duplicate requests, proceeding", "error", err)
238-
return nil // fail open
247+
return resultMap, nil // fail open
239248
}
240249
if len(trans) == 0 {
241-
return nil
250+
return resultMap, nil
242251
}
243-
return ErrDuplicateRequest
252+
duplicateCheck.Duplicate = true
253+
duplicateCheck.MatchedTransactionId = &trans[0].ID
254+
return resultMap, ErrDuplicateRequest
244255
}
245256

246257
func handleRetryRequest(ctx common.ExtendedContext, request *iso18626.Request, repo ill_db.IllRepo) (string, bool, error) {
@@ -310,22 +321,23 @@ func handleRetryRequest(ctx common.ExtendedContext, request *iso18626.Request, r
310321
return id, retryLookupChanged, err
311322
}
312323

313-
func (h *Iso18626Handler) HandleRequest(ctx common.ExtendedContext, illMessage *iso18626.ISO18626Message, w http.ResponseWriter) {
314-
handleRequest(ctx, illMessage, w, h.illRepo, h.eventBus, h.dirAdapter)
324+
func (h *Iso18626Handler) HandleRequest(ctx common.ExtendedContext, illMessage *iso18626.ISO18626Message, w http.ResponseWriter) map[string]any {
325+
return handleRequest(ctx, illMessage, w, h.illRepo, h.eventBus, h.dirAdapter)
315326
}
316327

317-
func handleRequest(ctx common.ExtendedContext, illMessage *iso18626.ISO18626Message, w http.ResponseWriter, repo ill_db.IllRepo, eventBus events.EventBus, dirAdapter adapter.DirectoryLookupAdapter) {
328+
func handleRequest(ctx common.ExtendedContext, illMessage *iso18626.ISO18626Message, w http.ResponseWriter, repo ill_db.IllRepo, eventBus events.EventBus, dirAdapter adapter.DirectoryLookupAdapter) map[string]any {
318329
request := illMessage.Request
330+
resultMap := map[string]any{}
319331
if request.Header.RequestingAgencyRequestId == "" {
320332
handleRequestError(ctx, w, request, iso18626.TypeErrorTypeUnrecognisedDataValue, ReqIdIsEmpty)
321-
return
333+
return resultMap
322334
}
323335

324336
requesterSymbol := createPgText(request.Header.RequestingAgencyId.AgencyIdType.Text + ":" + request.Header.RequestingAgencyId.AgencyIdValue)
325337
peers, _, _ := repo.GetCachedPeersBySymbols(ctx, []string{requesterSymbol.String}, dirAdapter)
326338
if len(peers) != 1 {
327339
handleRequestError(ctx, w, request, iso18626.TypeErrorTypeUnrecognisedDataValue, ReqAgencyNotFound)
328-
return
340+
return resultMap
329341
}
330342

331343
var err error
@@ -340,10 +352,10 @@ func handleRequest(ctx common.ExtendedContext, illMessage *iso18626.ISO18626Mess
340352
case iso18626.TypeRequestTypeRetry:
341353
id, mustLocate, err = handleRetryRequest(ctx, request, repo)
342354
case iso18626.TypeRequestTypeNew:
343-
id, err = handleNewRequest(ctx, request, repo, requesterSymbol, peers)
355+
id, resultMap, err = handleNewRequest(ctx, request, repo, requesterSymbol, peers)
344356
default:
345357
handleRequestError(ctx, w, request, iso18626.TypeErrorTypeUnrecognisedDataValue, UnsupportedRequestType)
346-
return
358+
return resultMap
347359
}
348360
if err != nil {
349361
var pgErr *pgconn.PgError
@@ -357,7 +369,7 @@ func handleRequest(ctx common.ExtendedContext, illMessage *iso18626.ISO18626Mess
357369
ctx.Logger().Error(InternalFailedToSaveTx, "error", err)
358370
http.Error(w, PublicFailedToProcessReqMsg, http.StatusInternalServerError)
359371
}
360-
return
372+
return resultMap
361373
}
362374
afterShim := shim.GetShim(peers[0].Vendor).ApplyToIncomingRequest(illMessage, &peers[0], nil)
363375
var resmsg = createRequestResponse(request, iso18626.TypeMessageStatusOK, nil, "")
@@ -374,9 +386,10 @@ func handleRequest(ctx common.ExtendedContext, illMessage *iso18626.ISO18626Mess
374386
event := events.EventNameRequestReceived
375387
if _, err = createNotice(ctx, eventBus, id, event, eventData, events.EventStatusSuccess); err != nil {
376388
http.Error(w, PublicFailedToProcessReqMsg, http.StatusInternalServerError)
377-
return
389+
return resultMap
378390
}
379391
writeResponse(ctx, resmsg, w)
392+
return resultMap
380393
}
381394

382395
func writeResponse(ctx common.ExtendedContext, resmsg *iso18626.ISO18626Message, w http.ResponseWriter) {

broker/handler/iso18626-handler_test.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,9 @@ func TestCheckDuplicateRequest(t *testing.T) {
454454
duplicate: tt.duplicate,
455455
err: tt.repoErr,
456456
}
457-
err := checkDuplicateRequest(appCtx, tt.request, mockRepo, "ISIL:REQ1", tt.peer)
457+
result, err := checkDuplicateRequest(appCtx, tt.request, mockRepo, "ISIL:REQ1", tt.peer)
458+
_, hasKey := result[duplicateCheckKey]
459+
assert.True(t, hasKey)
458460
assert.Equal(t, tt.wantErr, err)
459461
assert.Equal(t, tt.wantRepoCalled, mockRepo.called)
460462
if tt.wantRepoCalled {
@@ -465,6 +467,19 @@ func TestCheckDuplicateRequest(t *testing.T) {
465467
assert.True(t, tt.wantTitle == "" || strings.Contains(mockRepo.cql, tt.wantTitle))
466468
assert.True(t, tt.wantSvcType == "" || strings.Contains(mockRepo.cql, tt.wantSvcType))
467469
}
470+
if tt.duplicate {
471+
dupCheck, ok := result[duplicateCheckKey].(*events.DuplicateCheck)
472+
assert.True(t, ok)
473+
assert.True(t, dupCheck.Enabled)
474+
assert.Equal(t, tt.wantIdentifier, dupCheck.LookupParams.Identifier)
475+
assert.Equal(t, tt.wantIsbn, dupCheck.LookupParams.Isbn)
476+
assert.Equal(t, tt.wantIssn, dupCheck.LookupParams.Issn)
477+
assert.Equal(t, tt.wantTitle, dupCheck.LookupParams.Title)
478+
assert.Equal(t, tt.wantSvcType, dupCheck.LookupParams.ServiceType)
479+
assert.Equal(t, window1, *dupCheck.WindowHours)
480+
assert.NotNil(t, dupCheck.CutoffTime)
481+
assert.Equal(t, "duplicate-id", *dupCheck.MatchedTransactionId)
482+
}
468483
})
469484
}
470485
}

broker/patron_request/service/action_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3011,7 +3011,7 @@ type MockIso18626Handler struct {
30113011
lastSupplyingAgencyMessage *iso18626.SupplyingAgencyMessage
30123012
}
30133013

3014-
func (h *MockIso18626Handler) HandleRequest(ctx common.ExtendedContext, illMessage *iso18626.ISO18626Message, w http.ResponseWriter) {
3014+
func (h *MockIso18626Handler) HandleRequest(ctx common.ExtendedContext, illMessage *iso18626.ISO18626Message, w http.ResponseWriter) map[string]any {
30153015
status := iso18626.TypeMessageStatusOK
30163016
if illMessage.Request.Header.RequestingAgencyRequestId == "error" {
30173017
status = iso18626.TypeMessageStatusERROR
@@ -3032,11 +3032,12 @@ func (h *MockIso18626Handler) HandleRequest(ctx common.ExtendedContext, illMessa
30323032
output, err := xml.MarshalIndent(resmsg, " ", " ")
30333033
if err != nil {
30343034
ctx.Logger().Error("failed to produce response", "error", err, "body", string(output))
3035-
return
3035+
return nil
30363036
}
30373037
w.Header().Set("Content-Type", "application/xml")
30383038
w.WriteHeader(http.StatusOK)
30393039
w.Write(output)
3040+
return nil
30403041
}
30413042

30423043
func (h *MockIso18626Handler) HandleRequestingAgencyMessage(ctx common.ExtendedContext, illMessage *iso18626.ISO18626Message, w http.ResponseWriter) {

broker/patron_request/service/message_sender.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,13 @@ func (ms *PatronRequestMessageSender) sendBorrowingRequest(ctx common.ExtendedCo
156156
var illMessage = iso18626.NewISO18626Message()
157157
illMessage.Request = &illRequest
158158
w := NewResponseCaptureWriter()
159-
ms.iso18626Handler.HandleRequest(ctx, illMessage, w)
159+
resultMap := ms.iso18626Handler.HandleRequest(ctx, illMessage, w)
160+
if len(resultMap) > 0 {
161+
result.CustomData = make(map[string]any, len(resultMap))
162+
for key, value := range resultMap {
163+
result.CustomData[key] = value
164+
}
165+
}
160166
result.OutgoingMessage = illMessage
161167
result.IncomingMessage = w.IllMessage
162168
if w.StatusCode != http.StatusOK || w.IllMessage == nil || w.IllMessage.RequestConfirmation == nil ||

0 commit comments

Comments
 (0)