Skip to content

Commit 9dab4a3

Browse files
CROSSLINK-213 Item unifying and processing
1 parent ce14e3f commit 9dab4a3

5 files changed

Lines changed: 352 additions & 76 deletions

File tree

broker/common/common.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@ import (
44
"fmt"
55
"reflect"
66
"strings"
7+
8+
"github.com/indexdata/crosslink/iso18626"
79
)
810

11+
const MULTIPLE_ITEMS = "#MultipleItems#"
12+
const MULTIPLE_ITEMS_END = "#MultipleItemsEnd#"
13+
914
func StructToMap(obj interface{}) (map[string]interface{}, error) {
1015
result := make(map[string]interface{})
1116
val := reflect.ValueOf(obj)
@@ -37,3 +42,58 @@ func StructToMap(obj interface{}) (map[string]interface{}, error) {
3742

3843
return result, nil
3944
}
45+
46+
func SamHasItems(sam iso18626.SupplyingAgencyMessage) bool {
47+
return strings.Contains(sam.MessageInfo.Note, MULTIPLE_ITEMS) && strings.Contains(sam.MessageInfo.Note, MULTIPLE_ITEMS_END)
48+
}
49+
50+
func GetItemParams(note string) ([][]string, int, int) {
51+
startIdx := strings.Index(note, MULTIPLE_ITEMS)
52+
endIdx := strings.Index(note, MULTIPLE_ITEMS_END)
53+
54+
content := note[startIdx+len(MULTIPLE_ITEMS) : endIdx]
55+
content = strings.TrimSpace(content)
56+
var result [][]string
57+
for _, f := range strings.Split(content, "\n") {
58+
result = append(result, UnpackItemsNote(f))
59+
}
60+
return result, startIdx, endIdx
61+
}
62+
63+
func PackItemsNote(fields []string) string {
64+
escaped := make([]string, len(fields))
65+
for i, f := range fields {
66+
// Escape backslashes first, then the separator
67+
temp := strings.ReplaceAll(f, "\\", "\\\\")
68+
escaped[i] = strings.ReplaceAll(temp, "|", "\\|")
69+
}
70+
return strings.Join(escaped, "|")
71+
}
72+
73+
func UnpackItemsNote(input string) []string {
74+
var result []string
75+
var current strings.Builder
76+
escaped := false
77+
78+
for i := 0; i < len(input); i++ {
79+
char := input[i]
80+
81+
if escaped {
82+
current.WriteByte(char)
83+
escaped = false
84+
continue
85+
}
86+
87+
switch char {
88+
case '\\':
89+
escaped = true
90+
case '|':
91+
result = append(result, current.String())
92+
current.Reset()
93+
default:
94+
current.WriteByte(char)
95+
}
96+
}
97+
result = append(result, current.String())
98+
return result
99+
}

broker/patron_request/service/message-handler.go

Lines changed: 20 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"encoding/json"
55
"errors"
66
"fmt"
7-
"regexp"
7+
"strconv"
88
"strings"
99
"time"
1010

@@ -312,58 +312,38 @@ func (m *PatronRequestMessageHandler) updatePatronRequestAndCreateRamResponse(ct
312312
}
313313

314314
func (m *PatronRequestMessageHandler) saveItems(ctx common.ExtendedContext, pr pr_db.PatronRequest, sam iso18626.SupplyingAgencyMessage) error {
315-
if sam.DeliveryInfo != nil && sam.DeliveryInfo.ItemId != "" &&
316-
(sam.DeliveryInfo.SentVia == nil || sam.DeliveryInfo.SentVia.Text != "URL") {
317-
if strings.Contains(sam.DeliveryInfo.ItemId, "multivol:") {
318-
list := strings.Split(sam.DeliveryInfo.ItemId, ",multivol:")
319-
for _, item := range list {
320-
item = strings.Replace(item, "multivol:", "", 1)
321-
err := m.saveItem(ctx, pr.ID, item)
322-
if err != nil {
323-
return err
324-
}
315+
if common.SamHasItems(sam) {
316+
result, _, _ := common.GetItemParams(sam.MessageInfo.Note)
317+
for _, item := range result {
318+
var loopErr error
319+
if len(item) == 1 {
320+
loopErr = m.saveItem(ctx, pr.ID, item[0], item[0], nil)
321+
} else if len(item) == 3 {
322+
loopErr = m.saveItem(ctx, pr.ID, item[2], item[0], &item[1])
323+
} else {
324+
loopErr = errors.New("incorrect item param count: " + strconv.Itoa(len(item)))
325+
}
326+
if loopErr != nil {
327+
return loopErr
325328
}
326-
} else {
327-
return m.saveItem(ctx, pr.ID, sam.DeliveryInfo.ItemId)
328329
}
329330
}
330331
return nil
331332
}
332333

333-
func (m *PatronRequestMessageHandler) saveItem(ctx common.ExtendedContext, prId string, item string) error {
334-
id, name, callNumber := getItemValues(item)
334+
func (m *PatronRequestMessageHandler) saveItem(ctx common.ExtendedContext, prId string, id string, name string, callNumber *string) error {
335+
dbCallNumber := pgtype.Text{Valid: false, String: ""}
336+
if callNumber != nil {
337+
dbCallNumber = pgtype.Text{Valid: true, String: *callNumber}
338+
}
335339
_, err := m.prRepo.SaveItem(ctx, pr_db.SaveItemParams{
336340
ID: uuid.NewString(),
337341
CreatedAt: pgtype.Timestamp{Valid: true, Time: time.Now()},
338342
PrID: prId,
339343
ItemID: getDbText(id),
340344
Title: getDbText(name),
341-
CallNumber: callNumber,
345+
CallNumber: dbCallNumber,
342346
Barcode: id, //TODO barcode generation. How to do that?
343347
})
344348
return err
345349
}
346-
347-
func getItemValues(itemId string) (string, string, pgtype.Text) {
348-
re := regexp.MustCompile(`(.*),(.*),(.*)`)
349-
match := re.FindStringSubmatch(itemId)
350-
id := itemId
351-
name := itemId
352-
var iidCallNumber *string
353-
callNumber := pgtype.Text{
354-
Valid: false,
355-
String: "",
356-
}
357-
if len(match) > 0 {
358-
name = match[1]
359-
iidCallNumber = &match[2]
360-
id = match[3]
361-
}
362-
if iidCallNumber != nil {
363-
callNumber = pgtype.Text{
364-
Valid: true,
365-
String: *iidCallNumber,
366-
}
367-
}
368-
return id, name, callNumber
369-
}

broker/patron_request/service/message-handler_test.go

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,8 @@ func TestHandleSupplyingAgencyMessageLoaned(t *testing.T) {
216216
RequestingAgencyRequestId: patronRequestId,
217217
},
218218
StatusInfo: iso18626.StatusInfo{Status: iso18626.TypeStatusLoaned},
219-
DeliveryInfo: &iso18626.DeliveryInfo{
220-
ItemId: "1,2,3",
219+
MessageInfo: iso18626.MessageInfo{
220+
Note: "#MultipleItems#\n1|2|3\n#MultipleItemsEnd#",
221221
},
222222
}, pr_db.PatronRequest{})
223223
assert.Equal(t, events.EventStatusSuccess, status)
@@ -531,43 +531,19 @@ func TestHandleRequestMessageSaveError(t *testing.T) {
531531
assert.Equal(t, "db error", err.Error())
532532
}
533533

534-
func TestGetItemValues(t *testing.T) {
535-
id, name, callNumber := getItemValues("1,2,3")
536-
assert.Equal(t, "1", name)
537-
assert.Equal(t, "2", callNumber.String)
538-
assert.Equal(t, "3", id)
539-
540-
id, name, callNumber = getItemValues("1")
541-
assert.Equal(t, "1", name)
542-
assert.Equal(t, "", callNumber.String)
543-
assert.False(t, callNumber.Valid)
544-
assert.Equal(t, "1", id)
545-
546-
id, name, callNumber = getItemValues("1,2,3,4,5")
547-
assert.Equal(t, "1,2,3", name)
548-
assert.Equal(t, "4", callNumber.String)
549-
assert.Equal(t, "5", id)
550-
}
551-
552534
func TestSaveItems(t *testing.T) {
553535
mockPrRepo := new(MockPrRepo)
554536
mockEventBus := new(MockEventBus)
555537
handler := CreatePatronRequestMessageHandler(mockPrRepo, *new(events.EventRepo), *new(ill_db.IllRepo), mockEventBus)
556538

557-
// No delivery
539+
// Empty message
558540
sam := iso18626.SupplyingAgencyMessage{}
559541
err := handler.saveItems(appCtx, pr_db.PatronRequest{ID: "pr1"}, sam)
560542
assert.NoError(t, err)
561543
assert.Equal(t, 0, len(mockPrRepo.savedItems))
562544

563-
// No ItemId
564-
sam.DeliveryInfo = &iso18626.DeliveryInfo{}
565-
err = handler.saveItems(appCtx, pr_db.PatronRequest{ID: "pr1"}, sam)
566-
assert.NoError(t, err)
567-
assert.Equal(t, 0, len(mockPrRepo.savedItems))
568-
569545
// One Item
570-
sam.DeliveryInfo = &iso18626.DeliveryInfo{ItemId: "1,2,3"}
546+
sam.MessageInfo.Note = "#MultipleItems#\n1|2|3\n#MultipleItemsEnd#"
571547
err = handler.saveItems(appCtx, pr_db.PatronRequest{ID: "pr1"}, sam)
572548
assert.NoError(t, err)
573549
assert.Equal(t, 1, len(mockPrRepo.savedItems))
@@ -578,7 +554,7 @@ func TestSaveItems(t *testing.T) {
578554
assert.Equal(t, "pr1", mockPrRepo.savedItems[0].PrID)
579555

580556
// Two Items
581-
sam.DeliveryInfo = &iso18626.DeliveryInfo{ItemId: "multivol:1,2,3,multivol:4,5,6,7"}
557+
sam.MessageInfo.Note = "#MultipleItems#\n1|2|3\n4,5|6|7\n#MultipleItemsEnd#"
582558
mockPrRepo.savedItems = nil
583559
err = handler.saveItems(appCtx, pr_db.PatronRequest{ID: "pr1"}, sam)
584560
assert.NoError(t, err)
@@ -593,11 +569,4 @@ func TestSaveItems(t *testing.T) {
593569
assert.Equal(t, "7", mockPrRepo.savedItems[1].ItemID.String)
594570
assert.Equal(t, "7", mockPrRepo.savedItems[1].Barcode)
595571
assert.Equal(t, "pr1", mockPrRepo.savedItems[1].PrID)
596-
597-
// Don't create items for URL delivery
598-
sam.DeliveryInfo.SentVia = &iso18626.TypeSchemeValuePair{Text: "URL"}
599-
mockPrRepo.savedItems = nil
600-
err = handler.saveItems(appCtx, pr_db.PatronRequest{ID: "pr1"}, sam)
601-
assert.NoError(t, err)
602-
assert.Equal(t, 0, len(mockPrRepo.savedItems))
603572
}

broker/shim/shim.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"regexp"
66
"strings"
77

8+
"github.com/indexdata/crosslink/broker/common"
89
"github.com/indexdata/crosslink/broker/ill_db"
910
"github.com/indexdata/crosslink/directory"
1011
"github.com/indexdata/crosslink/iso18626"
@@ -88,6 +89,11 @@ func (i *Iso18626AlmaShim) ApplyToIncomingRequest(message *iso18626.ISO18626Mess
8889
copyMessage.RequestingAgencyMessage.Header.SupplyingAgencyId.AgencyIdValue = symbol[1]
8990
}
9091
}
92+
if message.SupplyingAgencyMessage != nil {
93+
copySam := *message.SupplyingAgencyMessage
94+
copyMessage.SupplyingAgencyMessage = &copySam
95+
i.unifyItem(copyMessage.SupplyingAgencyMessage)
96+
}
9197
return &copyMessage
9298
}
9399

@@ -107,6 +113,7 @@ func (i *Iso18626AlmaShim) ApplyToOutgoingRequest(message *iso18626.ISO18626Mess
107113
i.appendReturnAddressToSuppMsgNote(suppMsg)
108114
}
109115
i.appendUnfilledStatusAndReasonUnfilled(suppMsg)
116+
i.setItemId(suppMsg)
110117
}
111118
if message.Request != nil {
112119
request := message.Request
@@ -198,6 +205,20 @@ func (i *Iso18626AlmaShim) appendUnfilledStatusAndReasonUnfilled(suppMsg *iso186
198205
}
199206
}
200207

208+
func (i *Iso18626AlmaShim) setItemId(sam *iso18626.SupplyingAgencyMessage) {
209+
if common.SamHasItems(*sam) {
210+
result, startIdx, endIdx := common.GetItemParams(sam.MessageInfo.Note)
211+
var items []string
212+
for _, item := range result {
213+
items = append(items, item[0])
214+
}
215+
sam.DeliveryInfo.ItemId = strings.Join(items, ",")
216+
217+
sam.MessageInfo.Note = sam.MessageInfo.Note[0:startIdx-1] + // -1 because we remove new line symbol
218+
sam.MessageInfo.Note[endIdx+len(common.MULTIPLE_ITEMS_END):]
219+
}
220+
}
221+
201222
func (i *Iso18626AlmaShim) transferOfferedCostsToDeliveryCosts(suppMsg *iso18626.SupplyingAgencyMessage) {
202223
//alma doesn't care about the delivery costs unless the status is Loaned or CopyCompleted
203224
if suppMsg.StatusInfo.Status != iso18626.TypeStatusLoaned && suppMsg.StatusInfo.Status != iso18626.TypeStatusCopyCompleted {
@@ -516,13 +537,34 @@ func (i *Iso18626AlmaShim) fixRequesterConditionNote(requestingAgencyMessage *is
516537
}
517538
}
518539

540+
func (i *Iso18626AlmaShim) unifyItem(sam *iso18626.SupplyingAgencyMessage) {
541+
if sam.DeliveryInfo != nil && sam.DeliveryInfo.ItemId != "" {
542+
var sb strings.Builder
543+
//retain original note
544+
if sam.MessageInfo.Note != "" {
545+
sb.WriteString(sam.MessageInfo.Note)
546+
sb.WriteString("\n")
547+
}
548+
sb.WriteString(common.MULTIPLE_ITEMS)
549+
sb.WriteString("\n")
550+
list := strings.Split(sam.DeliveryInfo.ItemId, ",")
551+
for _, item := range list {
552+
sb.WriteString(common.PackItemsNote([]string{item}))
553+
sb.WriteString("\n")
554+
}
555+
sb.WriteString(common.MULTIPLE_ITEMS_END)
556+
sam.MessageInfo.Note = sb.String()
557+
}
558+
}
559+
519560
type Iso18626ReShareShim struct {
520561
Iso18626DefaultShim
521562
}
522563

523564
func (i *Iso18626ReShareShim) ApplyToOutgoingRequest(message *iso18626.ISO18626Message) ([]byte, error) {
524565
if message.SupplyingAgencyMessage != nil {
525566
i.transferDeliveryCostsToOfferedCosts(message.SupplyingAgencyMessage)
567+
i.setItemId(message.SupplyingAgencyMessage)
526568
}
527569
return xml.Marshal(message)
528570
}
@@ -543,3 +585,72 @@ func (i *Iso18626ReShareShim) transferDeliveryCostsToOfferedCosts(suppMsg *iso18
543585
}
544586
}
545587
}
588+
589+
func (i *Iso18626ReShareShim) ApplyToIncomingRequest(message *iso18626.ISO18626Message, requester *ill_db.Peer, supplier *ill_db.LocatedSupplier) *iso18626.ISO18626Message {
590+
if message == nil {
591+
return message
592+
}
593+
copyMessage := *message
594+
if message.SupplyingAgencyMessage != nil {
595+
copySam := *message.SupplyingAgencyMessage
596+
copyMessage.SupplyingAgencyMessage = &copySam
597+
i.unifyItem(copyMessage.SupplyingAgencyMessage)
598+
}
599+
return &copyMessage
600+
}
601+
602+
func (i *Iso18626ReShareShim) setItemId(sam *iso18626.SupplyingAgencyMessage) {
603+
if common.SamHasItems(*sam) {
604+
result, startIdx, endIdx := common.GetItemParams(sam.MessageInfo.Note)
605+
if len(result) == 1 {
606+
sam.DeliveryInfo.ItemId = strings.Join(result[0], ",")
607+
} else {
608+
var items []string
609+
for _, item := range result {
610+
items = append(items, strings.Join(item, ","))
611+
}
612+
sam.DeliveryInfo.ItemId = "multivol:" + strings.Join(items, ",multivol:")
613+
}
614+
615+
sam.MessageInfo.Note = sam.MessageInfo.Note[0:startIdx-1] + // -1 because we remove new line symbol
616+
sam.MessageInfo.Note[endIdx+len(common.MULTIPLE_ITEMS_END):]
617+
}
618+
}
619+
620+
func (i *Iso18626ReShareShim) unifyItem(sam *iso18626.SupplyingAgencyMessage) {
621+
if sam.DeliveryInfo != nil && sam.DeliveryInfo.ItemId != "" {
622+
var sb strings.Builder
623+
//retain original note
624+
if sam.MessageInfo.Note != "" {
625+
sb.WriteString(sam.MessageInfo.Note)
626+
sb.WriteString("\n")
627+
}
628+
sb.WriteString(common.MULTIPLE_ITEMS)
629+
sb.WriteString("\n")
630+
if strings.Contains(sam.DeliveryInfo.ItemId, "multivol:") {
631+
list := strings.Split(sam.DeliveryInfo.ItemId, ",multivol:")
632+
for _, item := range list {
633+
item = strings.Replace(item, "multivol:", "", 1)
634+
writeItemValues(&sb, item)
635+
sb.WriteString("\n")
636+
}
637+
} else {
638+
writeItemValues(&sb, sam.DeliveryInfo.ItemId)
639+
sb.WriteString("\n")
640+
}
641+
sb.WriteString(common.MULTIPLE_ITEMS_END)
642+
sam.MessageInfo.Note = sb.String()
643+
}
644+
}
645+
646+
func writeItemValues(sb *strings.Builder, itemId string) {
647+
re := regexp.MustCompile(`(.*),(.*),(.*)`)
648+
match := re.FindStringSubmatch(itemId)
649+
var row string
650+
if len(match) > 0 {
651+
row = common.PackItemsNote([]string{match[1], match[2], match[3]})
652+
} else {
653+
row = common.PackItemsNote([]string{itemId})
654+
}
655+
sb.WriteString(row)
656+
}

0 commit comments

Comments
 (0)