diff --git a/src/main/java/org/folio/bulkops/processor/MarcInstanceDataProcessor.java b/src/main/java/org/folio/bulkops/processor/MarcInstanceDataProcessor.java index e7fa02aa8..5b342044e 100644 --- a/src/main/java/org/folio/bulkops/processor/MarcInstanceDataProcessor.java +++ b/src/main/java/org/folio/bulkops/processor/MarcInstanceDataProcessor.java @@ -1,8 +1,10 @@ package org.folio.bulkops.processor; +import static java.lang.Character.isDigit; import static org.folio.bulkops.domain.dto.IdentifierType.HRID; import static org.folio.bulkops.domain.dto.MarcDataType.SUBFIELD; import static org.folio.bulkops.domain.dto.MarcDataType.VALUE; +import static org.folio.bulkops.domain.dto.UpdateActionType.ADD_TO_EXISTING; import static org.folio.bulkops.domain.dto.UpdateActionType.FIND; import static org.folio.bulkops.util.Constants.FIELD_999; import static org.folio.bulkops.util.Constants.INDICATOR_F; @@ -10,6 +12,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.ObjectUtils; import org.folio.bulkops.domain.dto.BulkOperationMarcRule; import org.folio.bulkops.domain.dto.BulkOperationMarcRuleCollection; import org.folio.bulkops.domain.dto.MarcActionDataInner; @@ -20,9 +23,11 @@ import org.marc4j.marc.DataField; import org.marc4j.marc.Record; import org.marc4j.marc.Subfield; +import org.marc4j.marc.impl.DataFieldImpl; import org.marc4j.marc.impl.SubfieldImpl; import org.springframework.stereotype.Component; +import java.util.Comparator; import java.util.List; @Component @@ -53,6 +58,8 @@ private void applyRuleToRecord(BulkOperationMarcRule rule, Record marcRecord) th case REPLACE_WITH -> processFindAndReplace(rule, marcRecord); default -> throw new BulkOperationException(String.format("Action FIND + %s is not supported yet.", actions.get(1).getName())); } + } else if (ADD_TO_EXISTING.equals(actions.get(0).getName())) { + processAddToExisting(rule, marcRecord); } else { throw new BulkOperationException(String.format("Action %s is not supported yet.", actions.get(0).getName())); } @@ -67,6 +74,7 @@ private void processFindAndAppend(BulkOperationMarcRule rule, Record marcRecord) dataField.getSubfields().forEach(subfield -> { if (subfieldCode == subfield.getCode() && findValue.equals(subfield.getData())) { dataField.addSubfield(new SubfieldImpl(appendSubfieldCode, appendValue)); + dataField.getSubfields().sort(subfieldComparator); } })); } @@ -92,6 +100,29 @@ private List findFields(BulkOperationMarcRule rule, Record marcRecord .toList(); } + private void processAddToExisting(BulkOperationMarcRule rule, Record marcRecord) throws BulkOperationException { + var tag = rule.getTag(); + char ind1 = fetchIndicatorValue(rule.getInd1()); + char ind2 = fetchIndicatorValue(rule.getInd2()); + var newField = new DataFieldImpl(tag, ind1, ind2); + var subfieldCode = rule.getSubfield().charAt(0); + var value = fetchActionDataValue(VALUE, rule.getActions().get(0).getData()); + newField.addSubfield(new SubfieldImpl(subfieldCode, value)); + if (ObjectUtils.isNotEmpty(rule.getSubfields())) { + for (var subfieldAction : rule.getSubfields()) { + var action = subfieldAction.getActions().get(0); + if (ADD_TO_EXISTING.equals(action.getName())) { + subfieldCode = subfieldAction.getSubfield().charAt(0); + value = fetchActionDataValue(VALUE, action.getData()); + newField.addSubfield(new SubfieldImpl(subfieldCode, value)); + } + } + newField.getSubfields().sort(subfieldComparator); + } + marcRecord.addVariableField(newField); + marcRecord.getDataFields().sort(Comparator.comparing(DataField::toString)); + } + private char fetchIndicatorValue(String s) { return "\\".equals(s) ? SPACE_CHAR : s.charAt(0); } @@ -112,4 +143,12 @@ private String fetchActionDataValue(MarcDataType dataType, List new BulkOperationException(String.format("Action data %s is absent.", dataType))); } + + private final Comparator subfieldComparator = (sf1, sf2) -> { + if (isDigit(sf1.getCode()) ^ isDigit(sf2.getCode())) { + return isDigit(sf1.getCode()) ? 1 : -1; + } else { + return sf1.toString().compareTo(sf2.toString()); + } + }; } diff --git a/src/main/resources/swagger.api/schemas/marc_subfield_action.json b/src/main/resources/swagger.api/schemas/marc_subfield_action.json index 169a8eb76..a965b58e6 100644 --- a/src/main/resources/swagger.api/schemas/marc_subfield_action.json +++ b/src/main/resources/swagger.api/schemas/marc_subfield_action.json @@ -13,7 +13,8 @@ "type": "array", "items": { "$ref": "marc_action.json#/MarcAction" - } + }, + "minItems": 1 } }, "required": [ diff --git a/src/test/java/org/folio/bulkops/processor/MarcInstanceDataProcessorTest.java b/src/test/java/org/folio/bulkops/processor/MarcInstanceDataProcessorTest.java index 9d3466369..60cca2aaf 100644 --- a/src/test/java/org/folio/bulkops/processor/MarcInstanceDataProcessorTest.java +++ b/src/test/java/org/folio/bulkops/processor/MarcInstanceDataProcessorTest.java @@ -2,6 +2,8 @@ import static java.util.Objects.nonNull; import static org.assertj.core.api.Assertions.assertThat; +import static org.folio.bulkops.domain.dto.UpdateActionType.ADDITIONAL_SUBFIELD; +import static org.folio.bulkops.domain.dto.UpdateActionType.ADD_TO_EXISTING; import static org.folio.bulkops.domain.dto.UpdateActionType.FIND; import static org.mockito.Mockito.verify; @@ -12,6 +14,7 @@ import org.folio.bulkops.domain.dto.MarcAction; import org.folio.bulkops.domain.dto.MarcActionDataInner; import org.folio.bulkops.domain.dto.MarcDataType; +import org.folio.bulkops.domain.dto.MarcSubfieldAction; import org.folio.bulkops.domain.dto.UpdateActionType; import org.folio.bulkops.domain.entity.BulkOperation; import org.folio.bulkops.service.ErrorService; @@ -45,19 +48,19 @@ void shouldApplyFindAndAppendRule() { .build(); var marcRecord = new RecordImpl(); var dataField = new DataFieldImpl("500", '1', ' '); - dataField.addSubfield(new SubfieldImpl('a', "text a")); + dataField.addSubfield(new SubfieldImpl('b', "text b")); marcRecord.addVariableField(dataField); dataField = new DataFieldImpl("500", '1', ' '); - dataField.addSubfield(new SubfieldImpl('a', "Text a")); + dataField.addSubfield(new SubfieldImpl('b', "Text b")); marcRecord.addVariableField(dataField); dataField = new DataFieldImpl("500", ' ', '1'); - dataField.addSubfield(new SubfieldImpl('a', "Text a")); + dataField.addSubfield(new SubfieldImpl('b', "Text b")); marcRecord.addVariableField(dataField); dataField = new DataFieldImpl("500", '1', ' '); - dataField.addSubfield(new SubfieldImpl('b', "Text a")); + dataField.addSubfield(new SubfieldImpl('a', "Text b")); marcRecord.addVariableField(dataField); dataField = new DataFieldImpl("510", '1', ' '); - dataField.addSubfield(new SubfieldImpl('a', "Text a")); + dataField.addSubfield(new SubfieldImpl('b', "Text b")); marcRecord.addVariableField(dataField); var findAndAppendRule = new BulkOperationMarcRule() .id(UUID.randomUUID()) @@ -65,22 +68,22 @@ void shouldApplyFindAndAppendRule() { .tag("500") .ind1("1") .ind2("\\") - .subfield("a") + .subfield("b") .actions(List.of( new MarcAction() .name(FIND) .data(Collections.singletonList(new MarcActionDataInner() .key(MarcDataType.VALUE) - .value("text a"))), + .value("text b"))), new MarcAction() .name(UpdateActionType.APPEND) .data(List.of( new MarcActionDataInner() .key(MarcDataType.VALUE) - .value("text b"), + .value("text a"), new MarcActionDataInner() .key(MarcDataType.SUBFIELD) - .value("b"))))); + .value("a"))))); var rules = new BulkOperationMarcRuleCollection() .bulkOperationMarcRules(Collections.singletonList(findAndAppendRule)) .totalRecords(1); @@ -90,20 +93,11 @@ void shouldApplyFindAndAppendRule() { var dataFields = marcRecord.getDataFields(); assertThat(dataFields).hasSize(5); - var subfields = dataFields.get(0).getSubfields(); - assertThat(subfields).hasSize(2); - assertThat(subfields.get(0).getCode()).isEqualTo('a'); - assertThat(subfields.get(0).getData()).isEqualTo("text a"); - assertThat(subfields.get(1).getCode()).isEqualTo('b'); - assertThat(subfields.get(1).getData()).isEqualTo("text b"); - subfields = dataFields.get(1).getSubfields(); - assertThat(subfields).hasSize(1); - subfields = dataFields.get(2).getSubfields(); - assertThat(subfields).hasSize(1); - subfields = dataFields.get(3).getSubfields(); - assertThat(subfields).hasSize(1); - subfields = dataFields.get(4).getSubfields(); - assertThat(subfields).hasSize(1); + assertThat(dataFields.get(0)).hasToString("500 1 $atext a$btext b"); + assertThat(dataFields.get(1).getSubfields()).hasSize(1); + assertThat(dataFields.get(2).getSubfields()).hasSize(1); + assertThat(dataFields.get(3).getSubfields()).hasSize(1); + assertThat(dataFields.get(4).getSubfields()).hasSize(1); } @Test @@ -278,4 +272,69 @@ void shouldNotApplyUnsupportedAction(UpdateActionType updateActionType1, UpdateA var errorMessage = String.format(errorMessageTemplate, errorMessageArg); verify(errorService).saveError(bulkOperationId, hrid, errorMessage); } + + @Test + void shouldApplyAddToExistingRule() { + var bulkOperationId = UUID.randomUUID(); + var operation = BulkOperation.builder() + .id(bulkOperationId) + .identifierType(IdentifierType.ID) + .build(); + var marcRecord = new RecordImpl(); + var dataField = new DataFieldImpl("500", '1', '1'); + dataField.addSubfield(new SubfieldImpl('a', "text a")); + marcRecord.addVariableField(dataField); + dataField = new DataFieldImpl("550", '1', '1'); + dataField.addSubfield(new SubfieldImpl('a', "text a")); + marcRecord.addVariableField(dataField); + var findAndAppendRule = new BulkOperationMarcRule() + .id(UUID.randomUUID()) + .bulkOperationId(bulkOperationId) + .tag("510") + .ind1("1") + .ind2("1") + .subfield("b") + .actions(List.of( + new MarcAction() + .name(ADD_TO_EXISTING) + .data(Collections.singletonList(new MarcActionDataInner() + .key(MarcDataType.VALUE) + .value("text b"))), + new MarcAction() + .name(ADDITIONAL_SUBFIELD) + .data(Collections.emptyList()))) + .subfields(List.of(new MarcSubfieldAction() + .subfield("1") + .actions(List.of(new MarcAction() + .name(ADD_TO_EXISTING) + .data(Collections.singletonList(new MarcActionDataInner() + .key(MarcDataType.VALUE) + .value("text 1"))), + new MarcAction() + .name(ADDITIONAL_SUBFIELD) + .data(Collections.emptyList()))), + new MarcSubfieldAction() + .subfield("a") + .actions(List.of(new MarcAction() + .name(ADD_TO_EXISTING) + .data(Collections.singletonList(new MarcActionDataInner() + .key(MarcDataType.VALUE) + .value("text a"))), + new MarcAction() + .name(ADDITIONAL_SUBFIELD) + .data(Collections.emptyList()))))); + var rules = new BulkOperationMarcRuleCollection() + .bulkOperationMarcRules(Collections.singletonList(findAndAppendRule)) + .totalRecords(1); + + processor.update(operation, marcRecord, rules); + + var dataFields = marcRecord.getDataFields(); + assertThat(dataFields).hasSize(3); + + assertThat(dataFields.get(0).getTag()).isEqualTo("500"); + assertThat(dataFields.get(1).getTag()).isEqualTo("510"); + assertThat(dataFields.get(1)).hasToString("510 11$atext a$btext b$1text 1"); + assertThat(dataFields.get(2).getTag()).isEqualTo("550"); + } }