Skip to content

Commit

Permalink
MODBULKOPS-242 - Supported Bulk Edit Actions for FOLIO Instance Notes (
Browse files Browse the repository at this point in the history
  • Loading branch information
siarhei-charniak authored May 10, 2024
1 parent 7aedf91 commit 4612d8d
Show file tree
Hide file tree
Showing 21 changed files with 766 additions and 37 deletions.
14 changes: 11 additions & 3 deletions descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@
"inventory-storage.holdings-note-types.item.get",
"inventory-storage.ill-policies.item.get",
"inventory-storage.holdings-sources.item.get",
"inventory-storage.statistical-codes.item.get"
"inventory-storage.statistical-codes.item.get",
"inventory-storage.instance-note-types.collection.get"
]
},
{
Expand All @@ -78,7 +79,8 @@
"inventory-storage.holdings-note-types.item.get",
"inventory-storage.ill-policies.item.get",
"inventory-storage.holdings-sources.item.get",
"inventory-storage.statistical-codes.item.get"
"inventory-storage.statistical-codes.item.get",
"inventory-storage.instance-note-types.item.get"
]
},
{
Expand Down Expand Up @@ -142,7 +144,9 @@
"inventory-storage.nature-of-content-terms.item.get",
"inventory-storage.instance-formats.collection.get",
"inventory-storage.instance-formats.item.get",
"inventory.instances.item.put"
"inventory.instances.item.put",
"inventory-storage.instance-note-types.item.get",
"inventory-storage.instance-note-types.collection.get"
]
},
{
Expand Down Expand Up @@ -445,6 +449,10 @@
{
"id": "entity-types",
"version": "1.0"
},
{
"id": "instance-note-types",
"version": "1.0"
}
],
"launchDescriptor": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.folio.bulkops.client;

