supplier) {
+ if (port == null) {
+ return null;
+ }
+ final String scheme = sslContext != null ? "https" : "http";
+ final String authType = securityContextType != null ? securityContextType.name().toLowerCase() : "unknown";
+ DatabaseClient client = null;
+ try {
+ client = supplier.get();
+ return new TestResult(host, port, scheme, authType, username, client.checkConnection());
+ } catch (Exception ex) {
+ return new TestResult(host, port, scheme, authType, username, false, ex.getMessage());
+ } finally {
+ if (client != null) {
+ client.release();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/marklogic/appdeployer/command/data/LoadDataCommand.java b/src/main/java/com/marklogic/appdeployer/command/data/LoadDataCommand.java
index 147ae6d3..35dd56a9 100644
--- a/src/main/java/com/marklogic/appdeployer/command/data/LoadDataCommand.java
+++ b/src/main/java/com/marklogic/appdeployer/command/data/LoadDataCommand.java
@@ -70,6 +70,9 @@ protected FileLoader buildFileLoader(AppConfig appConfig) {
final DatabaseClient client = determineDatabaseClient(appConfig);
final GenericFileLoader loader = new GenericFileLoader(client);
+ loader.setCascadeCollections(appConfig.isCascadeCollections());
+ loader.setCascadePermissions(appConfig.isCascadePermissions());
+
DataConfig dataConfig = appConfig.getDataConfig();
final Integer batchSize = dataConfig.getBatchSize();
diff --git a/src/main/java/com/marklogic/appdeployer/command/modules/DefaultModulesLoaderFactory.java b/src/main/java/com/marklogic/appdeployer/command/modules/DefaultModulesLoaderFactory.java
index 4dae7558..c86fbf49 100644
--- a/src/main/java/com/marklogic/appdeployer/command/modules/DefaultModulesLoaderFactory.java
+++ b/src/main/java/com/marklogic/appdeployer/command/modules/DefaultModulesLoaderFactory.java
@@ -59,6 +59,8 @@ public ModulesLoader newModulesLoader(AppConfig appConfig) {
RestBatchWriter assetBatchWriter = new RestBatchWriter(modulesDatabaseClient, false);
assetBatchWriter.setThreadCount(threadCount);
AssetFileLoader assetFileLoader = new AssetFileLoader(assetBatchWriter, modulesManager);
+ assetFileLoader.setCascadeCollections(appConfig.isCascadeCollections());
+ assetFileLoader.setCascadePermissions(appConfig.isCascadePermissions());
if (appConfig.getModulesLoaderBatchSize() != null) {
assetFileLoader.setBatchSize(appConfig.getModulesLoaderBatchSize());
}
diff --git a/src/main/java/com/marklogic/appdeployer/command/schemas/LoadSchemasCommand.java b/src/main/java/com/marklogic/appdeployer/command/schemas/LoadSchemasCommand.java
index 7c64cf3b..7eb2810c 100644
--- a/src/main/java/com/marklogic/appdeployer/command/schemas/LoadSchemasCommand.java
+++ b/src/main/java/com/marklogic/appdeployer/command/schemas/LoadSchemasCommand.java
@@ -80,9 +80,10 @@ protected void loadSchemasFromDatabaseSpecificPaths(CommandContext context) {
protected void loadSchemas(String schemasPath, String schemasDatabaseName, CommandContext context) {
logger.info(format("Loading schemas into database %s from: %s", schemasDatabaseName, schemasPath));
- DatabaseClient client = buildDatabaseClient(schemasDatabaseName, context);
+ DatabaseClient schemasClient = context.getAppConfig().newAppServicesDatabaseClient(schemasDatabaseName);
+ DatabaseClient contentClient = buildContentClient(context, schemasDatabaseName);
try {
- SchemasLoader schemasLoader = buildSchemasLoader(context, client, schemasDatabaseName);
+ SchemasLoader schemasLoader = buildSchemasLoader(context, schemasClient, contentClient);
schemasLoader.loadSchemas(schemasPath);
logger.info("Finished loading schemas from: " + schemasPath);
} catch (FailedRequestException fre) {
@@ -92,12 +93,29 @@ protected void loadSchemas(String schemasPath, String schemasDatabaseName, Comma
throw fre;
}
} finally {
- client.release();
+ schemasClient.release();
+ if (contentClient != null) {
+ contentClient.release();
+ }
}
}
- protected DatabaseClient buildDatabaseClient(String schemasDatabaseName, CommandContext context) {
- return context.getAppConfig().newAppServicesDatabaseClient(schemasDatabaseName);
+ /**
+ * Construct a content client, for use when validating TDEs and generating QBVs.
+ *
+ * @param context
+ * @param schemasDatabase
+ * @return
+ */
+ private DatabaseClient buildContentClient(CommandContext context, String schemasDatabase) {
+ String contentDatabase = findContentDatabaseAssociatedWithSchemasDatabase(context, schemasDatabase);
+ if (contentDatabase != null) {
+ logger.info(format("Will use %s as a content database when loading into schemas database: %s", contentDatabase, schemasDatabase));
+ return context.getAppConfig().newAppServicesDatabaseClient(contentDatabase);
+ }
+ logger.warn(format("Unable to find a content database associated with schemas database: %s; this may " +
+ "result in errors when loading TDE templates and Query-Based-View scripts.", schemasDatabase));
+ return null;
}
/**
@@ -106,24 +124,14 @@ protected DatabaseClient buildDatabaseClient(String schemasDatabaseName, Command
* So given a schemasDatabaseName,
*
* @param context
- * @param client
+ * @param schemasClient
* @return
*/
- protected SchemasLoader buildSchemasLoader(CommandContext context, DatabaseClient client, String schemasDatabaseName) {
+ protected SchemasLoader buildSchemasLoader(CommandContext context, DatabaseClient schemasClient, DatabaseClient contentClient) {
AppConfig appConfig = context.getAppConfig();
-
- String tdeValidationDatabase = null;
- if (appConfig.isTdeValidationEnabled()) {
- tdeValidationDatabase = findContentDatabaseAssociatedWithSchemasDatabase(context, schemasDatabaseName);
- if (tdeValidationDatabase != null) {
- logger.info(format("TDE templates loaded into %s will be validated against content database %s",
- schemasDatabaseName, tdeValidationDatabase));
- }
- } else {
- logger.info("TDE validation is disabled");
- }
-
- DefaultSchemasLoader schemasLoader = new DefaultSchemasLoader(client, tdeValidationDatabase);
+ DefaultSchemasLoader schemasLoader = new DefaultSchemasLoader(schemasClient, contentClient, context.getAppConfig().isTdeValidationEnabled());
+ schemasLoader.setCascadeCollections(appConfig.isCascadeCollections());
+ schemasLoader.setCascadePermissions(appConfig.isCascadePermissions());
FileFilter filter = appConfig.getSchemasFileFilter();
if (filter != null) {
schemasLoader.addFileFilter(filter);
diff --git a/src/main/java/com/marklogic/appdeployer/scaffold/ScaffoldGenerator.java b/src/main/java/com/marklogic/appdeployer/scaffold/ScaffoldGenerator.java
index ae10e445..c997a7e6 100644
--- a/src/main/java/com/marklogic/appdeployer/scaffold/ScaffoldGenerator.java
+++ b/src/main/java/com/marklogic/appdeployer/scaffold/ScaffoldGenerator.java
@@ -38,7 +38,7 @@ public class ScaffoldGenerator extends LoggingObject {
protected ObjectMapper objectMapper;
private PrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
- public void generateScaffold(String path, AppConfig config) {
+ public void generateScaffold(String path, AppInputs appInputs) {
if (objectMapper == null) {
objectMapper = ObjectMapperFactory.getObjectMapper();
}
@@ -50,29 +50,58 @@ public void generateScaffold(String path, AppConfig config) {
File modulesDir = getModulesDir(rootDir);
modulesDir.mkdirs();
- generateContentDatabaseFile(configDir, config);
- generateSecurityFiles(configDir, config);
+ generateDatabaseFiles(configDir);
+ if (appInputs.isWithUsersAndRoles()) {
+ generateSecurityFiles(configDir, appInputs.getAppName());
+ }
+ if (appInputs.isWithRestServer()) {
+ generateRestApiFile(configDir);
+ generateRestPropertiesFile(modulesDir);
+ generateSearchOptions(modulesDir, appInputs.getAppName());
+ }
+ }
+ /**
+ *
+ * @param path
+ * @param config
+ * @deprecated since 4.6.0; use the method using {@code ScaffoldInputs} instead.
+ */
+ @Deprecated
+ public void generateScaffold(String path, AppConfig config) {
+ if (objectMapper == null) {
+ objectMapper = ObjectMapperFactory.getObjectMapper();
+ }
+ File rootDir = new File(path);
+
+ File configDir = getConfigDir(rootDir);
+ configDir.mkdirs();
+
+ File modulesDir = getModulesDir(rootDir);
+ modulesDir.mkdirs();
+
+ generateDatabaseFiles(configDir);
+ generateSecurityFiles(configDir, config.getName());
if (!config.isNoRestServer()) {
- generateRestApiFile(configDir, config);
- generateRestPropertiesFile(modulesDir, config);
- generateSearchOptions(modulesDir, config);
+ generateRestApiFile(configDir);
+ generateRestPropertiesFile(modulesDir);
+ generateSearchOptions(modulesDir, config.getName());
}
}
- private void generateSearchOptions(File modulesDir, AppConfig config) {
+ private void generateSearchOptions(File modulesDir, String appName) {
File optionsDir = new File(modulesDir, "options");
optionsDir.mkdirs();
String xml = "\n unfiltered\n 0\n";
- writeFile(xml.getBytes(), new File(optionsDir, config.getName() + "-options.xml"));
+ writeFile(xml.getBytes(), new File(optionsDir, appName + "-options.xml"));
}
- protected void generateRestPropertiesFile(File modulesDir, AppConfig config) {
- writeFile(buildRestPropertiesJson(config), new File(modulesDir, "rest-properties.json"));
+ protected void generateRestPropertiesFile(File modulesDir) {
+ writeFile(buildRestPropertiesJson(), new File(modulesDir, "rest-properties.json"));
}
- protected ObjectNode buildRestPropertiesJson(AppConfig config) {
+ protected ObjectNode buildRestPropertiesJson() {
ObjectNode node = objectMapper.createObjectNode();
node.put("debug", false);
node.put("validate-queries", true);
@@ -81,70 +110,71 @@ protected ObjectNode buildRestPropertiesJson(AppConfig config) {
return node;
}
- protected void generateSecurityFiles(File configDir, AppConfig config) {
+ protected void generateSecurityFiles(File configDir, String appName) {
File rolesDir = new File(configDir, "security/roles");
rolesDir.mkdirs();
- writeFile(buildNobodyRole(config), new File(rolesDir, "1-" + config.getName() + "-nobody-role.json"));
- writeFile(buildReaderRole(config), new File(rolesDir, "2-" + config.getName() + "-reader-role.json"));
- writeFile(buildWriterRole(config), new File(rolesDir, "3-" + config.getName() + "-writer-role.json"));
- writeFile(buildInternalRole(config), new File(rolesDir, "4-" + config.getName() + "-internal-role.json"));
- writeFile(buildAdminRole(config), new File(rolesDir, "5-" + config.getName() + "-admin-role.json"));
+ writeFile(buildNobodyRole(appName), new File(rolesDir, "1-" + appName + "-nobody-role.json"));
+ writeFile(buildReaderRole(appName), new File(rolesDir, "2-" + appName + "-reader-role.json"));
+ writeFile(buildWriterRole(appName), new File(rolesDir, "3-" + appName + "-writer-role.json"));
+ writeFile(buildInternalRole(appName), new File(rolesDir, "4-" + appName + "-internal-role.json"));
+ writeFile(buildAdminRole(appName), new File(rolesDir, "5-" + appName + "-admin-role.json"));
File usersDir = new File(configDir, "security/users");
usersDir.mkdirs();
- writeFile(buildReaderUser(config), new File(usersDir, config.getName() + "-reader-user.json"));
- writeFile(buildWriterUser(config), new File(usersDir, config.getName() + "-writer-user.json"));
- writeFile(buildAdminUser(config), new File(usersDir, config.getName() + "-admin-user.json"));
+ writeFile(buildReaderUser(appName), new File(usersDir, appName + "-reader-user.json"));
+ writeFile(buildWriterUser(appName), new File(usersDir, appName + "-writer-user.json"));
+ writeFile(buildAdminUser(appName), new File(usersDir, appName + "-admin-user.json"));
}
- protected ObjectNode buildNobodyRole(AppConfig config) {
+ protected ObjectNode buildNobodyRole(String appName) {
ObjectNode node = objectMapper.createObjectNode();
- node.put("role-name", config.getName() + "-nobody");
+ node.put("role-name", appName + "-nobody");
node.put("description", "Unauthenticated user");
node.putArray("role");
return node;
}
- protected ObjectNode buildReaderRole(AppConfig config) {
+ protected ObjectNode buildReaderRole(String appName) {
ObjectNode node = objectMapper.createObjectNode();
- node.put("role-name", config.getName() + "-reader");
+ node.put("role-name", appName + "-reader");
node.put("description", "Can view documents, but not edit");
ArrayNode array = node.putArray("role");
- array.add("rest-reader");
- array.add(config.getName() + "-nobody");
+ array.add(appName + "-nobody");
+ array = node.putArray("privilege");
+ array.add(buildPrivilege("rest-reader", "http://marklogic.com/xdmp/privileges/rest-reader", "execute"));
return node;
}
- protected ObjectNode buildWriterRole(AppConfig config) {
+ protected ObjectNode buildWriterRole(String appName) {
ObjectNode node = objectMapper.createObjectNode();
- node.put("role-name", config.getName() + "-writer");
+ node.put("role-name", appName + "-writer");
node.put("description", "Can read and write documents");
ArrayNode array = node.putArray("role");
- array.add("rest-writer");
- array.add(config.getName() + "-reader");
+ array.add(appName + "-reader");
array = node.putArray("privilege");
+ array.add(buildPrivilege("rest-writer", "http://marklogic.com/xdmp/privileges/rest-writer", "execute"));
array.add(buildPrivilege("any-uri", "http://marklogic.com/xdmp/privileges/any-uri", "execute"));
array.add(buildPrivilege("unprotected-collections", "http://marklogic.com/xdmp/privileges/unprotected-collections", "execute"));
return node;
}
- protected ObjectNode buildInternalRole(AppConfig config) {
+ protected ObjectNode buildInternalRole(String appName) {
ObjectNode node = objectMapper.createObjectNode();
- node.put("role-name", config.getName() + "-internal");
+ node.put("role-name", appName + "-internal");
node.put("description", "Internal role used for amping");
ArrayNode array = node.putArray("role");
- array.add(config.getName() + "-writer");
+ array.add(appName + "-writer");
return node;
}
- protected ObjectNode buildAdminRole(AppConfig config) {
+ protected ObjectNode buildAdminRole(String appName) {
ObjectNode node = objectMapper.createObjectNode();
- node.put("role-name", config.getName() + "-admin");
+ node.put("role-name", appName + "-admin");
node.put("description", "Non-admin administrator");
ArrayNode array = node.putArray("role");
array.add("rest-admin");
array.add("manage-admin");
- array.add(config.getName() + "-writer");
+ array.add(appName + "-writer");
array = node.putArray("privilege");
array.add(buildPrivilege("any-uri", "http://marklogic.com/xdmp/privileges/any-uri", "execute"));
array.add(buildPrivilege("xdbc:insert-in", "http://marklogic.com/xdmp/privileges/xdbc-insert-in", "execute"));
@@ -160,54 +190,56 @@ protected ObjectNode buildPrivilege(String name, String action, String kind) {
return node;
}
- protected ObjectNode buildReaderUser(AppConfig config) {
+ protected ObjectNode buildReaderUser(String appName) {
ObjectNode node = objectMapper.createObjectNode();
- String name = config.getName() + "-reader";
+ String name = appName + "-reader";
node.put("user-name", name);
node.put("password", name);
ArrayNode roles = node.putArray("role");
- roles.add(config.getName() + "-reader");
+ roles.add(appName + "-reader");
return node;
}
- protected ObjectNode buildWriterUser(AppConfig config) {
+ protected ObjectNode buildWriterUser(String appName) {
ObjectNode node = objectMapper.createObjectNode();
- String name = config.getName() + "-writer";
+ String name = appName + "-writer";
node.put("user-name", name);
node.put("password", name);
ArrayNode roles = node.putArray("role");
- roles.add(config.getName() + "-writer");
+ roles.add(appName + "-writer");
return node;
}
- protected ObjectNode buildAdminUser(AppConfig config) {
+ protected ObjectNode buildAdminUser(String appName) {
ObjectNode node = objectMapper.createObjectNode();
- String name = config.getName() + "-admin";
+ String name = appName + "-admin";
node.put("user-name", name);
node.put("password", name);
ArrayNode roles = node.putArray("role");
- roles.add(config.getName() + "-admin");
+ roles.add(appName + "-admin");
return node;
}
- protected void generateRestApiFile(File configDir, AppConfig config) {
- writeFile(buildRestApiJson(config).getBytes(), new File(configDir, "rest-api.json"));
+ protected void generateRestApiFile(File configDir) {
+ writeFile(buildRestApiJson().getBytes(), new File(configDir, "rest-api.json"));
}
- protected String buildRestApiJson(AppConfig config) {
+ protected String buildRestApiJson() {
return RestApiUtil.buildDefaultRestApiJson();
}
- protected void generateContentDatabaseFile(File configDir, AppConfig config) {
+ protected void generateDatabaseFiles(File configDir) {
File databasesDir = new File(configDir, "databases");
databasesDir.mkdirs();
- writeFile(buildContentDatabaseJson(config), new File(databasesDir, "content-database.json"));
+ writeFile(buildContentDatabaseJson(), new File(databasesDir, "content-database.json"));
+ writeFile(buildSchemasDatabaseJson(), new File(databasesDir, "schemas-database.json"));
}
- protected ObjectNode buildContentDatabaseJson(AppConfig config) {
+ protected ObjectNode buildContentDatabaseJson() {
ObjectNode node = objectMapper.createObjectNode();
node.put("database-name", "%%DATABASE%%");
+ node.put("schema-database", "%%SCHEMAS_DATABASE%%");
ArrayNode array = node.putArray("range-element-index");
ObjectNode index = array.addObject();
index.put("scalar-type", "string");
@@ -219,6 +251,12 @@ protected ObjectNode buildContentDatabaseJson(AppConfig config) {
return node;
}
+ protected ObjectNode buildSchemasDatabaseJson() {
+ ObjectNode node = objectMapper.createObjectNode();
+ node.put("database-name", "%%SCHEMAS_DATABASE%%");
+ return node;
+ }
+
protected void writeFile(ObjectNode node, File f) {
try {
byte[] bytes = objectMapper.writer(prettyPrinter).writeValueAsBytes(node);
@@ -258,4 +296,32 @@ public void setObjectMapper(ObjectMapper objectMapper) {
public void setPrettyPrinter(PrettyPrinter prettyPrinter) {
this.prettyPrinter = prettyPrinter;
}
+
+ public static class AppInputs {
+ final private String appName;
+ final private boolean withRestServer;
+ final private boolean withUsersAndRoles;
+
+ public AppInputs(String appName) {
+ this(appName, true, true);
+ }
+
+ public AppInputs(String appName, boolean withRestServer, boolean withUsersAndRoles) {
+ this.appName = appName;
+ this.withRestServer = withRestServer;
+ this.withUsersAndRoles = withUsersAndRoles;
+ }
+
+ public String getAppName() {
+ return appName;
+ }
+
+ public boolean isWithRestServer() {
+ return withRestServer;
+ }
+
+ public boolean isWithUsersAndRoles() {
+ return withUsersAndRoles;
+ }
+ }
}
diff --git a/src/main/java/com/marklogic/mgmt/admin/AdminManager.java b/src/main/java/com/marklogic/mgmt/admin/AdminManager.java
index 6c2ebe8d..570f4ad2 100644
--- a/src/main/java/com/marklogic/mgmt/admin/AdminManager.java
+++ b/src/main/java/com/marklogic/mgmt/admin/AdminManager.java
@@ -236,6 +236,15 @@ public String getServerVersion() {
return getServerConfig().getElementValue("/m:host/m:version");
}
+ /**
+ *
+ * @return
+ * @since 4.6.0
+ */
+ public String getServerTimestamp() {
+ return getServerConfig().getElementValue("/m:host/m:timestamp");
+ }
+
public void setWaitForRestartCheckInterval(int waitForRestartCheckInterval) {
this.waitForRestartCheckInterval = waitForRestartCheckInterval;
}
diff --git a/src/main/java/com/marklogic/mgmt/cma/ConfigurationManager.java b/src/main/java/com/marklogic/mgmt/cma/ConfigurationManager.java
index 4017cb2b..62429dc7 100644
--- a/src/main/java/com/marklogic/mgmt/cma/ConfigurationManager.java
+++ b/src/main/java/com/marklogic/mgmt/cma/ConfigurationManager.java
@@ -15,17 +15,20 @@
*/
package com.marklogic.mgmt.cma;
+import com.fasterxml.jackson.databind.JsonNode;
import com.marklogic.mgmt.AbstractManager;
import com.marklogic.mgmt.ManageClient;
import com.marklogic.mgmt.SaveReceipt;
import com.marklogic.rest.util.MgmtResponseErrorHandler;
+import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.util.UriComponentsBuilder;
/**
* This doesn't extend AbstractResourceManager because a configuration isn't really a resource, it's a collection of
* resources.
- *
+ *
* Currently only supports JSON and XML configuration payloads. Not clear yet from the docs on what the format of a
* zip should be. The docs also mention a bunch of request parameters, but the examples don't show what the purpose
* of those are, so those aren't supported yet either.
@@ -48,6 +51,7 @@ protected boolean useSecurityUser() {
/**
* Returns true if the CMA endpoint exists. This temporarily disables logging in MgmtResponseErrorHandler so that
* a client doesn't see the 404 error being logged, which could be mistakenly perceived as a real error.
+ *
* @return
*/
public boolean endpointExists() {
@@ -96,4 +100,19 @@ public SaveReceipt submit(String payload) {
return new SaveReceipt(null, payload, PATH, response);
}
+ /**
+ * @param resourceType
+ * @return a JSON response containing details on each resource of the given type
+ * @since 4.6.0
+ */
+ public ResponseEntity getResourcesAsJson(String resourceType) {
+ String uri = UriComponentsBuilder
+ .fromUri(manageClient.buildUri("/manage/v3"))
+ .queryParam("format", "json")
+ .queryParam("resource-type", resourceType)
+ .encode()
+ .toUriString();
+
+ return manageClient.getRestTemplate().exchange(uri, HttpMethod.GET, null, JsonNode.class);
+ }
}
diff --git a/src/main/java/com/marklogic/mgmt/resource/forests/ForestManager.java b/src/main/java/com/marklogic/mgmt/resource/forests/ForestManager.java
index c957ac0a..112c508c 100644
--- a/src/main/java/com/marklogic/mgmt/resource/forests/ForestManager.java
+++ b/src/main/java/com/marklogic/mgmt/resource/forests/ForestManager.java
@@ -20,6 +20,7 @@
import com.marklogic.mgmt.ManageClient;
import com.marklogic.mgmt.api.API;
import com.marklogic.mgmt.api.forest.Forest;
+import com.marklogic.mgmt.cma.ConfigurationManager;
import com.marklogic.mgmt.mapper.DefaultResourceMapper;
import com.marklogic.mgmt.mapper.ResourceMapper;
import com.marklogic.mgmt.resource.AbstractResourceManager;
@@ -67,11 +68,7 @@ public ForestManager(ManageClient client) {
* @since 4.5.3
*/
public Map> getMapOfPrimaryForests() {
- String uri = UriComponentsBuilder.fromUri(getManageClient().buildUri("/manage/v3"))
- .queryParam("format", "json").queryParam("resource-type", "forest")
- .encode().toUriString();
-
- JsonNode json = getManageClient().getRestTemplate().exchange(uri, HttpMethod.GET, null, JsonNode.class).getBody();
+ JsonNode json = new ConfigurationManager(getManageClient()).getResourcesAsJson("forest").getBody();
// Config is an array of objects, and it will have a single object based on our request.
ArrayNode allPrimaryForests = (ArrayNode) json.get("config").get(0).get("forest");
ResourceMapper mapper = new DefaultResourceMapper(new API(getManageClient()));
diff --git a/src/main/java/com/marklogic/mgmt/resource/restapis/RestApiManager.java b/src/main/java/com/marklogic/mgmt/resource/restapis/RestApiManager.java
index d7e607ce..1cf9fadc 100644
--- a/src/main/java/com/marklogic/mgmt/resource/restapis/RestApiManager.java
+++ b/src/main/java/com/marklogic/mgmt/resource/restapis/RestApiManager.java
@@ -105,13 +105,24 @@ public boolean deleteRestApi(RestApiDeletionRequest request) {
PayloadParser parser = new PayloadParser();
if (request.isIncludeModules()) {
+ boolean includeModules = true;
if (request.isDeleteModulesReplicaForests()) {
- String modulesDatabase = parser.getPayloadFieldValue(payload, "modules-database");
- if (databaseManager.exists(modulesDatabase)) {
+ String modulesDatabase = null;
+ try {
+ modulesDatabase = parser.getPayloadFieldValue(payload, "modules-database");
+ } catch (Exception e) {
+ logger.warn("Unable to get value of `modules-database`; will not be able to delete " +
+ "modules database. This may be expected if the modules database has been set to " +
+ "'filesystem' for the app server. Error: {}", e.getMessage());
+ includeModules = false;
+ }
+ if (modulesDatabase != null && databaseManager.exists(modulesDatabase)) {
databaseManager.deleteReplicaForests(modulesDatabase);
}
}
- path += "include=modules&";
+ if (includeModules) {
+ path += "include=modules&";
+ }
}
if (request.isIncludeContent()) {
diff --git a/src/main/java/com/marklogic/rest/util/MgmtResponseErrorHandler.java b/src/main/java/com/marklogic/rest/util/MgmtResponseErrorHandler.java
index afcbf8bc..976a9cad 100644
--- a/src/main/java/com/marklogic/rest/util/MgmtResponseErrorHandler.java
+++ b/src/main/java/com/marklogic/rest/util/MgmtResponseErrorHandler.java
@@ -18,6 +18,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
+import org.springframework.http.InvalidMediaTypeException;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.HttpClientErrorException;
@@ -49,6 +50,14 @@ public void handleError(ClientHttpResponse response) throws IOException {
logger.error(message);
}
throw ex;
+ } catch (InvalidMediaTypeException ex) {
+ // In at least one scenario - when deleting a REST API server whose modules database has been set to be
+ // the filesystem (which is not a valid setup, but a user may still do it), MarkLogic returns a mime type
+ // containing commas - e.g. "text/plain, application/json". And Spring does not like that and throws this
+ // error. That obscures the actual error. So a runtime exception is thrown with the mime type error but
+ // also the response body from MarkLogic, which will contain the actual error.
+ String body = new String(getResponseBody(response));
+ throw new RuntimeException("Unable to parse mime type: " + ex.getMessage() + "; response body from MarkLogic: " + body);
}
}
diff --git a/src/main/java/com/marklogic/rest/util/RestConfig.java b/src/main/java/com/marklogic/rest/util/RestConfig.java
index ca3b770a..4152e40e 100644
--- a/src/main/java/com/marklogic/rest/util/RestConfig.java
+++ b/src/main/java/com/marklogic/rest/util/RestConfig.java
@@ -125,7 +125,7 @@ public DatabaseClientBuilder newDatabaseClientBuilder() {
@Override
public String toString() {
- return String.format("%s://%shost:%d", getScheme(), getHost(), getPort());
+ return String.format("%s://%s:%d", getScheme(), getHost(), getPort());
}
/**
diff --git a/src/test/java/com/marklogic/appdeployer/AbstractAppDeployerTest.java b/src/test/java/com/marklogic/appdeployer/AbstractAppDeployerTest.java
index 74674978..670360d9 100644
--- a/src/test/java/com/marklogic/appdeployer/AbstractAppDeployerTest.java
+++ b/src/test/java/com/marklogic/appdeployer/AbstractAppDeployerTest.java
@@ -15,12 +15,17 @@
*/
package com.marklogic.appdeployer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.marklogic.appdeployer.command.Command;
+import com.marklogic.appdeployer.command.CommandContext;
import com.marklogic.appdeployer.command.modules.DefaultModulesLoaderFactory;
import com.marklogic.appdeployer.command.modules.LoadModulesCommand;
+import com.marklogic.appdeployer.command.security.GenerateTemporaryCertificateCommand;
import com.marklogic.appdeployer.impl.SimpleAppDeployer;
import com.marklogic.client.ext.modulesloader.impl.DefaultModulesLoader;
import com.marklogic.mgmt.AbstractMgmtTest;
+import com.marklogic.mgmt.resource.appservers.ServerManager;
import com.marklogic.xcc.template.XccTemplate;
import org.junit.jupiter.api.BeforeEach;
@@ -108,4 +113,39 @@ protected LoadModulesCommand buildLoadModulesCommand() {
protected void setConfigBaseDir(String path) {
appConfig.getFirstConfigDir().setBaseDir(new File("src/test/resources/" + path));
}
+
+ /**
+ * Intended to simplify testing app servers that require SSL.
+ */
+ protected final void configureRestServersToRequireSSL() {
+ GenerateTemporaryCertificateCommand gtcc = new GenerateTemporaryCertificateCommand();
+ gtcc.setTemplateIdOrName("sample-app-template");
+ gtcc.execute(new CommandContext(appConfig, manageClient, adminManager));
+
+ ObjectNode payload = new ObjectMapper().createObjectNode()
+ .put("server-name", SAMPLE_APP_NAME)
+ .put("group-name", "Default")
+ .put("ssl-certificate-template", "sample-app-template");
+
+ ServerManager mgr = new ServerManager(manageClient);
+ mgr.save(payload.toString());
+ payload.put("server-name", SAMPLE_APP_NAME + "-test");
+ mgr.save(payload.toString());
+ }
+
+ protected final void configureRestServersToNotRequireSSL() {
+ ObjectNode payload = new ObjectMapper().createObjectNode()
+ .put("server-name", SAMPLE_APP_NAME)
+ .put("group-name", "Default")
+ .put("ssl-certificate-template", "");
+
+ ServerManager mgr = new ServerManager(manageClient);
+ mgr.save(payload.toString());
+ payload.put("server-name", SAMPLE_APP_NAME + "-test");
+ mgr.save(payload.toString());
+ }
+
+ protected final CommandContext newCommandContext() {
+ return new CommandContext(appConfig, manageClient, adminManager);
+ }
}
diff --git a/src/test/java/com/marklogic/appdeployer/DefaultAppConfigFactoryTest.java b/src/test/java/com/marklogic/appdeployer/DefaultAppConfigFactoryTest.java
index 27cdbeae..46db8757 100644
--- a/src/test/java/com/marklogic/appdeployer/DefaultAppConfigFactoryTest.java
+++ b/src/test/java/com/marklogic/appdeployer/DefaultAppConfigFactoryTest.java
@@ -393,6 +393,10 @@ public void mostProperties() {
p.setProperty("mlUpdateMimetypeWhenPropertiesAreEqual", "true");
+ // 4.6.0
+ p.setProperty("mlCascadeCollections", "true");
+ p.setProperty("mlCascadePermissions", "true");
+
sut = new DefaultAppConfigFactory(new SimplePropertySource(p));
AppConfig config = sut.newAppConfig();
@@ -565,6 +569,9 @@ public void mostProperties() {
assertEquals("other-group", map.get("host2"));
assertTrue(config.isUpdateMimetypeWhenPropertiesAreEqual());
+
+ assertTrue(config.isCascadeCollections());
+ assertTrue(config.isCascadePermissions());
}
/**
diff --git a/src/test/java/com/marklogic/appdeployer/command/TestConnectionsTest.java b/src/test/java/com/marklogic/appdeployer/command/TestConnectionsTest.java
new file mode 100644
index 00000000..fd59e050
--- /dev/null
+++ b/src/test/java/com/marklogic/appdeployer/command/TestConnectionsTest.java
@@ -0,0 +1,208 @@
+package com.marklogic.appdeployer.command;
+
+import com.marklogic.appdeployer.AbstractAppDeployerTest;
+import com.marklogic.appdeployer.command.restapis.DeployRestApiServersCommand;
+import com.marklogic.appdeployer.command.security.DeployCertificateTemplatesCommand;
+import com.marklogic.mgmt.ManageClient;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class TestConnectionsTest extends AbstractAppDeployerTest {
+
+ private TestConnectionsCommand command = new TestConnectionsCommand();
+
+ @AfterEach
+ void afterEach() {
+ undeploySampleApp();
+ }
+
+ /**
+ * Multiple scenarios are tested here as they can all use the same deployed app.
+ */
+ @Test
+ void allConnectionsSucceed() {
+ appConfig.setTestRestPort(SAMPLE_APP_TEST_REST_PORT);
+ initializeAppDeployer(new DeployRestApiServersCommand(), new DeployCertificateTemplatesCommand());
+ deploySampleApp();
+
+ verifyAllConnectionsSucceed();
+ verifyConnectionsSucceedWhenRestServersRequireSSL();
+ verifyResultsWhenManageConnectionFails();
+ }
+
+ /**
+ * In this scenario, the 3 REST API app servers don't exist yet - which is fine, no error should be thrown,
+ * we just don't get any test results for them.
+ */
+ @Test
+ void restServersDontExist() {
+ appConfig.setAppServicesPort(SAMPLE_APP_REST_PORT);
+ appConfig.setTestRestPort(SAMPLE_APP_TEST_REST_PORT);
+
+ CommandContext context = new CommandContext(appConfig, manageClient, adminManager);
+ TestConnectionsCommand command = new TestConnectionsCommand();
+ // Smoke test, just expecting logging
+ command.execute(context);
+
+ TestConnectionsCommand.TestResults results = command.testConnections(context);
+ assertFalse(results.anyTestFailed());
+
+ assertTrue(results.getManageTestResult().isSucceeded());
+ assertTrue(results.getAdminTestResult().isSucceeded());
+ assertNull(results.getAppServicesTestResult());
+ assertNull(results.getRestServerTestResult());
+ assertNull(results.getTestRestServerTestResult());
+ }
+
+ /**
+ * In this scenario, the Manage test fails, meaning we can't test any of the REST API app servers since we can't
+ * check to see if they exist or not via the Manage app server.
+ */
+ private void verifyResultsWhenManageConnectionFails() {
+ final String validPassword = manageConfig.getPassword();
+ try {
+ manageConfig.setPassword("Wrong password");
+ TestConnectionsCommand.TestResults results =
+ command.testConnections(new CommandContext(appConfig, new ManageClient(manageConfig), adminManager));
+
+ assertTrue(results.anyTestFailed());
+ assertFalse(results.getManageTestResult().isSucceeded());
+ assertTrue(results.getAdminTestResult().isSucceeded());
+ assertNull(results.getAppServicesTestResult());
+ assertNull(results.getRestServerTestResult());
+ assertNull(results.getTestRestServerTestResult());
+ } finally {
+ manageConfig.setPassword(validPassword);
+ }
+ }
+
+ private void verifyConnectionsSucceedWhenRestServersRequireSSL() {
+ appConfig.setSimpleSslConfig();
+ configureRestServersToRequireSSL();
+ try {
+ TestConnectionsCommand.TestResults results =
+ command.testConnections(new CommandContext(appConfig, manageClient, adminManager));
+ assertFalse(results.anyTestFailed());
+
+ TestConnectionsCommand.TestResult restResult = results.getRestServerTestResult();
+ assertEquals("https", restResult.getScheme());
+ assertTrue(restResult.toString().startsWith(
+ "Configured to connect to https://localhost:8004 using 'digest' authentication and username of 'admin'"),
+ "Unexpected message: " + restResult);
+
+ TestConnectionsCommand.TestResult testRestResult = results.getTestRestServerTestResult();
+ assertEquals("https", testRestResult.getScheme());
+ assertTrue(testRestResult.toString().startsWith(
+ "Configured to connect to https://localhost:8005 using 'digest' authentication and username of 'admin'"),
+ "Unexpected message: " + testRestResult);
+
+ // Disable SSL on the client side and verify we get errors
+ appConfig.setRestSslContext(null);
+ appConfig.setRestTrustManager(null);
+ appConfig.setRestSslHostnameVerifier(null);
+
+ results = command.testConnections(new CommandContext(appConfig, manageClient, adminManager));
+ restResult = results.getRestServerTestResult();
+ assertFalse(restResult.isSucceeded());
+ assertEquals("Received 403: Forbidden", restResult.getMessage(), "Unfortunately, the Java Client receives " +
+ "nothing indicating an SSL issue; the request doesn't even show up in the app server's AccessLog. " +
+ "All the user gets is a 403 back when SSL is required by the client is not using it.");
+ testRestResult = results.getTestRestServerTestResult();
+ assertFalse(testRestResult.isSucceeded());
+ assertEquals("Received 403: Forbidden", testRestResult.getMessage());
+ } finally {
+ configureRestServersToNotRequireSSL();
+ appConfig.setRestSslContext(null);
+ appConfig.setRestTrustManager(null);
+ appConfig.setRestSslHostnameVerifier(null);
+ }
+ }
+
+ private void verifyAllConnectionsSucceed() {
+ CommandContext context = new CommandContext(appConfig, manageClient, adminManager);
+
+ // Smoke test - this just logs text on success.
+ command.execute(context);
+ TestConnectionsCommand.TestResults results = command.testConnections(context);
+ assertFalse(results.anyTestFailed());
+
+ final String host = manageConfig.getHost();
+
+ TestConnectionsCommand.TestResult manageResult = results.getManageTestResult();
+ assertEquals(host, manageResult.getHost());
+ assertEquals(manageConfig.getPort(), manageResult.getPort());
+ assertEquals("http", manageResult.getScheme());
+ assertEquals(manageConfig.getUsername(), manageResult.getUsername());
+ assertEquals(manageConfig.getAuthType(), manageResult.getAuthType());
+ assertTrue(manageResult.isSucceeded());
+ assertTrue(manageResult.getMessage().startsWith("MarkLogic version:"),
+ "Unexpected message; the MarkLogic version should be shown as a quick confirmation of the version of " +
+ "MarkLogic that the user is connecting to; actual message: " + manageResult.getMessage());
+ assertTrue(
+ manageResult.toString().startsWith("Configured to connect to http://localhost:8002 using 'digest' authentication and username of 'admin'"),
+ "Unexpected toString content: " + manageResult
+ );
+
+ TestConnectionsCommand.TestResult adminResult = results.getAdminTestResult();
+ assertEquals(host, adminResult.getHost());
+ assertEquals(adminConfig.getPort(), adminResult.getPort());
+ assertEquals("http", adminResult.getScheme());
+ assertEquals(adminConfig.getUsername(), adminResult.getUsername());
+ assertEquals(adminConfig.getAuthType(), adminResult.getAuthType());
+ assertTrue(adminResult.isSucceeded());
+ assertTrue(adminResult.getMessage().startsWith("MarkLogic server timestamp:"),
+ "Unexpected message; the server timestamp should be shown as a quick confirmation of the timezone that " +
+ "the MarkLogic instance is running in; actual message: " + adminResult.getMessage());
+ assertTrue(
+ adminResult.toString().startsWith("Configured to connect to http://localhost:8001 using 'digest' authentication and username of 'admin'"),
+ "Unexpected toString content: " + adminResult
+ );
+
+ TestConnectionsCommand.TestResult appServicesResult = results.getAppServicesTestResult();
+ assertEquals(host, appServicesResult.getHost());
+ assertEquals(appConfig.getAppServicesPort(), appServicesResult.getPort());
+ assertEquals("http", appServicesResult.getScheme());
+ assertEquals(appConfig.getAppServicesUsername(), appServicesResult.getUsername());
+ assertEquals(appConfig.getAppServicesSecurityContextType().name().toLowerCase(), appServicesResult.getAuthType());
+ assertTrue(appServicesResult.isSucceeded());
+ assertNull(appServicesResult.getMessage(), "The Java Client doesn't provide any success message when " +
+ "checkConnection succeeds.");
+ assertTrue(
+ appServicesResult.toString().startsWith("Configured to connect to http://localhost:8000 using 'digest' authentication and username of 'admin'"),
+ "Unexpected toString content: " + appServicesResult
+ );
+
+ TestConnectionsCommand.TestResult restResult = results.getRestServerTestResult();
+ assertEquals(host, restResult.getHost());
+ assertEquals(appConfig.getRestPort(), restResult.getPort());
+ assertEquals("http", restResult.getScheme());
+ assertEquals(appConfig.getRestAdminUsername(), restResult.getUsername());
+ assertEquals(appConfig.getRestSecurityContextType().name().toLowerCase(), restResult.getAuthType());
+ assertTrue(restResult.isSucceeded());
+ assertNull(restResult.getMessage(), "The Java Client doesn't provide any success message when " +
+ "checkConnection succeeds.");
+ assertTrue(
+ restResult.toString().startsWith("Configured to connect to http://localhost:8004 using 'digest' authentication and username of 'admin'"),
+ "Unexpected toString content: " + restResult
+ );
+
+ TestConnectionsCommand.TestResult testRestResult = results.getTestRestServerTestResult();
+ assertEquals(host, testRestResult.getHost());
+ assertEquals(appConfig.getTestRestPort(), testRestResult.getPort());
+ assertEquals("http", testRestResult.getScheme());
+ assertEquals(appConfig.getRestAdminUsername(), testRestResult.getUsername());
+ assertEquals(appConfig.getRestSecurityContextType().name().toLowerCase(), testRestResult.getAuthType());
+ assertTrue(testRestResult.isSucceeded());
+ assertNull(testRestResult.getMessage(), "The Java Client doesn't provide any success message when " +
+ "checkConnection succeeds.");
+ assertTrue(
+ testRestResult.toString().startsWith("Configured to connect to http://localhost:8005 using 'digest' authentication and username of 'admin'"),
+ "Unexpected toString content: " + testRestResult
+ );
+ }
+}
diff --git a/src/test/java/com/marklogic/appdeployer/command/data/LoadDataTest.java b/src/test/java/com/marklogic/appdeployer/command/data/LoadDataTest.java
index fd553c60..f44b5dca 100644
--- a/src/test/java/com/marklogic/appdeployer/command/data/LoadDataTest.java
+++ b/src/test/java/com/marklogic/appdeployer/command/data/LoadDataTest.java
@@ -20,6 +20,7 @@
import com.marklogic.appdeployer.command.restapis.DeployRestApiServersCommand;
import com.marklogic.client.DatabaseClient;
import com.marklogic.client.document.GenericDocumentManager;
+import com.marklogic.client.ext.file.GenericFileLoader;
import com.marklogic.client.io.DocumentMetadataHandle;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
@@ -27,7 +28,11 @@
import java.io.File;
import java.util.Set;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
public class LoadDataTest extends AbstractAppDeployerTest {
@@ -92,4 +97,18 @@ public void databaseNameIsSet() {
assertEquals(appConfig.getAppServicesPort(), client.getPort());
client.release();
}
+
+ /**
+ * Just verifies the config; we assume that ml-javaclient-util will work properly if cascade is set to true.
+ */
+ @Test
+ void cascadeCollectionsAndPermissions() {
+ appConfig.setCascadePermissions(true);
+ appConfig.setCascadeCollections(true);
+
+ GenericFileLoader loader = (GenericFileLoader) new LoadDataCommand().buildFileLoader(appConfig);
+
+ assertTrue(loader.isCascadeCollections());
+ assertTrue(loader.isCascadePermissions());
+ }
}
diff --git a/src/test/java/com/marklogic/appdeployer/command/modules/DefaultsModulesLoaderFactoryTest.java b/src/test/java/com/marklogic/appdeployer/command/modules/DefaultsModulesLoaderFactoryTest.java
index d971c3f4..b5672632 100644
--- a/src/test/java/com/marklogic/appdeployer/command/modules/DefaultsModulesLoaderFactoryTest.java
+++ b/src/test/java/com/marklogic/appdeployer/command/modules/DefaultsModulesLoaderFactoryTest.java
@@ -21,7 +21,9 @@
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
public class DefaultsModulesLoaderFactoryTest extends AbstractAppDeployerTest {
@@ -43,4 +45,20 @@ public void dontUseHost() {
PropertiesModuleManager manager = (PropertiesModuleManager) loader.getModulesManager();
assertNull(manager.getHost());
}
+
+ @Test
+ void cascadeCollectionsAndPermissions() {
+ DefaultModulesLoader loader = (DefaultModulesLoader) factory.newModulesLoader(appConfig);
+
+ // Should default to false in the 4.x timeframe
+ assertFalse(loader.getAssetFileLoader().isCascadeCollections());
+ assertFalse(loader.getAssetFileLoader().isCascadePermissions());
+
+ appConfig.setCascadeCollections(true);
+ appConfig.setCascadePermissions(true);
+
+ loader = (DefaultModulesLoader) factory.newModulesLoader(appConfig);
+ assertTrue(loader.getAssetFileLoader().isCascadeCollections());
+ assertTrue(loader.getAssetFileLoader().isCascadePermissions());
+ }
}
diff --git a/src/test/java/com/marklogic/appdeployer/command/restapis/DeleteRestApiTest.java b/src/test/java/com/marklogic/appdeployer/command/restapis/DeleteRestApiTest.java
index 18b2a802..13a33337 100644
--- a/src/test/java/com/marklogic/appdeployer/command/restapis/DeleteRestApiTest.java
+++ b/src/test/java/com/marklogic/appdeployer/command/restapis/DeleteRestApiTest.java
@@ -18,9 +18,13 @@
import com.marklogic.appdeployer.AbstractAppDeployerTest;
import com.marklogic.appdeployer.ConfigDir;
import com.marklogic.appdeployer.command.databases.DeployOtherDatabasesCommand;
+import com.marklogic.mgmt.api.API;
+import com.marklogic.mgmt.api.database.Database;
+import com.marklogic.mgmt.api.server.Server;
import com.marklogic.mgmt.resource.appservers.ServerManager;
import com.marklogic.mgmt.resource.databases.DatabaseManager;
import com.marklogic.mgmt.resource.restapis.RestApiManager;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.io.File;
@@ -50,6 +54,28 @@ public void createAndDelete() {
assertFalse(serverMgr.exists(SAMPLE_APP_NAME), "The REST API app server have been deleted");
}
+ @Test
+ @Disabled("This fails due to a server bug - BUG-60358")
+ void deleteWithFilesystemAsModulesDatabase() {
+ initializeAppDeployer(new DeployRestApiServersCommand(true));
+ appDeployer.deploy(appConfig);
+
+ API api = new API(manageClient);
+
+ // Set the modules-database to the "filesystem", which will cause the DELETE to /v1/rest-apis/to fail due to
+ // the server bug.
+ Server server = new Server(api, appConfig.getRestServerName());
+ server.setModulesDatabase("0");
+ server.save();
+
+ try {
+ appDeployer.undeploy(appConfig);
+ } finally {
+ // Delete the modules-database to ensure it's not left around
+ new Database(api, appConfig.getModulesDatabaseName()).delete();
+ }
+ }
+
@Test
public void contentDatabaseCommandAndRestApiCommandConfiguredToDeleteContent() {
DatabaseManager dbMgr = new DatabaseManager(manageClient);
diff --git a/src/test/java/com/marklogic/appdeployer/command/schemas/LoadSchemasTest.java b/src/test/java/com/marklogic/appdeployer/command/schemas/LoadSchemasTest.java
index ea3091b6..3041b927 100644
--- a/src/test/java/com/marklogic/appdeployer/command/schemas/LoadSchemasTest.java
+++ b/src/test/java/com/marklogic/appdeployer/command/schemas/LoadSchemasTest.java
@@ -20,6 +20,7 @@
import com.marklogic.appdeployer.command.databases.DeployOtherDatabasesCommand;
import com.marklogic.client.DatabaseClient;
import com.marklogic.client.document.GenericDocumentManager;
+import com.marklogic.client.ext.schemasloader.impl.DefaultSchemasLoader;
import com.marklogic.client.io.BytesHandle;
import com.marklogic.client.io.DocumentMetadataHandle;
import org.junit.jupiter.api.AfterEach;
@@ -28,7 +29,11 @@
import java.io.File;
import java.io.FileFilter;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
public class LoadSchemasTest extends AbstractAppDeployerTest {
@@ -148,6 +153,9 @@ public void multipleSchemaPaths() {
File projectDir = new File("src/test/resources/schemas-project");
initializeAppConfig(projectDir);
+
+ // Turn off TDE validation, just to verify that the QBV still gets processed
+ appConfig.setTdeValidationEnabled(false);
appConfig.getSchemaPaths().add(new File(projectDir, "src/main/more-schemas").getAbsolutePath());
initializeAppDeployer(new DeployOtherDatabasesCommand(1), new LoadSchemasCommand());
@@ -157,9 +165,30 @@ public void multipleSchemaPaths() {
GenericDocumentManager docMgr = client.newDocumentManager();
assertNotNull(docMgr.exists("/tde/template1.json"));
assertNotNull(docMgr.exists("/tde/template2.json"));
+ assertNotNull(docMgr.exists("/qbv/example.sjs.xml"),
+ "The QBV XML should have been generated, even though TDE validation is disabled.");
+
+ assertTrue(docMgr.readMetadata("/tde/template1.json",
+ new DocumentMetadataHandle()).getCollections().contains("http://marklogic.com/xdmp/tde"));
+ assertTrue(docMgr.readMetadata("/tde/template2.json",
+ new DocumentMetadataHandle()).getCollections().contains("http://marklogic.com/xdmp/tde"));
+ assertTrue(docMgr.readMetadata("/qbv/example.sjs.xml",
+ new DocumentMetadataHandle()).getCollections().contains("http://marklogic.com/xdmp/qbv"));
+ }
+
+ /**
+ * Just verifies the config; we assume that ml-javaclient-util will work properly if cascade is set to true.
+ */
+ @Test
+ void cascadeCollectionsAndPermissions() {
+ appConfig.setCascadePermissions(true);
+ appConfig.setCascadeCollections(true);
+
+ DefaultSchemasLoader loader = (DefaultSchemasLoader) new LoadSchemasCommand().buildSchemasLoader(
+ newCommandContext(), appConfig.newSchemasDatabaseClient(), null);
- assertTrue(docMgr.readMetadata("/tde/template1.json", new DocumentMetadataHandle()).getCollections().contains("http://marklogic.com/xdmp/tde"));
- assertTrue(docMgr.readMetadata("/tde/template2.json", new DocumentMetadataHandle()).getCollections().contains("http://marklogic.com/xdmp/tde"));
+ assertTrue(loader.isCascadeCollections());
+ assertTrue(loader.isCascadePermissions());
}
private Command newCommand() {
diff --git a/src/test/java/com/marklogic/appdeployer/scaffold/GenerateScaffoldTest.java b/src/test/java/com/marklogic/appdeployer/scaffold/GenerateScaffoldTest.java
index b7f01abb..0f6dbd21 100644
--- a/src/test/java/com/marklogic/appdeployer/scaffold/GenerateScaffoldTest.java
+++ b/src/test/java/com/marklogic/appdeployer/scaffold/GenerateScaffoldTest.java
@@ -24,65 +24,121 @@
import com.marklogic.mgmt.resource.databases.DatabaseManager;
import com.marklogic.mgmt.resource.security.RoleManager;
import com.marklogic.mgmt.resource.security.UserManager;
+import org.apache.commons.io.FileUtils;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.File;
+import java.io.IOException;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.*;
public class GenerateScaffoldTest extends AbstractAppDeployerTest {
- @Test
- public void generateScaffoldAndThenDeploy() {
- // Assume this is run out of the main directory, so default to "." and build out src/main etc.
- String path = "src/test/resources/scaffold-test";
- File dir = new File(path);
- dir.delete();
- dir.mkdirs();
-
- ScaffoldGenerator sg = new ScaffoldGenerator();
- sg.generateScaffold(path, appConfig);
+ // Assume this is run out of the main directory, so default to "." and build out src/main etc.
+ final static String ROOT_PATH = "src/test/resources/scaffold-test";
+ final static File dir = new File(ROOT_PATH);
+ final static File configDir = new File(dir, "src/main/ml-config");
+ final static File modulesDir = new File(dir, "src/main/ml-modules");
- assertConfigFilesAreCreated(dir);
- assertModulesFilesAreCreated(dir);
+ @BeforeEach
+ public void beforeEach() throws IOException {
+ if (dir.exists()) {
+ FileUtils.deleteDirectory(dir);
+ }
+ assertTrue(dir.mkdirs());
+ }
- // Now try deploying the app
- appConfig.setConfigDir(new ConfigDir(new File(path, "src/main/ml-config")));
- appConfig.getModulePaths().clear();
- appConfig.getModulePaths().add(path + "/src/main/ml-modules");
+ @Test
+ public void generateScaffoldWithDefaultsAndThenDeploy() {
+ ScaffoldGenerator.AppInputs appInputs = new ScaffoldGenerator.AppInputs(SAMPLE_APP_NAME, true, true);
+ new ScaffoldGenerator().generateScaffold(ROOT_PATH, appInputs);
- initializeAppDeployer(new DeployRestApiServersCommand(), new DeployOtherDatabasesCommand(),
- new DeployUsersCommand(), new DeployRolesCommand(), buildLoadModulesCommand());
- appDeployer.deploy(appConfig);
+ assertConfigFilesAreCreated( true);
+ assertModulesFilesAreCreated(true, true);
+ deployAppUsingAppConfig();
try {
DatabaseManager dbMgr = new DatabaseManager(manageClient);
- assertTrue(dbMgr.exists(appConfig.getContentDatabaseName()));
-
- assertTrue(new UserManager(manageClient).exists("sample-app-reader"));
- assertTrue(new UserManager(manageClient).exists("sample-app-writer"));
- assertTrue(new UserManager(manageClient).exists("sample-app-admin"));
- assertTrue(new RoleManager(manageClient).exists("sample-app-nobody"));
- assertTrue(new RoleManager(manageClient).exists("sample-app-reader"));
- assertTrue(new RoleManager(manageClient).exists("sample-app-writer"));
- assertTrue(new RoleManager(manageClient).exists("sample-app-internal"));
- assertTrue(new RoleManager(manageClient).exists("sample-app-admin"));
+ assertTrue(dbMgr.exists(SAMPLE_APP_NAME + "-content"));
+ assertTrue(dbMgr.exists(SAMPLE_APP_NAME + "-schemas"));
+ assertSecurity(true);
} finally {
undeploySampleApp();
}
}
- private void assertConfigFilesAreCreated(File dir) {
- File configDir = new File(dir, "src/main/ml-config");
+ @Test
+ public void generateScaffoldWithChoicesSetToFalseAndThenDeploy() {
+ ScaffoldGenerator.AppInputs appInputs = new ScaffoldGenerator.AppInputs(SAMPLE_APP_NAME, false, false);
+ new ScaffoldGenerator().generateScaffold(ROOT_PATH, appInputs);
+
+ assertConfigFilesAreCreated(false);
+ assertModulesFilesAreCreated(false, false);
+
+ deployAppUsingAppConfig();
+ try {
+ DatabaseManager dbMgr = new DatabaseManager(manageClient);
+ assertTrue(dbMgr.exists(appConfig.getContentDatabaseName()));
+ assertSecurity(false);
+ } finally {
+ undeploySampleApp();
+ }
+ }
+
+ @Test
+ public void generateScaffoldWithDatabaseNoSecurityAndThenDeploy() {
+ ScaffoldGenerator.AppInputs appInputs = new ScaffoldGenerator.AppInputs(SAMPLE_APP_NAME, true, false);
+ new ScaffoldGenerator().generateScaffold(ROOT_PATH, appInputs);
+
+ assertConfigFilesAreCreated(true);
+ assertModulesFilesAreCreated(true, true);
+
+ deployAppUsingAppConfig();
+ try {
+ DatabaseManager dbMgr = new DatabaseManager(manageClient);
+ assertTrue(dbMgr.exists(SAMPLE_APP_NAME + "-content"));
+ assertTrue(dbMgr.exists(SAMPLE_APP_NAME + "-schemas"));
+ assertSecurity(false);
+ } finally {
+ undeploySampleApp();
+ }
+ }
+
+ private void deployAppUsingAppConfig() {
+ appConfig.setConfigDir(new ConfigDir(new File(ROOT_PATH, "src/main/ml-config")));
+ appConfig.getModulePaths().clear();
+ appConfig.getModulePaths().add(ROOT_PATH + "/src/main/ml-modules");
+ initializeAppDeployer(new DeployRestApiServersCommand(), new DeployOtherDatabasesCommand(),
+ new DeployUsersCommand(), new DeployRolesCommand(), buildLoadModulesCommand());
+ appDeployer.deploy(appConfig);
+ }
+
+ private void assertConfigFilesAreCreated(Boolean restApiExist) {
assertTrue(configDir.exists());
- assertTrue(new File(configDir, "rest-api.json").exists());
- assertTrue(new File(configDir, "databases/content-database.json").exists());
+ assertEquals(restApiExist, new File(configDir, "rest-api.json").exists());
+ assertTrue(new File(configDir, "databases/content-database.json").exists());
+ assertTrue(new File(configDir, "databases/schemas-database.json").exists());
}
- private void assertModulesFilesAreCreated(File dir) {
- File modulesDir = new File(dir, "src/main/ml-modules");
+ private void assertModulesFilesAreCreated(Boolean restPropertiesExist, Boolean sampleOptionsExist) {
assertTrue(modulesDir.exists());
- assertTrue(new File(modulesDir, "rest-properties.json").exists());
- assertTrue(new File(modulesDir, "options/sample-app-options.xml").exists());
+ assertEquals(restPropertiesExist, new File(modulesDir, "rest-properties.json").exists());
+ assertEquals(sampleOptionsExist, new File(modulesDir, "options/sample-app-options.xml").exists());
}
+
+ private void assertSecurity(Boolean shouldExist) {
+ assertEquals(shouldExist, new UserManager(manageClient).exists("sample-app-reader"));
+ assertEquals(shouldExist, new UserManager(manageClient).exists("sample-app-writer"));
+ assertEquals(shouldExist, new UserManager(manageClient).exists("sample-app-admin"));
+ assertEquals(shouldExist, new RoleManager(manageClient).exists("sample-app-nobody"));
+ assertEquals(shouldExist, new RoleManager(manageClient).exists("sample-app-reader"));
+ assertEquals(shouldExist, new RoleManager(manageClient).exists("sample-app-writer"));
+ assertEquals(shouldExist, new RoleManager(manageClient).exists("sample-app-internal"));
+ assertEquals(shouldExist, new RoleManager(manageClient).exists("sample-app-admin"));
+ if (shouldExist) {
+ assertTrue(new RoleManager(manageClient).getPropertiesAsXmlString("sample-app-reader").contains("http://marklogic.com/xdmp/privileges/rest-reader"));
+ assertTrue(new RoleManager(manageClient).getPropertiesAsXmlString("sample-app-writer").contains("http://marklogic.com/xdmp/privileges/rest-writer"));
+ }
+ }
}
diff --git a/src/test/java/com/marklogic/rest/util/RestConfigTest.java b/src/test/java/com/marklogic/rest/util/RestConfigTest.java
index 24a7a006..c0a9acf4 100644
--- a/src/test/java/com/marklogic/rest/util/RestConfigTest.java
+++ b/src/test/java/com/marklogic/rest/util/RestConfigTest.java
@@ -72,4 +72,13 @@ void buildUriWithBasePath() {
config.setBasePath("/secure");
assertEquals("https://somehost:8002/secure/target/path", config.buildUri(targetPath).toString());
}
+
+ @Test
+ void verifyToString() {
+ RestConfig config = new RestConfig("somewhere", 8008, "some", "user");
+ assertEquals("http://somewhere:8008", config.toString());
+
+ config.setScheme("https");
+ assertEquals("https://somewhere:8008", config.toString());
+ }
}
diff --git a/src/test/java/com/marklogic/rest/util/RestTemplateUtilTest.java b/src/test/java/com/marklogic/rest/util/RestTemplateUtilTest.java
index 3aa82e91..238681f2 100644
--- a/src/test/java/com/marklogic/rest/util/RestTemplateUtilTest.java
+++ b/src/test/java/com/marklogic/rest/util/RestTemplateUtilTest.java
@@ -143,7 +143,7 @@ void noUsername() {
RuntimeException ex = assertThrows(RuntimeException.class,
() -> RestTemplateUtil.newRestTemplate(manageConfig));
- assertEquals("Unable to connect to the MarkLogic app server at http://localhosthost:8002; cause: username must be of type String",
+ assertEquals("Unable to connect to the MarkLogic app server at http://localhost:8002; cause: username must be of type String",
ex.getMessage(),
"As of 4.5.0, since auth strategies other than basic/digest are now supported, the error message is expected " +
"to identify which MarkLogic app server is being accessed but not any authentication details. This is " +
diff --git a/src/test/resources/schemas-project/src/main/more-schemas/qbv/example.sjs b/src/test/resources/schemas-project/src/main/more-schemas/qbv/example.sjs
new file mode 100644
index 00000000..b28b2a3d
--- /dev/null
+++ b/src/test/resources/schemas-project/src/main/more-schemas/qbv/example.sjs
@@ -0,0 +1,3 @@
+'use strict';
+const op = require('/MarkLogic/optic');
+op.fromView('Example2', 'default').generateView('qbv', 'example')