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

com.gs.obevo.impl.reader.RerunnableChangeParser Maven / Gradle / Ivy

There is a newer version: 8.2.1
Show newest version
/**
 * Copyright 2017 Goldman Sachs.
 * 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.gs.obevo.impl.reader;

import com.gs.obevo.api.appdata.ArtifactRestrictions;
import com.gs.obevo.api.appdata.Change;
import com.gs.obevo.api.appdata.ChangeRerunnable;
import com.gs.obevo.api.appdata.CodeDependency;
import com.gs.obevo.api.appdata.CodeDependencyType;
import com.gs.obevo.api.appdata.doc.TextMarkupDocument;
import com.gs.obevo.api.appdata.doc.TextMarkupDocumentSection;
import com.gs.obevo.api.platform.ChangeType;
import com.gs.obevo.impl.DeployMetricsCollector;
import com.gs.obevo.impl.DeployMetricsCollectorImpl;
import com.gs.obevo.util.VisibleForTesting;
import com.gs.obevo.util.hash.OldWhitespaceAgnosticDbChangeHashStrategy;
import com.gs.obevo.util.vfs.FileObject;
import org.eclipse.collections.api.bag.MutableBag;
import org.eclipse.collections.api.block.function.Function;
import org.eclipse.collections.api.block.function.Function2;
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.api.list.MutableList;
import org.eclipse.collections.api.set.MutableSet;
import org.eclipse.collections.api.tuple.primitive.ObjectBooleanPair;
import org.eclipse.collections.impl.block.factory.Predicates;
import org.eclipse.collections.impl.block.factory.StringFunctions;
import org.eclipse.collections.impl.block.factory.StringPredicates;
import org.eclipse.collections.impl.block.factory.primitive.IntPredicates;
import org.eclipse.collections.impl.factory.Lists;
import org.eclipse.collections.impl.factory.Sets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RerunnableChangeParser extends AbstractDbChangeFileParser {
    private static final Logger LOG = LoggerFactory.getLogger(RerunnableChangeParser.class);
    private static final String ATTR_ORDER = "order";
    private final OldWhitespaceAgnosticDbChangeHashStrategy contentHashStrategy = new OldWhitespaceAgnosticDbChangeHashStrategy();

    @VisibleForTesting
    public RerunnableChangeParser() {
        this(false, new DeployMetricsCollectorImpl(), new TextMarkupDocumentReader(false));
    }

    public RerunnableChangeParser(boolean backwardsCompatibleMode, DeployMetricsCollector deployMetricsCollector, TextMarkupDocumentReader textMarkupDocumentReader) {
        super(backwardsCompatibleMode, deployMetricsCollector,
                Sets.immutable.with(
                        TextMarkupDocumentReader.ATTR_DEPENDENCIES,
                        TextMarkupDocumentReader.ATTR_EXCLUDE_DEPENDENCIES,
                        TextMarkupDocumentReader.ATTR_INCLUDE_DEPENDENCIES,
                        TextMarkupDocumentReader.TAG_PERM_SCHEME,
                        TextMarkupDocumentReader.EXCLUDE_ENVS,
                        TextMarkupDocumentReader.EXCLUDE_PLATFORMS,
                        TextMarkupDocumentReader.INCLUDE_ENVS,
                        TextMarkupDocumentReader.INCLUDE_PLATFORMS,
                        TextMarkupDocumentReader.ATTR_UPDATE_TIME_COLUMN,
                        TextMarkupDocumentReader.ATTR_PRIMARY_KEYS,
                        ATTR_ORDER
                ),
                Sets.immutable.with(
                        TextMarkupDocumentReader.TOGGLE_DISABLE_QUOTED_IDENTIFIERS
                ),
                Sets.immutable.with(TextMarkupDocumentReader.TAG_CHANGE),
                textMarkupDocumentReader
        );
    }

    @Override
    public ImmutableList value(ChangeType changeType, FileObject file, String fileContent, String objectName, String schema, TextMarkupDocumentSection packageMetadata) {
        ObjectBooleanPair docStatusPair = readDocument(fileContent, packageMetadata);
        TextMarkupDocument doc = docStatusPair.getOne();
        boolean backwardsCompatibleModeUsed = docStatusPair.getTwo();

        TextMarkupDocumentSection mainSection = getMainSection(doc, backwardsCompatibleModeUsed);
        String contentToUse = mainSection.getContent();

        MutableList sections = Lists.mutable.empty();

        TextMarkupDocumentSection dropCommandSection = doc.findSectionWithElementName(TextMarkupDocumentReader.TAG_DROP_COMMAND);
        TextMarkupDocumentSection metadata = getOrCreateMetadataNode(doc);
        String permissionScheme = getPermissionSchemeValue(doc);
        ChangeRerunnable change = createRerunnableChange(changeType, file, objectName, schema, contentToUse, metadata, permissionScheme);
        change.setDropContent(dropCommandSection != null ? dropCommandSection.getContent() : null);
        sections.add(change);

        TextMarkupDocumentSection bodySection = doc.findSectionWithElementName(TextMarkupDocumentReader.TAG_BODY);
        if (bodySection != null) {
            ChangeType bodyChangeType = changeType.getBodyChangeType();
            if (bodyChangeType == null) {
                throw new IllegalArgumentException("Cannot specify a //// " + TextMarkupDocumentReader.TAG_BODY + " section for an object type without a body type configured: [" + changeType + "]");
            }
            ChangeRerunnable bodyChange = createRerunnableChange(bodyChangeType, file, objectName, schema, bodySection.getContent(), bodySection, permissionScheme);
            bodyChange.setChangeName("body");
            sections.add(bodyChange);
        }

        return sections.toImmutable();
    }

    private ChangeRerunnable createRerunnableChange(ChangeType changeType, FileObject file, String objectName, String schema, String contentToUse, TextMarkupDocumentSection metadata, String permissionScheme) {
        ChangeRerunnable change = new ChangeRerunnable(changeType
                , schema
                , objectName
                , contentHashStrategy.hashContent(contentToUse)
                , contentToUse);

        ImmutableList restrictions = new DbChangeRestrictionsReader().valueOf(metadata);
        change.setRestrictions(restrictions);

        String dependenciesStr = metadata.getAttr(TextMarkupDocumentReader.ATTR_DEPENDENCIES);
        String excludeDependenciesStr = metadata.getAttr(TextMarkupDocumentReader.ATTR_EXCLUDE_DEPENDENCIES);
        String includeDependenciesStr = metadata.getAttr(TextMarkupDocumentReader.ATTR_INCLUDE_DEPENDENCIES);

        if (dependenciesStr != null && (excludeDependenciesStr != null || includeDependenciesStr != null)) {
            throw new IllegalArgumentException(String.format("Cannot specify the %1%s attribute with either the %2$s or %3$s attributes; either go w/ just %1$s or a combo of %2$s and %3$s or neither",
                    TextMarkupDocumentReader.ATTR_DEPENDENCIES,
                    TextMarkupDocumentReader.ATTR_EXCLUDE_DEPENDENCIES,
                    TextMarkupDocumentReader.ATTR_INCLUDE_DEPENDENCIES));
        }

        if (dependenciesStr != null) {
            change.setCodeDependencies(Sets.immutable.with(dependenciesStr.split(",")).reject(StringPredicates.empty()).collectWith(new Function2() {
                @Override
                public CodeDependency value(String target, CodeDependencyType codeDependencyType) {
                    return new CodeDependency(target, codeDependencyType);
                }
            }, CodeDependencyType.EXPLICIT));
        } else {
            if (excludeDependenciesStr != null) {
                change.setExcludeDependencies(Sets.immutable.with(excludeDependenciesStr.split(",")).reject(StringPredicates.empty()));
            }
            if (includeDependenciesStr != null) {
                change.setIncludeDependencies(Sets.immutable.with(includeDependenciesStr.split(",")).reject(StringPredicates.empty()));
            }
        }

        String orderStr = metadata.getAttr(ATTR_ORDER);
        if (orderStr != null) {
            change.setOrder(Integer.valueOf(orderStr));
        }

        change.setPermissionScheme(permissionScheme);
        change.setMetadataSection(metadata);

        change.setFileLocation(file);
        return change;
    }

    @Override
    protected void validateStructureOld(TextMarkupDocument doc) {
        final MutableSet foundSections = doc.getSections().collect(new Function() {
            @Override
            public String valueOf(TextMarkupDocumentSection section) {
                return section.getName();
            }
        }).toSet().reject(Predicates.isNull());
        final MutableSet expectedSections = Sets.mutable.with(TextMarkupDocumentReader.TAG_METADATA, TextMarkupDocumentReader.TAG_BODY, TextMarkupDocumentReader.TAG_DROP_COMMAND);
        final MutableSet extraSections = foundSections.difference(expectedSections);
        if (extraSections.notEmpty()) {
            throw new IllegalArgumentException("Unexpected sections found: " + extraSections + "; only expecting: " + expectedSections);
        }
    }

    @Override
    protected void validateStructureNew(TextMarkupDocument doc) {
        String allowedSectionString = Sets.immutable.with(TextMarkupDocumentReader.TAG_METADATA, TextMarkupDocumentReader.TAG_BODY, TextMarkupDocumentReader.TAG_DROP_COMMAND).collect(StringFunctions.prepend("//// ")).makeString(", ");

        ImmutableList docSections = doc.getSections();
        if (docSections.isEmpty()) {
            throw new IllegalArgumentException("No content defined");
        }

        ImmutableList disallowedSections = docSections.reject(Predicates.attributeIn(new Function() {
            @Override
            public Object valueOf(TextMarkupDocumentSection section) {
                return section.getName();
            }
        }, Sets.immutable.with(null, TextMarkupDocumentReader.TAG_METADATA, TextMarkupDocumentReader.TAG_DROP_COMMAND, TextMarkupDocumentReader.TAG_BODY)));
        if (disallowedSections.notEmpty()) {
            throw new IllegalArgumentException("Only allowed 1 content section and at most 1 of these [" + allowedSectionString + "]; instead, found these disallowed sections: " + disallowedSections);
        }

        ImmutableList sectionNames = docSections.collect(new Function() {
            @Override
            public String valueOf(TextMarkupDocumentSection section) {
                return section.getName();
            }
        });
        MutableBag duplicateSections = sectionNames.toBag().selectByOccurrences(IntPredicates.greaterThan(1));
        if (duplicateSections.notEmpty()) {
            throw new IllegalArgumentException("Only allowed 1 content section and at most 1 of these [" + allowedSectionString + "]; instead, found these extra sections instances: " + duplicateSections.toSet());
        }

        int metadataIndex = sectionNames.indexOf(TextMarkupDocumentReader.TAG_METADATA);
        int contentIndex = sectionNames.indexOf(null);
        int dropIndexIndex = sectionNames.indexOf(TextMarkupDocumentReader.TAG_DROP_COMMAND);

        if (metadataIndex != -1 && contentIndex != -1 && metadataIndex > contentIndex) {
            throw new IllegalArgumentException("Improper section ordering: " + TextMarkupDocumentReader.TAG_METADATA + " section must come before the content section");
        } else if (contentIndex != -1 && dropIndexIndex != -1 && contentIndex > dropIndexIndex) {
            throw new IllegalArgumentException("Improper section ordering: content section must come before the " + TextMarkupDocumentReader.TAG_DROP_COMMAND + " section");
        }
    }

    private TextMarkupDocumentSection getMainSection(TextMarkupDocument doc, boolean backwardsCompatibleModeUsed) {
        return backwardsCompatibleModeUsed ? getMainSectionOld(doc) : getMainSectionNew(doc);
    }

    private TextMarkupDocumentSection getMainSectionNew(TextMarkupDocument doc) {
        return doc.findSectionWithElementName(null);
    }

    private TextMarkupDocumentSection getMainSectionOld(TextMarkupDocument doc) {
        return doc.getSections().get(0);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy