Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CIRC-2099 POST Api implementation #1481

Merged
merged 17 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,23 @@
}
]
},
{
"id": "print-events",
"version": "1.0",
"handlers": [
{
"methods": ["POST"],
"pathPattern": "/circulation/print-events-entry",
"permissionsRequired": [
"circulation.print-events-entry.item.post"
],
"modulePermissions": [
"print-events-storage.print-events-entry.item.post",
"circulation-storage.circulation-settings.collection.get"
]
}
]
},
{
"id": "_timer",
"version": "1.0",
Expand Down Expand Up @@ -1318,6 +1335,10 @@
{
"id": "circulation-settings-storage",
"version": "1.0"
},
{
"id": "print-events-storage",
"version": "1.0"
}
],
"optional": [
Expand All @@ -1327,6 +1348,11 @@
}
],
"permissionSets": [
{
"permissionName": "circulation.print-events-entry.item.post",
"displayName": "circulation - create print events",
"description": "create print event logs"
},
{
"permissionName": "circulation.requests.queue.reorder.collection.post",
"displayName": "circulation - reorder queue for an item",
Expand Down
9 changes: 9 additions & 0 deletions ramls/examples/print-events-request.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"requestIds": [
"059e54bb-53e5-4039-a2fb-b34358e88b0a",
"e70dcbae-30c6-47ac-94f8-4ffefd44a935"
],
"requesterId": "d51470ea-5daa-480b-a4aa-09c8c6d9940e",
"requesterName": "requester",
"printEventDate": "2024-06-25T20:00:00+05:30"
}
2 changes: 2 additions & 0 deletions src/main/java/org/folio/circulation/CirculationVerticle.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.folio.circulation.resources.OverdueFineCirculationRulesEngineResource;
import org.folio.circulation.resources.OverdueFineScheduledNoticeProcessingResource;
import org.folio.circulation.resources.PickSlipsResource;
import org.folio.circulation.resources.PrintEventsResource;
import org.folio.circulation.resources.RequestByInstanceIdResource;
import org.folio.circulation.resources.RequestCirculationRulesEngineResource;
import org.folio.circulation.resources.RequestCollectionResource;
Expand Down Expand Up @@ -152,6 +153,7 @@ public void start(Promise<Void> startFuture) {
new LoanRelatedFeeFineClosedHandlerResource(client).register(router);
new FeeFineBalanceChangedHandlerResource(client).register(router);
new CirculationSettingsResource(client).register(router);
new PrintEventsResource(client).register(router);

server.requestHandler(router)
.listen(config().getInteger("port"), result -> {
Expand Down
59 changes: 59 additions & 0 deletions src/main/java/org/folio/circulation/domain/PrintEventRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.folio.circulation.domain;

import io.vertx.core.json.JsonObject;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.invoke.MethodHandles;
import java.util.List;
import java.util.Set;

import static lombok.AccessLevel.PRIVATE;
import static org.folio.circulation.support.json.JsonPropertyFetcher.getArrayProperty;
import static org.folio.circulation.support.json.JsonPropertyFetcher.getProperty;

@AllArgsConstructor(access = PRIVATE)
@ToString(onlyExplicitlyIncluded = true)
public class PrintEventRequest {
private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass());
public static final String REQUEST_IDS_FIELD = "requestIds";
public static final String REQUESTER_ID_FIELD = "requesterId";
public static final String REQUESTER_NAME_FIELD = "requesterName";
public static final String PRINT_DATE_FIELD = "printEventDate";

@ToString.Include
@Getter
private final JsonObject representation;

@Getter
private final List<String> requestIds;
@Getter
private final String requesterId;
@Getter
private final String requesterName;
@Getter
private final String printEventDate;

public static PrintEventRequest from(JsonObject representation) {
final var requestIds = getArrayProperty(representation, REQUEST_IDS_FIELD).stream()
.map(String.class::cast)
.toList();
final var requesterId = getProperty(representation, REQUESTER_ID_FIELD);
final var requesterName = getProperty(representation, REQUESTER_NAME_FIELD);
final var printEventDate = getProperty(representation, PRINT_DATE_FIELD);

if (requestIds.isEmpty() || null == requesterId || null == requesterName || null == printEventDate || !containsOnlyKnownFields(representation)) {
log.info("from:: Print Event Request JSON is invalid: {},{},{},{},{}", representation, requestIds, requesterName, requesterId, printEventDate);
return null;
}
return new PrintEventRequest(representation, requestIds, requesterId, requesterName, printEventDate);
}

private static boolean containsOnlyKnownFields(JsonObject representation) {
return Set.of(REQUEST_IDS_FIELD, REQUESTER_ID_FIELD, REQUESTER_NAME_FIELD, PRINT_DATE_FIELD)
.containsAll(representation.fieldNames());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.folio.circulation.infrastructure.storage;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.circulation.domain.PrintEventRequest;
import org.folio.circulation.support.Clients;
import org.folio.circulation.support.CollectionResourceClient;
import org.folio.circulation.support.http.client.ResponseInterpreter;
import org.folio.circulation.support.results.Result;

import java.lang.invoke.MethodHandles;
import java.util.concurrent.CompletableFuture;

import static org.folio.circulation.support.http.ResponseMapping.forwardOnFailure;
import static org.folio.circulation.support.results.Result.succeeded;

public class PrintEventsRepository {
private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass());

private final CollectionResourceClient printEventsStorageClient;

public PrintEventsRepository(Clients clients) {
printEventsStorageClient = clients.printEventsStorageClient();
}

public CompletableFuture<Result<Void>> create(PrintEventRequest printEventRequest) {
log.info("create:: parameters printEvent: {}", printEventRequest);
final var storagePrintEventRequest = printEventRequest.getRepresentation();
final ResponseInterpreter<Void> interpreter = new ResponseInterpreter<Void>()
.on(201, succeeded(null))
.otherwise(forwardOnFailure());
return printEventsStorageClient.post(storagePrintEventRequest).thenApply(interpreter::flatMap);
}

}
111 changes: 111 additions & 0 deletions src/main/java/org/folio/circulation/resources/PrintEventsResource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package org.folio.circulation.resources;

import io.vertx.core.http.HttpClient;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.circulation.domain.CirculationSetting;
import org.folio.circulation.domain.MultipleRecords;
import org.folio.circulation.domain.PrintEventRequest;
import org.folio.circulation.infrastructure.storage.CirculationSettingsRepository;
import org.folio.circulation.infrastructure.storage.PrintEventsRepository;
import org.folio.circulation.infrastructure.storage.requests.RequestRepository;
import org.folio.circulation.support.Clients;
import org.folio.circulation.support.RouteRegistration;
import org.folio.circulation.support.http.server.JsonHttpResponse;
import org.folio.circulation.support.http.server.WebContext;
import org.folio.circulation.support.results.Result;

import java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;

import static org.folio.circulation.support.ValidationErrorFailure.singleValidationError;
import static org.folio.circulation.support.json.JsonPropertyFetcher.getProperty;
import static org.folio.circulation.support.results.Result.ofAsync;
import static org.folio.circulation.support.results.Result.succeeded;

public class PrintEventsResource extends Resource {
private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass());
private static final String PRINT_EVENT_FLAG_QUERY = "query=name=printEventLogFeature";
private static final String PRINT_EVENT_FEATURE_DISABLED_ERROR = "print event feature is disabled for this tenant";
private static final String NO_CONFIG_FOUND_ERROR = "No configuration found for print event feature";
private static final String MULTIPLE_CONFIGS_ERROR = "Multiple configurations found for print event feature";
private static final String PRINT_EVENT_FLAG_PROPERTY_NAME = "enablePrintLog";

public PrintEventsResource(HttpClient client) {
super(client);
}

@Override
public void register(Router router) {
new RouteRegistration("/circulation/print-events-entry", router)
.create(this::create);
}

void create(RoutingContext routingContext) {
final var context = new WebContext(routingContext);
final var clients = Clients.create(context, client);
final var printEventsRepository = new PrintEventsRepository(clients);
final var circulationSettingsRepository = new CirculationSettingsRepository(clients);
final var requestRepository = new RequestRepository(clients);
final var incomingRepresentation = routingContext.body().asJsonObject();
final var printEventRequest = PrintEventRequest.from(incomingRepresentation);

log.info("create:: Creating print event: {}", printEventRequest);

ofAsync(printEventRequest)
.thenApply(refuseWhenPrintEventRequestIsInvalid())
.thenCompose(r -> r.after(validatePrintEventFeatureFlag(circulationSettingsRepository)))
.thenCompose(r -> r.after(validateRequests(requestRepository)))
.thenCompose(r -> r.after(printEventsRepository::create))
.thenApply(r -> r.map(response -> JsonHttpResponse.created(null, null)))
.thenAccept(context::writeResultToHttpResponse);
}

private static Function<PrintEventRequest, CompletableFuture<Result<PrintEventRequest>>>
validateRequests(RequestRepository requestRepository) {
return printRequest -> requestRepository.fetchRequests(printRequest.getRequestIds())
.thenApply(printRequestList -> printRequestList.map(Collection::size)).thenApply(size -> {
if (size.value() != printRequest.getRequestIds().size()) {
return Result.failed(singleValidationError("invalid request found", "", ""));
}
return succeeded(printRequest);
});
}

private static Function<Result<PrintEventRequest>, Result<PrintEventRequest>>
refuseWhenPrintEventRequestIsInvalid() {
return r -> r.failWhen(printEventRequest -> succeeded(printEventRequest == null),
circulationSetting -> singleValidationError("Print Event Request JSON is invalid", "", ""));
}

private static Function<PrintEventRequest, CompletableFuture<Result<PrintEventRequest>>> validatePrintEventFeatureFlag(
CirculationSettingsRepository circulationSettingsRepository) {
return printEventRequest -> circulationSettingsRepository.findBy(PRINT_EVENT_FLAG_QUERY)
.thenApply(result ->
handleCirculationSettingResult(result.map(MultipleRecords::getRecords), printEventRequest)
);
}

private static Result<PrintEventRequest> handleCirculationSettingResult(Result<Collection<CirculationSetting>> result,
PrintEventRequest printEventRequest) {

int size = result.value().size();
if (size == 0) {
return Result.failed(singleValidationError(NO_CONFIG_FOUND_ERROR, "", ""));
} else if (size > 1) {
return Result.failed(singleValidationError(MULTIPLE_CONFIGS_ERROR, "", ""));
}
boolean isEnabled = result.value().stream()
.map(x -> Boolean.valueOf(getProperty(x.getValue(), PRINT_EVENT_FLAG_PROPERTY_NAME))).findFirst().orElse(true);

if (!isEnabled) {
return Result.failed(singleValidationError(PRINT_EVENT_FEATURE_DISABLED_ERROR, "", ""));
}
return succeeded(printEventRequest);
}

}
15 changes: 15 additions & 0 deletions src/main/java/org/folio/circulation/support/Clients.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ public class Clients {
private final CollectionResourceClient circulationItemClient;
private final GetManyRecordsClient settingsStorageClient;
private final CollectionResourceClient circulationSettingsStorageClient;
private final CollectionResourceClient printEventsStorageClient;


public static Clients create(WebContext context, HttpClient httpClient) {
return new Clients(context.createHttpClient(httpClient), context);
Expand Down Expand Up @@ -138,6 +140,8 @@ private Clients(OkapiHttpClient client, WebContext context) {
settingsStorageClient = createSettingsStorageClient(client, context);
circulationItemClient = createCirculationItemClient(client, context);
circulationSettingsStorageClient = createCirculationSettingsStorageClient(client, context);
printEventsStorageClient = createPrintEventsStorageClient(client, context);

}
catch(MalformedURLException e) {
throw new InvalidOkapiLocationException(context.getOkapiLocation(), e);
Expand Down Expand Up @@ -380,6 +384,10 @@ public CollectionResourceClient circulationSettingsStorageClient() {
return circulationSettingsStorageClient;
}

public CollectionResourceClient printEventsStorageClient() {
return printEventsStorageClient;
}

private static CollectionResourceClient getCollectionResourceClient(
OkapiHttpClient client, WebContext context,
String path)
Expand Down Expand Up @@ -814,6 +822,13 @@ private CollectionResourceClient createCirculationSettingsStorageClient(
"/circulation-settings-storage/circulation-settings");
}

private CollectionResourceClient createPrintEventsStorageClient(
OkapiHttpClient client, WebContext context) throws MalformedURLException {

return getCollectionResourceClient(client, context,
"/print-events-storage/print-events-entry");
}

private GetManyRecordsClient createSettingsStorageClient(
OkapiHttpClient client, WebContext context)
throws MalformedURLException {
Expand Down
Loading