import org.folio.bulkops.configs.FeignClientConfiguration;
import org.folio.bulkops.domain.dto.InstanceNoteType;
import org.folio.bulkops.domain.dto.InstanceNoteTypeCollection;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "instance-note-types", configuration = FeignClientConfiguration.class)
public interface InstanceNoteTypesClient {
@GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
InstanceNoteType getNoteTypeById(@PathVariable String id);

@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
InstanceNoteTypeCollection getNoteTypesByQuery(@RequestParam String query, @RequestParam("limit") int limit);

@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
InstanceNoteTypeCollection getInstanceNoteTypes(@RequestParam("limit") int limit);
}
35 changes: 20 additions & 15 deletions src/main/java/org/folio/bulkops/domain/bean/Instance.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.folio.bulkops.domain.converter.ContributorListConverter;
import org.folio.bulkops.domain.converter.DateWithoutTimeConverter;
import org.folio.bulkops.domain.converter.InstanceFormatListConverter;
import org.folio.bulkops.domain.converter.InstanceNoteListConverter;
import org.folio.bulkops.domain.converter.InstanceStatusConverter;
import org.folio.bulkops.domain.converter.InstanceTypeConverter;
import org.folio.bulkops.domain.converter.ModeOfIssuanceConverter;
Expand Down Expand Up @@ -93,82 +94,88 @@ public class Instance implements BulkOperationsEntity {
@UnifiedTableCell(visible = false)
private String modeOfIssuanceId;

@JsonProperty("notes")
@CsvCustomBindByName(column = "Notes", converter = InstanceNoteListConverter.class)
@CsvCustomBindByPosition(position = 9, converter = InstanceNoteListConverter.class)
@UnifiedTableCell(visible = false)
private List<InstanceNote> instanceNotes;

@JsonProperty("administrativeNotes")
@CsvCustomBindByName(column = "Administrative note", converter = StringListPipedConverter.class)
@CsvCustomBindByPosition(position = 9, converter = StringListPipedConverter.class)
@CsvCustomBindByPosition(position = 10, converter = StringListPipedConverter.class)
@UnifiedTableCell(visible = false)
private List<String> administrativeNotes;

@JsonProperty("title")
@CsvCustomBindByName(column = "Resource title", converter = StringConverter.class)
@CsvCustomBindByPosition(position = 10, converter = StringConverter.class)
@CsvCustomBindByPosition(position = 11, converter = StringConverter.class)
@UnifiedTableCell
private String title;

@JsonProperty("indexTitle")
@CsvCustomBindByName(column = "Index title", converter = StringConverter.class)
@CsvCustomBindByPosition(position = 11, converter = StringConverter.class)
@CsvCustomBindByPosition(position = 12, converter = StringConverter.class)
@UnifiedTableCell(visible = false)
private String indexTitle;

@JsonProperty("series")
@CsvCustomBindByName(column = "Series statements", converter = SeriesListConverter.class)
@CsvCustomBindByPosition(position = 12, converter = SeriesListConverter.class)
@CsvCustomBindByPosition(position = 13, converter = SeriesListConverter.class)
@UnifiedTableCell(visible = false)
private List<Series> series;

@JsonProperty("contributors")
@CsvCustomBindByName(column = "Contributors", converter = ContributorListConverter.class)
@CsvCustomBindByPosition(position = 13, converter = ContributorListConverter.class)
@CsvCustomBindByPosition(position = 14, converter = ContributorListConverter.class)
@UnifiedTableCell
private List<ContributorName> contributors;

@JsonProperty("editions")
@CsvCustomBindByName(column = "Edition", converter = StringListPipedConverter.class)
@CsvCustomBindByPosition(position = 14, converter = StringListPipedConverter.class)
@CsvCustomBindByPosition(position = 15, converter = StringListPipedConverter.class)
@UnifiedTableCell(visible = false)
private List<String> editions;

@JsonProperty("physicalDescriptions")
@CsvCustomBindByName(column = "Physical description", converter = StringListPipedConverter.class)
@CsvCustomBindByPosition(position = 15, converter = StringListPipedConverter.class)
@CsvCustomBindByPosition(position = 16, converter = StringListPipedConverter.class)
@UnifiedTableCell(visible = false)
private List<String> physicalDescriptions;

@JsonProperty("instanceTypeId")
@CsvCustomBindByName(column = "Resource type", converter = InstanceTypeConverter.class)
@CsvCustomBindByPosition(position = 16, converter = InstanceTypeConverter.class)
@CsvCustomBindByPosition(position = 17, converter = InstanceTypeConverter.class)
@UnifiedTableCell
private String instanceTypeId;

@JsonProperty("natureOfContentTermIds")
@CsvCustomBindByName(column = "Nature of content", converter = NatureOfContentTermListConverter.class)
@CsvCustomBindByPosition(position = 17, converter = NatureOfContentTermListConverter.class)
@CsvCustomBindByPosition(position = 18, converter = NatureOfContentTermListConverter.class)
@UnifiedTableCell(visible = false)
private List<String> natureOfContentTermIds;

@JsonProperty("instanceFormatIds")
@CsvCustomBindByName(column = "Formats", converter = InstanceFormatListConverter.class)
@CsvCustomBindByPosition(position = 18, converter = InstanceFormatListConverter.class)
@CsvCustomBindByPosition(position = 19, converter = InstanceFormatListConverter.class)
@UnifiedTableCell(visible = false)
private List<String> instanceFormatIds;

@JsonProperty("languages")
@CsvCustomBindByName(column = "Languages", converter = StringListPipedConverter.class)
@CsvCustomBindByPosition(position = 19, converter = StringListPipedConverter.class)
@CsvCustomBindByPosition(position = 20, converter = StringListPipedConverter.class)
@UnifiedTableCell(visible = false)
private List<String> languages;

@JsonProperty("publicationFrequency")
@Valid
@CsvCustomBindByName(column = "Publication frequency", converter = StringListPipedConverter.class)
@CsvCustomBindByPosition(position = 20, converter = StringListPipedConverter.class)
@CsvCustomBindByPosition(position = 21, converter = StringListPipedConverter.class)
@UnifiedTableCell(visible = false)
private List<String> publicationFrequency;

@JsonProperty("publicationRange")
@CsvCustomBindByName(column = "Publication range", converter = StringListPipedConverter.class)
@CsvCustomBindByPosition(position = 21, converter = StringListPipedConverter.class)
@CsvCustomBindByPosition(position = 22, converter = StringListPipedConverter.class)
@UnifiedTableCell(visible = false)
private List<String> publicationRange;

Expand All @@ -188,8 +195,6 @@ public class Instance implements BulkOperationsEntity {
private PublicationPeriod publicationPeriod;
@JsonProperty("electronicAccess")
private List<ElectronicAccess> electronicAccesses;
@JsonProperty("notes")
private List<InstanceNote> instanceNotes;
@JsonProperty("statisticalCodeIds")
private List<String> statisticalCodeIds;
@JsonProperty("sourceRecordFormat")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.folio.bulkops.domain.converter;

import static org.folio.bulkops.domain.format.SpecialCharacterEscaper.escape;
import static org.folio.bulkops.domain.format.SpecialCharacterEscaper.restore;
import static org.folio.bulkops.util.Constants.ARRAY_DELIMITER;
import static org.folio.bulkops.util.Constants.ITEM_DELIMITER;
import static org.folio.bulkops.util.Constants.ITEM_DELIMITER_PATTERN;
import static org.folio.bulkops.util.Utils.booleanToStringNullSafe;

import org.apache.commons.lang3.ObjectUtils;
import org.folio.bulkops.domain.bean.InstanceNote;
import org.folio.bulkops.exception.EntityFormatException;
import org.folio.bulkops.service.InstanceReferenceHelper;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class InstanceNoteListConverter extends BaseConverter<List<InstanceNote>> {
private static final int NUMBER_OF_INSTANCE_NOTE_ELEMENTS = 3;
private static final int INSTANCE_NOTE_NOTE_TYPE_INDEX = 0;
private static final int INSTANCE_NOTE_NOTE_INDEX = 1;
private static final int INSTANCE_NOTE_STAFF_ONLY_INDEX = 2;

@Override
public List<InstanceNote> convertToObject(String value) {
return Arrays.stream(value.split(ITEM_DELIMITER_PATTERN))
.map(this::restoreInstanceNote)
.filter(ObjectUtils::isNotEmpty)
.toList();
}

@Override
public String convertToString(List<InstanceNote> object) {
return object.stream()
.filter(Objects::nonNull)
.map(note -> String.join(ARRAY_DELIMITER,
escape(InstanceReferenceHelper.service().getNoteTypeNameById(note.getInstanceNoteTypeId())),
escape(note.getNote()),
booleanToStringNullSafe(note.getStaffOnly())))
.collect(Collectors.joining(ITEM_DELIMITER));
}

private InstanceNote restoreInstanceNote(String s) {
if (ObjectUtils.isEmpty(s)) {
return null;
}
var tokens = s.split(ARRAY_DELIMITER, -1);
if (tokens.length < NUMBER_OF_INSTANCE_NOTE_ELEMENTS) {
throw new EntityFormatException(String.format("Illegal number of instance note elements: %d, expected: %d", tokens.length,
NUMBER_OF_INSTANCE_NOTE_ELEMENTS));
}
return InstanceNote.builder()
.instanceNoteTypeId(InstanceReferenceHelper.service().getNoteTypeIdByName(restore(tokens[INSTANCE_NOTE_NOTE_TYPE_INDEX])))
.note(restore(tokens[INSTANCE_NOTE_NOTE_INDEX]))
.staffOnly(ObjectUtils.isEmpty(tokens[INSTANCE_NOTE_STAFF_ONLY_INDEX]) ? null : Boolean.parseBoolean(tokens[INSTANCE_NOTE_STAFF_ONLY_INDEX]))
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import org.folio.bulkops.domain.bean.CirculationNote;
import org.folio.bulkops.domain.bean.HoldingsNote;
import org.folio.bulkops.domain.bean.HoldingsRecord;
import org.folio.bulkops.domain.bean.Instance;
import org.folio.bulkops.domain.bean.InstanceNote;
import org.folio.bulkops.domain.bean.Item;
import org.folio.bulkops.domain.bean.ItemNote;
import org.folio.bulkops.domain.dto.Action;
Expand All @@ -12,6 +14,8 @@
import java.util.ArrayList;
import java.util.List;

import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static java.util.stream.Collectors.toCollection;
import static org.folio.bulkops.processor.ItemsNotesUpdater.CHECK_IN_NOTE_TYPE;
import static org.folio.bulkops.processor.ItemsNotesUpdater.CHECK_OUT_NOTE_TYPE;
Expand All @@ -25,11 +29,9 @@ public List<String> removeAdministrativeNotes() {
}

public List<String> addToAdministrativeNotes(String administrativeNote, List<String> administrativeNotes) {
if (administrativeNotes == null) {
administrativeNotes = new ArrayList<>();
}
administrativeNotes.add(administrativeNote);
return administrativeNotes;
List<String> notes = isNull(administrativeNotes) ? new ArrayList<>() : new ArrayList<>(administrativeNotes);
notes.add(administrativeNote);
return notes;
}

public List<String> findAndRemoveAdministrativeNote(String administrativeNoteToRemove, List<String> administrativeNotes) {
Expand Down Expand Up @@ -78,4 +80,21 @@ public void changeNoteTypeForAdministrativeNotes(Item item, Action action) {
item.setAdministrativeNotes(new ArrayList<>());
}
}

public void changeNoteTypeForAdministrativeNotes(Instance instance, Action action) {
if (nonNull(instance.getAdministrativeNotes())) {
List<InstanceNote> instanceNotes = isNull(instance.getInstanceNotes()) ?
new ArrayList<>() :
new ArrayList<>(instance.getInstanceNotes());
var notes = instance.getAdministrativeNotes().stream()
.map(administrativeNote -> InstanceNote.builder()
.instanceNoteTypeId(action.getUpdated())
.note(administrativeNote)
.staffOnly(false).build())
.toList();
instanceNotes.addAll(notes);
instance.setInstanceNotes(instanceNotes);
instance.setAdministrativeNotes(new ArrayList<>());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package org.folio.bulkops.processor;

import static java.lang.String.format;
import static org.folio.bulkops.domain.dto.UpdateActionType.CHANGE_TYPE;
import static org.folio.bulkops.domain.dto.UpdateActionType.CLEAR_FIELD;
import static org.folio.bulkops.domain.dto.UpdateActionType.SET_TO_FALSE;
import static org.folio.bulkops.domain.dto.UpdateActionType.SET_TO_TRUE;
import static org.folio.bulkops.domain.dto.UpdateOptionType.ADMINISTRATIVE_NOTE;
import static org.folio.bulkops.domain.dto.UpdateOptionType.INSTANCE_NOTE;
import static org.folio.bulkops.domain.dto.UpdateOptionType.STAFF_SUPPRESS;
import static org.folio.bulkops.domain.dto.UpdateOptionType.SUPPRESS_FROM_DISCOVERY;

import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.folio.bulkops.domain.bean.Instance;
import org.folio.bulkops.domain.dto.Action;
Expand All @@ -21,13 +24,19 @@

@Log4j2
@Component
@AllArgsConstructor
@RequiredArgsConstructor
public class InstanceDataProcessor extends AbstractDataProcessor<Instance> {
private final InstanceNotesUpdaterFactory instanceNotesUpdaterFactory;

@Override
public Validator<UpdateOptionType, Action> validator(Instance instance) {
return (option, action) -> {
if (CLEAR_FIELD.equals(action.getType()) && Set.of(STAFF_SUPPRESS, SUPPRESS_FROM_DISCOVERY).contains(option)) {
throw new RuleValidationException("Suppress flag cannot be cleared");
} else if (INSTANCE_NOTE.equals(option) && !"FOLIO".equals(instance.getSource())) {
throw new RuleValidationException("Bulk edit of instance notes is not supported for MARC Instances");
} else if (ADMINISTRATIVE_NOTE.equals(option) && CHANGE_TYPE.equals(action.getType()) && !"FOLIO".equals(instance.getSource())) {
throw new RuleValidationException("Change note type for administrative notes is not supported for MARC Instances");
}
};
}
Expand All @@ -47,9 +56,9 @@ public Updater<Instance> updater(UpdateOptionType option, Action action) {
return instance -> instance.setDiscoverySuppress(false);
}
}
return instance -> {
return instanceNotesUpdaterFactory.getUpdater(option, action).orElseGet(() -> instance -> {
throw new BulkOperationException(format("Combination %s and %s isn't supported yet", option, action.getType()));
};
});
}

@Override
Expand Down
Loading

0 comments on commit 4612d8d

Please sign in to comment.