use of org.folio.oaipmh.Request in project mod-oai-pmh by folio-org.
the class AbstractGetRecordsHelper method buildRecords.
/**
* Builds {@link Map} with storage id as key and {@link RecordType} with populated header if there is any,
* otherwise empty map is returned
*/
private Map<String, RecordType> buildRecords(Context context, Request request, JsonArray instances) {
final boolean suppressedRecordsProcessingEnabled = getBooleanProperty(request.getRequestId(), REPOSITORY_SUPPRESSED_RECORDS_PROCESSING);
if (instances != null && !instances.isEmpty()) {
Map<String, RecordType> records = new HashMap<>();
RecordMetadataManager metadataManager = RecordMetadataManager.getInstance();
// Using LinkedHashMap just to rely on order returned by storage service
String identifierPrefix = request.getIdentifierPrefix();
instances.stream().map(JsonObject.class::cast).filter(instance -> isNotEmpty(storageHelper.getIdentifierId(instance))).forEach(instance -> {
String recordId = storageHelper.getRecordId(instance);
String identifierId = storageHelper.getIdentifierId(instance);
RecordType record = createRecord(request, identifierPrefix, instance, identifierId);
// Some repositories like SRS can return record source data along with other info
String source = storageHelper.getInstanceRecordSource(instance);
if (source != null && record.getHeader().getStatus() == null) {
if (suppressedRecordsProcessingEnabled) {
source = metadataManager.updateMetadataSourceWithDiscoverySuppressedData(source, instance);
}
try {
record.withMetadata(buildOaiMetadata(request, source));
} catch (Exception e) {
logger.error("Error occurred while converting record to xml representation. {}.", e.getMessage(), e);
logger.debug("Skipping problematic record due the conversion error. Source record id - {}.", recordId);
return;
}
} else {
context.put(recordId, instance);
}
if (filterInstance(request, instance)) {
records.put(recordId, record);
}
});
return records;
}
return Collections.emptyMap();
}
use of org.folio.oaipmh.Request in project mod-oai-pmh by folio-org.
the class MarcWithHoldingsRequestHelper method processBatch.
private void processBatch(Request request, Context context, Promise<Response> oaiPmhResponsePromise, String requestId, boolean firstBatch) {
try {
boolean deletedRecordSupport = RepositoryConfigurationUtil.isDeletedRecordsEnabled(request.getRequestId());
int batchSize = Integer.parseInt(RepositoryConfigurationUtil.getProperty(request.getRequestId(), REPOSITORY_MAX_RECORDS_PER_RESPONSE));
getNextInstances(request, batchSize, requestId, firstBatch).future().onComplete(fut -> {
if (fut.failed()) {
logger.error("Get instances failed: {}.", fut.cause().getMessage(), fut.cause());
oaiPmhResponsePromise.fail(fut.cause());
return;
}
List<JsonObject> instances = fut.result();
logger.debug("Processing instances: {}.", instances.size());
if (CollectionUtils.isEmpty(instances) && !firstBatch) {
handleException(oaiPmhResponsePromise, new IllegalArgumentException("Specified resumption token doesn't exists."));
return;
}
if (!firstBatch && (CollectionUtils.isNotEmpty(instances) && !instances.get(0).getString(INSTANCE_ID_FIELD_NAME).equals(request.getNextRecordId()))) {
handleException(oaiPmhResponsePromise, new IllegalArgumentException("Stale resumption token."));
return;
}
if (CollectionUtils.isEmpty(instances)) {
logger.debug("Got empty instances.");
buildRecordsResponse(request, requestId, instances, new HashMap<>(), firstBatch, null, deletedRecordSupport).onSuccess(oaiPmhResponsePromise::complete).onFailure(e -> handleException(oaiPmhResponsePromise, e));
return;
}
String nextInstanceId = instances.size() <= batchSize ? null : instances.get(batchSize).getString(INSTANCE_ID_FIELD_NAME);
List<JsonObject> instancesWithoutLast = nextInstanceId != null ? instances.subList(0, batchSize) : instances;
final SourceStorageSourceRecordsClientWrapper srsClient = createAndSetupSrsClient(request);
int retryAttempts = Integer.parseInt(RepositoryConfigurationUtil.getProperty(request.getRequestId(), REPOSITORY_SRS_HTTP_REQUEST_RETRY_ATTEMPTS));
requestSRSByIdentifiers(srsClient, context.owner(), instancesWithoutLast, deletedRecordSupport, retryAttempts).onSuccess(res -> buildRecordsResponse(request, requestId, instancesWithoutLast, res, firstBatch, nextInstanceId, deletedRecordSupport).onSuccess(oaiPmhResponsePromise::complete).onFailure(e -> handleException(oaiPmhResponsePromise, e))).onFailure(e -> handleException(oaiPmhResponsePromise, e));
});
} catch (Exception e) {
handleException(oaiPmhResponsePromise, e);
}
}
use of org.folio.oaipmh.Request in project mod-oai-pmh by folio-org.
the class MarcWithHoldingsRequestHelper method enrichInstances.
private Future<List<JsonObject>> enrichInstances(List<JsonObject> result, Request request) {
Map<String, JsonObject> instances = result.stream().collect(LinkedHashMap::new, (map, instance) -> map.put(instance.getString(INSTANCE_ID_FIELD_NAME), instance), Map::putAll);
Promise<List<JsonObject>> promise = Promise.promise();
var webClient = WebClientProvider.getWebClient();
var httpRequest = webClient.postAbs(request.getOkapiUrl() + INVENTORY_ITEMS_AND_HOLDINGS_ENDPOINT);
if (request.getOkapiUrl().contains("https:")) {
httpRequest.ssl(true);
}
httpRequest.putHeader(OKAPI_TOKEN, request.getOkapiToken());
httpRequest.putHeader(OKAPI_TENANT, TenantTool.tenantId(request.getOkapiHeaders()));
httpRequest.putHeader(ACCEPT, APPLICATION_JSON);
httpRequest.putHeader(CONTENT_TYPE, APPLICATION_JSON);
JsonObject entries = new JsonObject();
entries.put(INSTANCE_IDS_ENRICH_PARAM_NAME, new JsonArray(new ArrayList<>(instances.keySet())));
entries.put(SKIP_SUPPRESSED_FROM_DISCOVERY_RECORDS, isSkipSuppressed(request));
Promise<Boolean> responseChecked = Promise.promise();
var jsonParser = new OaiPmhJsonParser().objectValueMode();
jsonParser.handler(event -> {
JsonObject itemsAndHoldingsFields = event.objectValue();
String instanceId = itemsAndHoldingsFields.getString(INSTANCE_ID_FIELD_NAME);
JsonObject instance = instances.get(instanceId);
if (instance != null) {
enrichDiscoverySuppressed(itemsAndHoldingsFields, instance);
instance.put(RecordMetadataManager.ITEMS_AND_HOLDINGS_FIELDS, itemsAndHoldingsFields);
// case when no items
if (itemsAndHoldingsFields.getJsonArray(ITEMS).isEmpty()) {
enrichOnlyEffectiveLocationEffectiveCallNumberFromHoldings(instance);
} else {
adjustItems(instance);
}
} else {
logger.info("Instance with instanceId {} wasn't in the request.", instanceId);
}
});
jsonParser.exceptionHandler(throwable -> responseChecked.future().onSuccess(invalidResponseReceivedAndProcessed -> {
if (invalidResponseReceivedAndProcessed) {
return;
}
logger.error("Error has been occurred at JsonParser while reading data from items-and-holdings response. Message:{}", throwable.getMessage(), throwable);
promise.fail(throwable);
}));
httpRequest.as(BodyCodec.jsonStream(jsonParser)).sendBuffer(entries.toBuffer()).onSuccess(response -> {
switch(response.statusCode()) {
case 200:
responseChecked.complete(false);
break;
case 403:
{
String errorMsg = getErrorFromStorageMessage(INVENTORY_STORAGE, request.getOkapiUrl() + INVENTORY_ITEMS_AND_HOLDINGS_ENDPOINT, ENRICH_INSTANCES_MISSED_PERMISSION);
logger.error(errorMsg);
promise.fail(new IllegalStateException(errorMsg));
responseChecked.complete(true);
break;
}
default:
{
String errorFromStorageMessage = getErrorFromStorageMessage(INVENTORY_STORAGE, request.getOkapiUrl() + INVENTORY_ITEMS_AND_HOLDINGS_ENDPOINT, response.statusMessage());
String errorMessage = errorFromStorageMessage + response.statusCode();
logger.error(errorMessage);
promise.fail(new IllegalStateException(errorFromStorageMessage));
responseChecked.complete(true);
}
}
promise.complete(new ArrayList<>(instances.values()));
}).onFailure(e -> {
logger.error(e.getMessage());
promise.fail(e);
});
return promise.future();
}
use of org.folio.oaipmh.Request in project mod-oai-pmh by folio-org.
the class VerbValidator method validateExclusiveParam.
/**
* In case of resumption token param presence verifies if there any other parameters were specified too. If they were then error
* will be added to error list.
*
* @param verb - verb
* @param requestParams - request parameters
* @param request - oai-pmh request
* @param errors - list of errors
*/
private void validateExclusiveParam(Verb verb, Map<String, String> requestParams, Request request, List<OAIPMHerrorType> errors) {
String resumptionToken = requestParams.get(verb.getExclusiveParam());
if (verb.getExclusiveParam() != null && resumptionToken != null) {
requestParams.keySet().stream().filter(p -> !verb.getExcludedParams().contains(p)).filter(p -> !verb.getExclusiveParam().equals(p)).findAny().ifPresent(param -> {
if (!param.equals(VERB_PARAM)) {
errors.add(new OAIPMHerrorType().withCode(BAD_ARGUMENT).withValue(format(EXCLUSIVE_PARAM_ERROR_MESSAGE, verb.name(), verb.getExclusiveParam())));
}
});
if (!request.isResumptionTokenParsableAndValid()) {
OAIPMHerrorType error = new OAIPMHerrorType().withCode(BAD_RESUMPTION_TOKEN).withValue(format(INVALID_RESUMPTION_TOKEN, verb.name()));
errors.add(error);
return;
}
if (request.isResumptionTokenTimeExpired()) {
OAIPMHerrorType errorByExpiredTime = new OAIPMHerrorType().withCode(BAD_RESUMPTION_TOKEN).withValue(EXPIRED_RESUMPTION_TOKEN);
errors.add(errorByExpiredTime);
}
}
}
use of org.folio.oaipmh.Request in project mod-oai-pmh by folio-org.
the class GetOaiIdentifiersHelper method buildListIdentifiers.
/**
* Builds {@link ListIdentifiersType} with headers if there is any item or {@code null}
*
* @param request request
* @param srsRecords the response from the storage which contains items
* @return {@link ListIdentifiersType} with headers if there is any or {@code null}
*/
private OAIPMH buildListIdentifiers(Request request, JsonObject srsRecords) {
ResponseHelper responseHelper = getResponseHelper();
JsonArray instances = storageHelper.getItems(srsRecords);
Integer totalRecords = storageHelper.getTotalRecords(srsRecords);
if (request.isRestored() && !canResumeRequestSequence(request, totalRecords, instances)) {
return responseHelper.buildOaipmhResponseWithErrors(request, BAD_RESUMPTION_TOKEN, RESUMPTION_TOKEN_FLOW_ERROR);
}
if (instances != null && !instances.isEmpty()) {
logger.debug("{} entries retrieved out of {}.", instances.size(), totalRecords);
ListIdentifiersType identifiers = new ListIdentifiersType().withResumptionToken(buildResumptionToken(request, instances, totalRecords));
String identifierPrefix = request.getIdentifierPrefix();
instances.stream().map(object -> (JsonObject) object).filter(instance -> StringUtils.isNotEmpty(storageHelper.getIdentifierId(instance))).filter(instance -> filterInstance(request, instance)).map(instance -> addHeader(identifierPrefix, request, instance)).forEach(identifiers::withHeaders);
if (identifiers.getHeaders().isEmpty()) {
OAIPMH oaipmh = responseHelper.buildBaseOaipmhResponse(request);
return oaipmh.withErrors(createNoRecordsFoundError());
}
ResumptionTokenType resumptionToken = buildResumptionToken(request, instances, totalRecords);
OAIPMH oaipmh = responseHelper.buildBaseOaipmhResponse(request).withListIdentifiers(identifiers);
addResumptionTokenToOaiResponse(oaipmh, resumptionToken);
return oaipmh;
}
return responseHelper.buildOaipmhResponseWithErrors(request, createNoRecordsFoundError());
}
Aggregations