All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.marklogic.hub.deploy.commands.DeployDatabaseFieldCommand Maven / Gradle / Ivy

There is a newer version: 6.1.1
Show newest version
/*
 * Copyright (c) 2021 MarkLogic Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.marklogic.hub.deploy.commands;

import com.marklogic.appdeployer.command.AbstractResourceCommand;
import com.marklogic.appdeployer.command.CommandContext;
import com.marklogic.appdeployer.command.SortOrderConstants;
import com.marklogic.mgmt.ManageClient;
import com.marklogic.mgmt.resource.ResourceManager;
import com.marklogic.mgmt.resource.databases.DatabaseManager;
import com.marklogic.rest.util.Fragment;
import org.apache.commons.lang3.StringUtils;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.output.XMLOutputter;
import org.springframework.http.ResponseEntity;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * Per DHFPROD-2554, this command ensures that when it adds DHF-specific fields and range field indexes to the
 * staging, final, and jobs databases, it will not drop any existing fields or range field indexes.
 * 

* The reason that these fields are applied via an XML file is to avoid a bug specific to JSON files and the Manage * API that is not yet fixed in ML 9.0-9. */ public class DeployDatabaseFieldCommand extends AbstractResourceCommand { private final static Namespace MANAGE_NS = Namespace.getNamespace("http://marklogic.com/manage"); public DeployDatabaseFieldCommand() { setExecuteSortOrder(SortOrderConstants.DEPLOY_OTHER_DATABASES + 1); } @Override protected File[] getResourceDirs(CommandContext context) { return findResourceDirs(context, configDir -> new File(configDir.getBaseDir(), "database-fields")); } @Override protected ResourceManager getResourceManager(CommandContext commandContext) { return new HubDatabaseManager(commandContext.getManageClient()); } @Override protected String adjustPayloadBeforeSavingResource(CommandContext context, File f, String payload) { payload = super.adjustPayloadBeforeSavingResource(context, f, payload); return addExistingFieldsAndRangeFieldIndexes(payload, new DatabaseManager(context.getManageClient())); } @Override public void undo(CommandContext context) { //no-op as deleting database will delete all the fields and indexes associated with it. logger.info("No action required on undeploy, as the command for deleting databases on undeploy will also delete " + "the fields and indexes created by this command."); } protected static String addExistingFieldsAndRangeFieldIndexes(String payload, ResourceManager dbManager) { Fragment newProps = new Fragment(payload); Fragment existingProps = dbManager.getPropertiesAsXml(newProps.getElementValue("/node()/m:database-name")); addExistingFields(newProps, existingProps); addExistingRangeFieldIndexes(newProps, existingProps); addExistingRangePathIndexes(newProps, existingProps); addExistingPathNamespaces(newProps, existingProps); return newProps.getPrettyXml(); } protected static void addExistingFields(Fragment newProps, Fragment existingProps) { Element newFields = newProps.getInternalDoc().getRootElement().getChild("fields", MANAGE_NS); if (newFields != null) { List newFieldNames = new ArrayList<>(); newProps.getElements("/m:database-properties/m:fields/m:field").forEach(field -> { String name = field.getChildText("field-name", MANAGE_NS); if (StringUtils.isNotBlank(name)) { newFieldNames.add(name); } }); for (Element field : existingProps.getElements("/m:database-properties/m:fields/m:field")) { String name = field.getChildText("field-name", MANAGE_NS); if (StringUtils.isNotBlank(name) && !newFieldNames.contains(name)) { newFields.addContent(field.detach()); } } } } protected static void addExistingRangeFieldIndexes(Fragment newProps, Fragment existingProps) { Element newRangeFieldIndexes = newProps.getInternalDoc().getRootElement().getChild("range-field-indexes", MANAGE_NS); if (newRangeFieldIndexes != null) { List newIndexFieldNames = new ArrayList<>(); newProps.getElements("/m:database-properties/m:range-field-indexes/m:range-field-index").forEach(index -> { String name = index.getChildText("field-name", MANAGE_NS); if (StringUtils.isNotBlank(name)) { newIndexFieldNames.add(name); } }); for (Element index : existingProps.getElements("/m:database-properties/m:range-field-indexes/m:range-field-index")) { String name = index.getChildText("field-name", MANAGE_NS); if (StringUtils.isNotBlank(name) && !newIndexFieldNames.contains(name)) { newRangeFieldIndexes.addContent(index.detach()); } } } } protected static void addExistingRangePathIndexes(Fragment newProps, Fragment existingProps) { Element newRangePathIndexes = newProps.getInternalDoc().getRootElement().getChild("range-path-indexes", MANAGE_NS); if (newRangePathIndexes != null) { List newIndexPathExpressions = new ArrayList<>(); newProps.getElements("/m:database-properties/m:range-path-indexes/m:range-path-index").forEach(index -> { String pathExpression = index.getChildText("path-expression", MANAGE_NS); if (StringUtils.isNotBlank(pathExpression)) { newIndexPathExpressions.add(pathExpression); } }); for (Element index : existingProps.getElements("/m:database-properties/m:range-path-indexes/m:range-path-index")) { String pathExpression = index.getChildText("path-expression", MANAGE_NS); if (StringUtils.isNotBlank(pathExpression) && !newIndexPathExpressions.contains(pathExpression)) { newRangePathIndexes.addContent(index.detach()); } } } } protected static void addExistingPathNamespaces(Fragment newProps, Fragment existingProps) { Element newNamespaces = newProps.getInternalDoc().getRootElement().getChild("path-namespaces", MANAGE_NS); if (newNamespaces != null) { List newNamespacePrefixes = new ArrayList<>(); newProps.getElements("/m:database-properties/m:path-namespaces/m:path-namespace").forEach(namespace -> newNamespacePrefixes.add(namespace.getChildText("prefix", MANAGE_NS)) ); for (Element namespace : existingProps.getElements("/m:database-properties/m:path-namespaces/m:path-namespace")) { String prefix = namespace.getChildText("prefix", MANAGE_NS); if (!newNamespacePrefixes.contains(prefix)) { newNamespaces.addContent(namespace.detach()); } } } } public static class HubDatabaseManager extends DatabaseManager { public HubDatabaseManager(ManageClient manageClient) { super(manageClient); } protected String getResourceName() { return "database"; } @Override protected ResponseEntity putPayload(ManageClient client, String path, String payload) { if (!payloadParser.isJsonPayload(payload)) { payload = removeDatabaseNameFromXmlPayload(payload); } return super.putPayload(client, path, payload); } /** * The database-name is not required, and for a data-hub-developer, the inclusion of it will cause a * Forbidden error from the Manage API. * * @param payload * @return */ protected static String removeDatabaseNameFromXmlPayload(String payload) { Fragment frag = new Fragment(payload); List elements = frag.getElements("/node()/m:database-name"); if (elements != null) { // Should only be one of these, of course elements.forEach(Element::detach); } return new XMLOutputter().outputString(frag.getInternalDoc()); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy