Skip to content

Commit

Permalink
feat(pub-period-migration) implement request and responce instance AP…
Browse files Browse the repository at this point in the history
…I without publicationPeriod field.
  • Loading branch information
SvitlanaKovalova1 committed Oct 22, 2024
1 parent c9e1913 commit a2a373a
Show file tree
Hide file tree
Showing 23 changed files with 339 additions and 103 deletions.
5 changes: 3 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
* Provides `instance-storage-batch 3.1`
* Provides `instance-storage-batch-sync 3.0`
* Provides `instance-storage-batch-sync-unsafe 3.0`
* Provides `inventory-view 3.0`
* Provides `inventory-view-instance-set 3.0`
* Provides `inventory-view 3.1`
* Provides `inventory-view-instance-set 3.1`
* Provides `instance-iteration 1.0`
* Requires `holdings-storage 8.0`
* Requires `bound-with-parts-storage 2.0`
* Requires `async-migration 1.0`
* Requires `item-storage-dereferenced 1.1`

### Features
* Add module descriptor validator plugin and fix the permission names ([MODINVSTOR-1247](https://folio-org.atlassian.net/browse/MODINVSTOR-1247))
Expand Down
6 changes: 3 additions & 3 deletions descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"provides": [
{
"id": "item-storage-dereferenced",
"version": "1.0",
"version": "1.1",
"handlers": [{
"methods": ["GET"],
"pathPattern": "/item-storage-dereferenced/items",
Expand Down Expand Up @@ -1337,7 +1337,7 @@
},
{
"id": "inventory-view",
"version": "3.0",
"version": "3.1",
"handlers": [
{
"methods": ["GET"],
Expand All @@ -1348,7 +1348,7 @@
},
{
"id": "inventory-view-instance-set",
"version": "3.0",
"version": "3.1",
"handlers": [
{
"methods": ["GET"],
Expand Down
8 changes: 4 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<commons-lang3.version>3.17.0</commons-lang3.version>
<log4j.version>2.24.1</log4j.version>
<folio-s3-client.version>2.2.0-SNAPSHOT</folio-s3-client.version>
<commons-beanutils.version>1.9.4</commons-beanutils.version>
<google-code-gson.version>2.11.0</google-code-gson.version>

<okapi-testing.version>6.0.3</okapi-testing.version>
<junit.version>5.11.2</junit.version>
Expand Down Expand Up @@ -140,9 +140,9 @@
<version>${folio-s3-client.version}</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>${commons-beanutils.version}</version>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${google-code-gson.version}</version> <!-- Replace with the latest version -->
</dependency>

<!-- Test Dependencies -->
Expand Down
4 changes: 2 additions & 2 deletions ramls/dereferenceditem.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
},
"instanceRecord":{
"type": "object",
"description" : "Parent instance record.",
"$ref": "instance.json"
"description" : "Parent instance record without the publicationPeriod.",
"$ref": "instance-without-pub-period.json"
},
"_version": {
"type": "integer",
Expand Down
2 changes: 1 addition & 1 deletion ramls/holdings-storage/holdingsRecordView.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"holdingsInstance": {
"description": "Instance of holding record. This is a virtual field, accessible only when using mod-graphql.",
"type": "object",
"folio:$ref": "instance.json",
"folio:$ref": "instance-without-pub-period.json",
"readonly": true,
"folio:isVirtual": true,
"folio:linkBase": "inventory/instances",
Expand Down
4 changes: 2 additions & 2 deletions ramls/instance-set.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"$ref": "uuid.json"
},
"instance": {
"description": "Instance record",
"$ref": "instance.json"
"description": "Instance record without the publicationPeriod",
"$ref": "instance-without-pub-period.json"
},
"holdingsRecords": {
"type": "array",
Expand Down
12 changes: 1 addition & 11 deletions ramls/instance-storage.raml
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,7 @@ resourceTypes:
searchable: {description: "by title (using CQL)",
example: "title=\"*uproot*\""},
]
responses:
200:
body:
application/json:
type: instances
post:
responses:
200:
body:
application/json:
type: instance
delete:
is: [searchable: { description: "CQL to select instances to delete, use cql.allRecords=1 to delete all. Deletes connected marc source records.",
example: "hrid==\"in123-0*\"" } ]
Expand All @@ -108,7 +98,7 @@ resourceTypes:
200:
body:
application/json:
type: instance
type: instanceWithoutPubPeriod
description: |
Get Instance by InstanceId
Instances are stored and accessed by a hash of key properties. The rules which govern
Expand Down
4 changes: 2 additions & 2 deletions ramls/instances-batch-response.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
"properties": {
"instances": {
"id": "instancesList",
"description": "List of all successfully saved Instances",
"description": "List of all successfully saved Instances without the publicationPeriod",
"type": "array",
"items": {
"type": "object",
"$ref": "instance.json"
"$ref": "instance-without-pub-period.json"
}
},
"errorMessages": {
Expand Down
35 changes: 35 additions & 0 deletions ramls/inventory-view-instance-without-pub-period.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Instance with holdings and items",
"type": "object",
"properties": {
"instanceId": {
"description": "Instance id",
"$ref": "uuid.json"
},
"isBoundWith": {
"description": "Records the relationship between a part of a bound-with (a holdings-record) and the bound-with as a whole (the circulatable item)",
"type": "boolean"
},
"instance": {
"description": "Instance record without the publicationPeriod",
"$ref": "instance-without-pub-period.json"
},
"holdingsRecords": {
"type": "array",
"description": "Holdings records for the instance",
"items": {
"$ref": "holdings-storage/holdingsRecord.json"
}
},
"items": {
"type": "array",
"description": "Items for the instance",
"items": {
"$ref": "item.json"
}
}
},
"additionalProperties": false,
"required": ["instanceId", "instance"]
}
3 changes: 2 additions & 1 deletion ramls/inventory-view.raml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ documentation:

types:
inventoryViewInstance: !include inventory-view-instance.json
inventoryViewInstanceWithoutPubPeriod: !include inventory-view-instance-without-pub-period.json
errors: !include raml-util/schemas/errors.schema

traits:
Expand All @@ -23,7 +24,7 @@ resourceTypes:
/inventory-view/instances:
type:
collection-stream:
schemaCollection: inventoryViewInstance
schemaCollection: inventoryViewInstanceWithoutPubPeriod
exampleCollection: !include examples/inventory-view-instances.json
get:
description: Get instances by id with their holdings and items
Expand Down
121 changes: 121 additions & 0 deletions src/main/java/org/folio/persist/AbstractRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,50 @@
import static io.vertx.core.Promise.promise;
import static java.lang.String.format;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.Json;
import io.vertx.ext.web.RoutingContext;
import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.RowSet;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.ws.rs.core.MediaType;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.HttpStatus;
import org.folio.cql2pgjson.CQL2PgJSON;
import org.folio.dbschema.ObjectMapperTool;
import org.folio.rest.jaxrs.model.Diagnostic;
import org.folio.rest.jaxrs.model.ResultInfo;
import org.folio.rest.persist.Criteria.Criterion;
import org.folio.rest.persist.PgExceptionUtil;
import org.folio.rest.persist.PostgresClient;
import org.folio.rest.persist.PostgresClientFuturized;
import org.folio.rest.persist.PostgresClientStreamResult;
import org.folio.rest.persist.SQLConnection;
import org.folio.rest.persist.cql.CQLWrapper;
import org.folio.rest.persist.facets.FacetField;
import org.folio.rest.persist.facets.FacetManager;
import org.folio.rest.persist.interfaces.Results;
import org.folio.utils.ObjectConverterUtils;

public abstract class AbstractRepository<T> {

private static final Logger logger = LogManager.getLogger();
private static final ObjectMapper OBJECT_MAPPER = ObjectMapperTool.getMapper();
private static final String JSON_COLUMN = "jsonb";

protected final PostgresClientFuturized postgresClientFuturized;
protected final PostgresClient postgresClient;
protected final String tableName;
Expand Down Expand Up @@ -111,4 +137,99 @@ public Future<RowSet<Row>> deleteAll() {
public Future<RowSet<Row>> deleteById(String id) {
return postgresClientFuturized.deleteById(tableName, id);
}

public <S, R> void streamGet(String table, Class<S> clazz,
String cql, int offset, int limit, List<String> facets,
String element, int queryTimeout, RoutingContext routingContext,
Class<R> targetClazz) {

var response = routingContext.response();
try {
var facetList = FacetManager.convertFacetStrings2FacetFields(facets, JSON_COLUMN);
var wrapper = new CQLWrapper(new CQL2PgJSON(table + "." + JSON_COLUMN), cql, limit, offset);
streamGetInstances(table, clazz, wrapper, facetList, element, queryTimeout, routingContext, targetClazz);
} catch (Exception e) {
logger.error(e.getMessage(), e);
response.setStatusCode(HttpStatus.HTTP_INTERNAL_SERVER_ERROR.toInt());
response.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN);
response.end(e.toString());
}
}

private <S, R> void streamGetInstances(String table, Class<S> clazz,
CQLWrapper filter, List<FacetField> facetList,
String element, int queryTimeout,
RoutingContext routingContext, Class<R> targetClazz) {

var response = routingContext.response();
postgresClient.streamGet(table, clazz, JSON_COLUMN, filter, true, null,
facetList, queryTimeout, reply -> {
if (reply.failed()) {
handleFailure(filter, reply, response);
return;
}
streamGetResult(reply.result(), element, response, targetClazz);
});
}

private <S, R> void streamGetResult(PostgresClientStreamResult<S> result,
String element, HttpServerResponse response,
Class<R> targetClazz) {
response.setStatusCode(HttpStatus.HTTP_OK.toInt());
response.setChunked(true);
response.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
response.write("{\n");
response.write(String.format(" \"%s\": [%n", element));
AtomicBoolean first = new AtomicBoolean(true);
result.exceptionHandler(res -> handleException(result, response, res));
result.endHandler(res -> streamTrailer(response, result.resultInfo()));
result.handler(res -> handleResult(response, targetClazz, res, first));
}

private <S> void handleFailure(CQLWrapper filter,
AsyncResult<PostgresClientStreamResult<S>> reply,
HttpServerResponse response) {
var message = PgExceptionUtil.badRequestMessage(reply.cause());
if (message == null) {
message = reply.cause().getMessage();
}
message = String.format("%s: %s", message, filter.getQuery());
logger.error(message, reply.cause());
response.setStatusCode(HttpStatus.HTTP_BAD_REQUEST.toInt());
response.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN);
response.end(message);
}

private void streamTrailer(HttpServerResponse response, ResultInfo resultInfo) {
response.write("],\n");
if (resultInfo.getTotalRecords() != null) {
response.write(String.format(" \"totalRecords\": %d,\n", resultInfo.getTotalRecords()));
}
response.end(String.format(" \"resultInfo\": %s\n}", Json.encode(resultInfo)));
}

private <S> void handleException(PostgresClientStreamResult<S> result, HttpServerResponse response, Throwable res) {
var diagnostic = new Diagnostic()
.withCode(HttpStatus.HTTP_INTERNAL_SERVER_ERROR.toString())
.withMessage(res.getMessage());
result.resultInfo().setDiagnostics(List.of(diagnostic));
streamTrailer(response, result.resultInfo());
}

private <S, R> void handleResult(HttpServerResponse response, Class<R> targetClazz, S res, AtomicBoolean first) {
String itemString;
try {
var targetObject = ObjectConverterUtils.convertObject(res, targetClazz);
itemString = OBJECT_MAPPER.writeValueAsString(targetObject);
} catch (JsonProcessingException ex) {
logger.error(ex.getMessage(), ex);
throw new IllegalArgumentException(ex.getCause());
}
if (first.get()) {
first.set(false);
} else {
response.write(String.format(",%n"));
}
response.write(itemString);
}
}
5 changes: 3 additions & 2 deletions src/main/java/org/folio/rest/impl/InstanceBatchSyncApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
import java.util.Map;
import javax.ws.rs.core.Response;
import org.folio.rest.annotations.Validate;
import org.folio.rest.jaxrs.model.Instances;
import org.folio.rest.jaxrs.model.InstancesPost;
import org.folio.rest.jaxrs.resource.InstanceStorageBatchSynchronous;
import org.folio.services.instance.InstanceService;
import org.folio.utils.InstanceUtils;
import org.folio.utils.ObjectConverterUtils;

public class InstanceBatchSyncApi implements InstanceStorageBatchSynchronous {
@Validate
Expand All @@ -21,7 +22,7 @@ public void postInstanceStorageBatchSynchronous(boolean upsert, InstancesPost en
Handler<AsyncResult<Response>> asyncResultHandler,
Context vertxContext) {

var instances = InstanceUtils.copyPropertiesToInstances(entity.getInstances());
var instances = ObjectConverterUtils.convertObject(entity, Instances.class);

new InstanceService(vertxContext, okapiHeaders)
.createInstances(instances.getInstances(), upsert, true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
import java.util.Map;
import javax.ws.rs.core.Response;
import org.folio.rest.annotations.Validate;
import org.folio.rest.jaxrs.model.Instances;
import org.folio.rest.jaxrs.model.InstancesPost;
import org.folio.rest.jaxrs.resource.InstanceStorageBatchSynchronousUnsafe;
import org.folio.services.instance.InstanceService;
import org.folio.utils.InstanceUtils;
import org.folio.utils.ObjectConverterUtils;

public class InstanceBatchSyncUnsafeApi implements InstanceStorageBatchSynchronousUnsafe {
@Validate
Expand All @@ -20,7 +21,7 @@ public void postInstanceStorageBatchSynchronousUnsafe(InstancesPost entity, Map<
Handler<AsyncResult<Response>> asyncResultHandler,
Context vertxContext) {

var instances = InstanceUtils.copyPropertiesToInstances(entity.getInstances());
var instances = ObjectConverterUtils.convertObject(entity, Instances.class);

new InstanceService(vertxContext, okapiHeaders)
.createInstances(instances.getInstances(), true, false)
Expand Down
Loading

0 comments on commit a2a373a

Please sign in to comment.