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

Bug-fixes, transformation POST/PUT (MODHAADM-68) #103

Merged
merged 1 commit into from
Jan 10, 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
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import static org.folio.harvesteradmin.dataaccess.statics.RequestParameters.supportedGetRequestParameters;
import static org.folio.okapi.common.HttpResponse.responseText;

import io.vertx.core.AsyncResult;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
Expand Down Expand Up @@ -140,7 +142,7 @@ public Future<ProcessedHarvesterResponseGetById> getConfigRecordById(
return promise.future();
}

private Future<ProcessedHarvesterResponse> getConfigRecordByIdOrName(
private Future<ProcessedHarvesterResponse> getUniqueConfigRecordByIdOrName(
String harvesterPath, String id, String name) {
Promise<ProcessedHarvesterResponse> promise = Promise.promise();
if (id == null || id.isEmpty()) {
Expand All @@ -160,13 +162,13 @@ private Future<ProcessedHarvesterResponse> getConfigRecordByIdOrName(
promise.complete(
new ProcessedHarvesterResponseGetUniqueByName(
recordsByName.jsonObject(),
422,
404,
"Record with name \"" + name + "\" not found", recordsFoundByName));
} else {
promise.complete(
new ProcessedHarvesterResponseGetUniqueByName(
recordsByName.jsonObject(),
422,
404,
"Found multiple records with name \"" + name + "\"", recordsFoundByName));
}
return promise.future();
Expand Down Expand Up @@ -249,7 +251,7 @@ public Future<ProcessedHarvesterResponsePost> resolveReferencedEntities(
final String transformationId = entity.getJsonObject("transformation").getString("id");
final String storageName = entity.getJsonObject("storage").getString("name");
final String transformationName = entity.getJsonObject("transformation").getString("name");
getConfigRecordByIdOrName(HARVESTER_STORAGES_PATH, storageId, storageName)
getUniqueConfigRecordByIdOrName(HARVESTER_STORAGES_PATH, storageId, storageName)
.onComplete(storage -> {
if (storage.succeeded()) {
if (storage.result().wasOK()) {
Expand All @@ -265,7 +267,7 @@ public Future<ProcessedHarvesterResponsePost> resolveReferencedEntities(
fatalError.add("Error looking up storage by id or name "
+ storage.cause().getMessage());
}
getConfigRecordByIdOrName(HARVESTER_TRANSFORMATIONS_PATH,
getUniqueConfigRecordByIdOrName(HARVESTER_TRANSFORMATIONS_PATH,
transformationId, transformationName)
.onComplete(transformation -> {
if (transformation.succeeded()) {
Expand All @@ -278,7 +280,7 @@ public Future<ProcessedHarvesterResponsePost> resolveReferencedEntities(
constraintViolation.add(transformation.result().errorMessage());

}
if (constraintViolation.size() > 0) {
if (!constraintViolation.isEmpty()) {
promise.complete(
new ProcessedHarvesterResponsePost(422, constraintViolation.toString())
);
Expand All @@ -292,7 +294,7 @@ public Future<ProcessedHarvesterResponsePost> resolveReferencedEntities(
+ transformation.cause().getMessage());
}
});
if (fatalError.size() > 0) {
if (!fatalError.isEmpty()) {
promise.complete(new ProcessedHarvesterResponsePost(500,fatalError.toString()));
}
});
Expand Down Expand Up @@ -408,8 +410,12 @@ public Future<ProcessedHarvesterResponsePut> putConfigRecord(RoutingContext rout
String harvesterPath = mapToHarvesterPath(routingContext);
JsonObject jsonToPut = routingContext.body().asJsonObject();
String id = routingContext.request().getParam("id");
if (harvesterPath != null && harvesterPath.equals(HARVESTER_HARVESTABLES_PATH)) {
jsonToPut.put("lastUpdated", iso_instant.format(Instant.now()));
if (harvesterPath != null) {
if (harvesterPath.equals(HARVESTER_TRANSFORMATIONS_PATH)) {
return putTransformation(routingContext);
} else if (harvesterPath.equals(HARVESTER_HARVESTABLES_PATH)) {
jsonToPut.put("lastUpdated", iso_instant.format(Instant.now()));
}
}
return putConfigRecord(routingContext, harvesterPath, jsonToPut, id);
}
Expand Down Expand Up @@ -580,12 +586,41 @@ public Future<Integer> checkForReferencingEntities(String api, String id) {
return promise.future();
}



private Future<ProcessedHarvesterResponsePost> doPostAndPutTransformation(
RoutingContext routingContext) {
private Future<ProcessedHarvesterResponsePut> putTransformation(RoutingContext routingContext) {
JsonObject transformationJson = routingContext.body().asJsonObject();
logger.debug("About to POST-then-PUT " + transformationJson.encodePrettily());
logger.debug("About to PUT " + transformationJson.encodePrettily());
List<Future<ProcessedHarvesterResponse>> stepFutures = getStepLookupFutures(transformationJson);
Promise<ProcessedHarvesterResponsePut> promise = Promise.promise();
GenericCompositeFuture.all(stepFutures).onComplete(steps -> {
if (steps.succeeded()) {
boolean allStepsFound = true;
for (int h = 0; h < steps.result().size(); h++) {
ProcessedHarvesterResponse stepResponse = steps.result().resultAt(h);
if (stepResponse.statusCode() == NOT_FOUND) {
allStepsFound = false;
promise.complete(new ProcessedHarvesterResponsePut(422,
"Uniquely referenced step not found, cannot store transformation pipeline: "
+ stepResponse.errorMessage()));
break;
}
}
if (allStepsFound) {
expandAssociatedSteps(steps, transformationJson);
putConfigRecord(
routingContext,
HARVESTER_TRANSFORMATIONS_PATH,
transformationJson,
transformationJson.getString("id")).onComplete(response ->
promise.complete(response.result())
);
}
}
});
return promise.future();
}

private List<Future<ProcessedHarvesterResponse>> getStepLookupFutures(
JsonObject transformationJson) {
JsonArray stepsIdsJson =
transformationJson.containsKey("stepAssociations") ? transformationJson.getJsonArray(
"stepAssociations").copy() : new JsonArray();
Expand All @@ -599,19 +634,32 @@ private Future<ProcessedHarvesterResponsePost> doPostAndPutTransformation(
String stepName = step.containsKey("step")
? step.getJsonObject("step").getString("name")
: step.getString("stepName");
stepFutures.add(getConfigRecordByIdOrName(HARVESTER_STEPS_PATH, stepId, stepName));
stepFutures.add(getUniqueConfigRecordByIdOrName(HARVESTER_STEPS_PATH, stepId, stepName));
}
return stepFutures;
}

/**
* Checks that referenced steps exist, POSTs the transformation without the steps,
* creates schema compliant step associations in the transformation object,
* PUTs the transformation, checks that a transformation with the given ID exists.
* @return response structure
*/
private Future<ProcessedHarvesterResponsePost> doPostAndPutTransformation(
RoutingContext routingContext) {
JsonObject transformationJson = routingContext.body().asJsonObject();
logger.debug("About to POST-then-PUT " + transformationJson.encodePrettily());
List<Future<ProcessedHarvesterResponse>> stepFutures = getStepLookupFutures(transformationJson);
Promise<ProcessedHarvesterResponsePost> promise = Promise.promise();
GenericCompositeFuture.all(stepFutures).onComplete(steps -> {
if (steps.succeeded()) {
boolean allStepsFound = true;
for (int h = 0; h < steps.result().size(); h++) {
ProcessedHarvesterResponseGetById stepResponse = steps.result().resultAt(h);
ProcessedHarvesterResponse stepResponse = steps.result().resultAt(h);
if (stepResponse.statusCode() == NOT_FOUND) {
logger.info("Step not found: " + stepResponse.errorMessage());
allStepsFound = false;
promise.complete(new ProcessedHarvesterResponsePost(422,
"Referenced step not found, cannot store transformation pipeline: "
"Uniquely referenced step not found, cannot store transformation pipeline: "
+ stepResponse.errorMessage()));
break;
}
Expand All @@ -625,21 +673,7 @@ private Future<ProcessedHarvesterResponsePost> doPostAndPutTransformation(
if (transformationPost.succeeded()
&& transformationPost.result().statusCode() == CREATED) {
JsonObject createdTransformation = transformationPost.result().jsonObject();
createdTransformation.put("stepAssociations", new JsonArray());
for (int i = 0; i < steps.result().size(); i++) {
ProcessedHarvesterResponseGetById stepResponse = steps.result().resultAt(i);
final JsonObject stepJson = stepResponse.jsonObject();
JsonObject tsaJson = new JsonObject();
tsaJson.put("id", getRandomFifteenDigitString());
tsaJson.put("position", Integer.toString(i + 1));
tsaJson.put("step", new JsonObject());
tsaJson.getJsonObject("step")
.put("entityType",
typeToEmbeddedTypeMap.get(stepJson.getString("type")));
tsaJson.getJsonObject("step").put("id", stepJson.getString("id"));
tsaJson.put("transformation", createdTransformation.getString("id"));
createdTransformation.getJsonArray("stepAssociations").add(tsaJson);
}
expandAssociatedSteps(steps, createdTransformation);
putConfigRecord(
routingContext,
HARVESTER_TRANSFORMATIONS_PATH,
Expand Down Expand Up @@ -687,6 +721,25 @@ private Future<ProcessedHarvesterResponsePost> doPostAndPutTransformation(
return promise.future();
}

private static void expandAssociatedSteps(AsyncResult<CompositeFuture> steps,
JsonObject createdTransformation) {
createdTransformation.put("stepAssociations", new JsonArray());
for (int i = 0; i < steps.result().size(); i++) {
ProcessedHarvesterResponse stepResponse = steps.result().resultAt(i);
final JsonObject stepJson = stepResponse.jsonObject();
JsonObject tsaJson = new JsonObject();
tsaJson.put("id", getRandomFifteenDigitString());
tsaJson.put("position", Integer.toString(i + 1));
tsaJson.put("step", new JsonObject());
tsaJson.getJsonObject("step")
.put("entityType",
typeToEmbeddedTypeMap.get(stepJson.getString("type")));
tsaJson.getJsonObject("step").put("id", stepJson.getString("id"));
tsaJson.put("transformation", createdTransformation.getString("id"));
createdTransformation.getJsonArray("stepAssociations").add(tsaJson);
}
}

private Future<ProcessedHarvesterResponsePost> doPostTsaPutTransformation(
RoutingContext routingContext) {
JsonObject incomingTsa = routingContext.body().asJsonObject();
Expand All @@ -695,7 +748,7 @@ private Future<ProcessedHarvesterResponsePost> doPostTsaPutTransformation(
String stepId = incomingTsa.getJsonObject("step").getString("id");
String stepName = incomingTsa.getJsonObject("step").getString("name");
Promise<ProcessedHarvesterResponsePost> promise = Promise.promise();
getConfigRecordByIdOrName(HARVESTER_TRANSFORMATIONS_PATH, transId, transName).onComplete(
getUniqueConfigRecordByIdOrName(HARVESTER_TRANSFORMATIONS_PATH, transId, transName).onComplete(
theTransformation -> {
if (theTransformation.failed()) {
promise.complete(
Expand All @@ -710,7 +763,7 @@ private Future<ProcessedHarvesterResponsePost> doPostTsaPutTransformation(
+ transId + " not found."));
} else {
JsonObject transformationFound = theTransformation.result().jsonObject();
getConfigRecordByIdOrName(HARVESTER_STEPS_PATH, stepId, stepName).onComplete(
getUniqueConfigRecordByIdOrName(HARVESTER_STEPS_PATH, stepId, stepName).onComplete(
theStep -> {
if (!theStep.result().found()) {
promise.complete(
Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/openapi/schemas/transformationPostPut.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
"type": "string",
"description": "Unique record identifier."
},
"acl": {
"type": "string",
"description": "System controlled access control string.",
"readOnly": true
},
"name": {
"type": "string",
"description": "Name of the transformation pipeline."
Expand Down