Skip to content

Commit

Permalink
Merge pull request #1341 from okolawole-ebsco/OKAPI-1181
Browse files Browse the repository at this point in the history
OKAPI-1181 Implement dependency check option
  • Loading branch information
julianladisch committed Jan 11, 2024
2 parents 6a0e32a + 8f0a4c8 commit 76d1b93
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ public class TenantDescriptor {
private String name;
private String description;

public TenantDescriptor() {
}

public TenantDescriptor(String id, String name) {
this.id = id;
this.name = name;
}

public void setName(String name) {
this.name = name;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,9 @@ Future<List<TenantModuleDescriptor>> installUpgradeCreate(
}
}
}
if (!options.getDepCheck() && options.getMaxParallel() > 1) {
return Future.failedFuture(new OkapiError(ErrorType.USER, messages.getMessage("10411")));
}
return tenants.getNotFound(tenantId).compose(tenant ->
moduleManager.getModulesWithFilter(options.getModuleVersionFilter(), null)
.compose(modules -> {
Expand Down Expand Up @@ -974,7 +977,9 @@ private Future<List<TenantModuleDescriptor>> runJob(
Map<String, ModuleDescriptor> modsEnabled, InstallJob job) {

List<TenantModuleDescriptor> tml = job.getModules();
DepResolution.install(modsAvailable, modsEnabled, tml, options.getReinstall());
if (options.getDepCheck()) {
DepResolution.install(modsAvailable, modsEnabled, tml, options.getReinstall());
}
if (options.getSimulate()) {
return Future.succeededFuture(tml);
}
Expand Down Expand Up @@ -1075,7 +1080,7 @@ private void jobRunPending(Tenant t, ProxyContext pc, TenantInstallOptions optio
continue;
}
ModuleDescriptor md = modsAvailable.get(tm.getId());
if (!depsOK(tm, md, getEnabledModules(t))) {
if (options.getDepCheck() && !depsOK(tm, md, getEnabledModules(t))) {
continue;
}
if (isExclusive(md)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public static TenantInstallOptions createTenantOptions(MultiMap params) {
options.setIgnoreErrors(getParamBoolean(params, "ignoreErrors", false));
options.setReinstall(getParamBoolean(params, "reinstall", false));
options.setMaxParallel(getParamInteger(params, "parallel", 1));
options.setDepCheck(getParamBoolean(params, "depCheck", true));
return options;
}

Expand Down
9 changes: 9 additions & 0 deletions okapi-core/src/main/raml/okapi.raml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ traits:
description: |
Number of parallel calls to tenant interfaces of modules.
A value of 1 means calls are happening in sequence.
If depCheck=false and parallel>1, the invocation will be rejected.
type: integer
required: false
default: 1
Expand All @@ -104,6 +105,14 @@ traits:
description: Parameters for tenant init
type: string
required: false
depCheck:
description: |
Whether to check dependencies of modules before being installed. Modules are installed exactly
in the order supplied by the client. If depCheck=false and parallel>1, the invocation will be
rejected.
type: boolean
required: false
default: true

/_/deployment/modules:
description: |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
10408=Missing id property in JSON response for module {0}: {1} {2}
10409=Unexpected Location header in response for module {0}: {1} {2}
10410=Tenant operation failed for module {0}: {1}
10411=Install Option parallel can not be greater than 1 when Install Option depCheck is false

#OkapiClient
10500=OkapiClient: No OkapiUrl specified
Expand Down
152 changes: 151 additions & 1 deletion okapi-core/src/test/java/org/folio/okapi/ModuleTenantsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1515,7 +1515,7 @@ public void test4() {
}

@Test
public void testDepCheck() {
public void testRegisterDepCheck() {
RestAssured.port = port;
RestAssuredClient c;
Response r;
Expand Down Expand Up @@ -1587,6 +1587,156 @@ public void testDepCheck() {
c.getLastReport().isEmpty());
}

@Test
public void testInstallDepCheck() {
final String okapiTenant = "roskilde";
RestAssured.port = port;
RestAssuredClient c;
Response r;

// create basic 1.0.0
final String docBasic_1_0_0 = "{" + LS
+ " \"id\" : \"basic-module-1.0.0-alpha\"," + LS
+ " \"name\" : \"this module\"," + LS
+ " \"provides\" : [ {" + LS
+ " \"id\" : \"_tenant\"," + LS
+ " \"version\" : \"1.1\"," + LS
+ " \"interfaceType\" : \"system\"," + LS
+ " \"handlers\" : [ {" + LS
+ " \"methods\" : [ \"POST\", \"DELETE\" ]," + LS
+ " \"pathPattern\" : \"/_/tenant\"," + LS
+ " \"permissionsRequired\" : [ ]" + LS
+ " }, {" + LS
+ " \"methods\" : [ \"POST\" ]," + LS
+ " \"pathPattern\" : \"/_/tenant/disable\"," + LS
+ " \"permissionsRequired\" : [ ]" + LS
+ " } ]" + LS
+ " }, {" + LS
+ " \"id\" : \"bint\"," + LS
+ " \"version\" : \"1.0\"," + LS
+ " \"handlers\" : [ {" + LS
+ " \"methods\" : [ \"GET\", \"POST\" ]," + LS
+ " \"pathPattern\" : \"/foo\"," + LS
+ " \"permissionsRequired\" : [ ]" + LS
+ " } ]" + LS
+ " } ]," + LS
+ " \"requires\" : [ { " + LS
+ " \"id\" : \"unknown1\", \"version\" : \"1.0\""
+ " }, {"
+ " \"id\" : \"unknown2\", \"version\" : \"2.0\""
+ " } ]," + LS
+ " \"launchDescriptor\" : {" + LS
+ " \"exec\" : "
+ "\"java -Dport=%p -jar ../okapi-test-module/target/okapi-test-module-fat.jar\"" + LS
+ " }" + LS
+ "}";

// add tenant
final String docTenantRoskilde = "{" + LS
+ " \"id\" : \"" + okapiTenant + "\"," + LS
+ " \"name\" : \"" + okapiTenant + "\"," + LS
+ " \"description\" : \"Roskilde bibliotek\"" + LS
+ "}";
c = api.createRestAssured3();
r = c.given()
.header("Content-Type", "application/json")
.body(docTenantRoskilde).post("/_/proxy/tenants")
.then().statusCode(201)
.body(equalTo(docTenantRoskilde))
.extract().response();
Assert.assertTrue(
"raml: " + c.getLastReport().toString(),
c.getLastReport().isEmpty());

// register the module basic 1.0.0
c = api.createRestAssured3();
r = c.given()
.header("Content-Type", "application/json")
.body(docBasic_1_0_0).post("/_/proxy/modules?check=false").then().statusCode(201)
.extract().response();
Assert.assertTrue(
"raml: " + c.getLastReport().toString(),
c.getLastReport().isEmpty());

// deploy basic 1.0.0
final String docBasicDeployment_1_0_0 = "{" + LS
+ " \"srvcId\" : \"basic-module-1.0.0-alpha\"," + LS
+ " \"nodeId\" : \"localhost\"" + LS
+ "}";
c = api.createRestAssured3();
r = c.given()
.header("Content-Type", "application/json")
.body(docBasicDeployment_1_0_0).post("/_/discovery/modules")
.then()
.statusCode(201)
.extract().response();
Assert.assertTrue("raml: " + c.getLastReport().toString(),
c.getLastReport().isEmpty());

// install module with depCheck not set
c = api.createRestAssured3();
r = c.given()
.header("Content-Type", "application/json")
.body("[ {\"id\" : \"basic-module-1.0.0-alpha\", \"action\" : \"enable\"} ]")
.post("/_/proxy/tenants/" + okapiTenant + "/install")
.then().statusCode(400)
.extract().response();
Assert.assertTrue(
"raml: " + c.getLastReport().toString(),
c.getLastReport().isEmpty());
Assert.assertEquals("interface unknown2 required by module basic-module-1.0.0-alpha not found." +
" interface unknown1 required by module basic-module-1.0.0-alpha not found", r.getBody().asString());

// install module with depCheck enabled
c = api.createRestAssured3();
r = c.given()
.header("Content-Type", "application/json")
.body("[ {\"id\" : \"basic-module-1.0.0-alpha\", \"action\" : \"enable\"} ]")
.post("/_/proxy/tenants/" + okapiTenant + "/install?depCheck=true")
.then().statusCode(400)
.extract().response();
Assert.assertTrue(
"raml: " + c.getLastReport().toString(),
c.getLastReport().isEmpty());
Assert.assertEquals("interface unknown2 required by module basic-module-1.0.0-alpha not found." +
" interface unknown1 required by module basic-module-1.0.0-alpha not found", r.getBody().asString());

// install module with depCheck disabled and parallel set to 3
c = api.createRestAssured3();
r = c.given()
.header("Content-Type", "application/json")
.body("[ {\"id\" : \"basic-module-1.0.0-alpha\", \"action\" : \"enable\"} ]")
.post("/_/proxy/tenants/" + okapiTenant + "/install?depCheck=false&parallel=3")
.then().statusCode(400)
.extract().response();
Assert.assertTrue(
"raml: " + c.getLastReport().toString(),
c.getLastReport().isEmpty());
Assert.assertEquals("Install Option parallel can not be greater than 1 " +
"when Install Option depCheck is false", r.getBody().asString());

// install module with depCheck disabled and parallel not set
c = api.createRestAssured3();
r = c.given()
.header("Content-Type", "application/json")
.body("[ {\"id\" : \"basic-module-1.0.0-alpha\", \"action\" : \"enable\"} ]")
.post("/_/proxy/tenants/" + okapiTenant + "/install?depCheck=false")
.then().statusCode(200)
.extract().response();
Assert.assertTrue(
"raml: " + c.getLastReport().toString(),
c.getLastReport().isEmpty());

c = api.createRestAssured3();
c.given()
.header("Content-Type", "application/json")
.delete("/_/discovery/modules")
.then()
.statusCode(204).log().ifValidationFails();
Assert.assertTrue("raml: " + c.getLastReport().toString(),
c.getLastReport().isEmpty());
}

@Test
public void test641() {
final String okapiTenant = "roskilde";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
package org.folio.okapi.managers;

import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.apache.logging.log4j.Logger;
import org.folio.okapi.bean.InterfaceDescriptor;
import org.folio.okapi.bean.ModuleDescriptor;
import org.folio.okapi.bean.ModuleInstance;
import org.folio.okapi.bean.RoutingEntry;
import org.folio.okapi.bean.Tenant;
import org.folio.okapi.bean.TenantDescriptor;
import org.folio.okapi.bean.*;
import org.folio.okapi.common.ErrorType;
import org.folio.okapi.common.OkapiLogger;
import org.folio.okapi.service.impl.TenantStoreNull;
import org.folio.okapi.util.LockedTypedMap1Faulty;
import org.folio.okapi.util.OkapiError;
import org.folio.okapi.util.TenantInstallOptions;
import org.folio.okapi.util.TestBase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@RunWith(VertxUnitRunner.class)
public class TenantManagerTest extends TestBase {
Expand Down Expand Up @@ -466,4 +472,58 @@ public void testTenantInterfacesv2(TestContext context) {
context.assertTrue(obj.getBoolean("purge"));
}));
}

@Test
public void depCheckTrue(TestContext testContext) {
depCheck(true)
.onComplete(testContext.asyncAssertFailure(e -> {
assertThat(e).isInstanceOf(OkapiError.class);
assertThat(e.getMessage()).isEqualTo("interface Child Interface required by module parentMod-1.0.0 not found");
}));
}

@Test
public void depCheckFalse(TestContext testContext) {
depCheck(false)
.onComplete(testContext.asyncAssertSuccess());
}

private Future<List<TenantModuleDescriptor>> depCheck(boolean depCheck) {
String parentModuleId = "parentMod-1.0.0";
String childModuleId = "childMod-1.0.0";
String testTenantId = "testTenant";
// Child Module
InterfaceDescriptor[] childInterfaces = {new InterfaceDescriptor("Child Interface", "1.0")};
ModuleDescriptor childModuleDescriptor = new ModuleDescriptor(childModuleId);
childModuleDescriptor.setProvides(childInterfaces);

// Parent Module
InterfaceDescriptor[] parentInterfaces = {new InterfaceDescriptor("Parent Interface", "1.0")};
ModuleDescriptor parentModuleDescriptor = new ModuleDescriptor(parentModuleId);
parentModuleDescriptor.setProvides(parentInterfaces);
parentModuleDescriptor.setRequires(childInterfaces);

ModuleManager mockedModuleManager = mock(ModuleManager.class);
when(mockedModuleManager.getModulesWithFilter(any(), ArgumentMatchers.isNull()))
.thenReturn(Future.succeededFuture(Collections.singletonList(parentModuleDescriptor)));
when(mockedModuleManager.get(parentModuleId)).thenReturn(Future.succeededFuture(parentModuleDescriptor));

TenantManager tenantManager = new TenantManager(mockedModuleManager, new TenantStoreNull(), true);
return tenantManager.init(vertx)
.compose(x -> {
Tenant tenant = new Tenant(new TenantDescriptor(testTenantId, "Test Tenant"));
return tenantManager.insert(tenant);
})
.compose(x -> {
TenantInstallOptions tenantInstallOptions = new TenantInstallOptions();
tenantInstallOptions.setDepCheck(depCheck);
TenantModuleDescriptor tenantModuleDescriptor = new TenantModuleDescriptor();
tenantModuleDescriptor.setId(parentModuleId);
tenantModuleDescriptor.setAction(TenantModuleDescriptor.Action.enable);
List<TenantModuleDescriptor> moduleDescriptorList = new LinkedList<>();
moduleDescriptorList.add(tenantModuleDescriptor);

return tenantManager.installUpgradeCreate(testTenantId, "depCheck", null, tenantInstallOptions, moduleDescriptorList);
});
}
}

0 comments on commit 76d1b93

Please sign in to comment.