org.squashtest.tm.service.internal.batchimport.EntityValidator.java Source code

Java tutorial

Introduction

Here is the source code for org.squashtest.tm.service.internal.batchimport.EntityValidator.java

Source

/**
 *     This file is part of the Squashtest platform.
 *     Copyright (C) 2010 - 2016 Henix, henix.fr
 *
 *     See the NOTICE file distributed with this work for additional
 *     information regarding copyright ownership.
 *
 *     This is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     this software is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with this software.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.squashtest.tm.service.internal.batchimport;

import org.apache.commons.lang3.StringUtils;
import org.squashtest.tm.core.foundation.lang.PathUtils;
import org.squashtest.tm.domain.infolist.InfoListItem;
import org.squashtest.tm.domain.library.LibraryNode;
import org.squashtest.tm.domain.requirement.RequirementVersion;
import org.squashtest.tm.domain.testcase.Parameter;
import org.squashtest.tm.domain.testcase.ParameterAssignationMode;
import org.squashtest.tm.domain.testcase.TestCase;
import org.squashtest.tm.domain.testcase.TestStep;
import org.squashtest.tm.service.importer.*;
import org.squashtest.tm.service.infolist.InfoListItemFinderService;
import org.squashtest.tm.service.internal.batchimport.testcase.excel.StepSheetColumn;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.squashtest.tm.service.internal.batchimport.Existence.NOT_EXISTS;
import static org.squashtest.tm.service.internal.batchimport.Existence.TO_BE_DELETED;
import static org.squashtest.tm.service.internal.batchimport.requirement.excel.RequirementSheetColumn.*;
import static org.squashtest.tm.service.internal.batchimport.testcase.excel.TestCaseSheetColumn.TC_NAME;
import static org.squashtest.tm.service.internal.batchimport.testcase.excel.TestCaseSheetColumn.TC_REFERENCE;

class EntityValidator {

    private final ValidationFacilitySubservicesProvider subservicesProvider;

    Model getModel() {
        return subservicesProvider.getModel();
    }

    InfoListItemFinderService getInfoListItemService() {
        return subservicesProvider.getInfoListItemService();
    }

    public EntityValidator(ValidationFacilitySubservicesProvider modelProvider) {
        super();
        this.subservicesProvider = modelProvider;
    }

    /**
     * Prerforms Test Case entity check before modifying a test case.
     *
     * It checks : - the path is well formed (failure) - the test case has a name (failure) - the test case name has
     * length between 0 and 255 - the project exists (failure) - the size of fields that are restricted in size
     * (warning)
     *
     * @param target
     * @param testCase
     * @return
     */
    public LogTrain updateTestCaseChecks(TestCaseTarget target, TestCase testCase) {

        LogTrain logs = createTestCaseChecks(target, testCase);

        // 1 - name must be supplied
        String name = testCase.getName();
        if (StringUtils.isBlank(name)) {
            logs.addEntry(LogEntry.failure().forTarget(target)
                    .withMessage(Messages.ERROR_FIELD_MANDATORY, TC_NAME.header).build());
        }

        // 2 - natures and types now
        logs.append(checkNatureAndTypeAndFixIfNeeded(target, testCase));

        return logs;
    }

    /**
     * Performs Test Case entity check before creating a test case.
     *
     * @param target
     * @param testCase
     * @return
     */
    public LogTrain createTestCaseChecks(TestCaseTarget target, TestCase testCase) {
        String name = testCase.getName();
        LogTrain logs = new LogTrain();

        // 1 - path must be supplied and and well formed
        if (!target.isWellFormed()) {
            logs.addEntry(LogEntry.failure().forTarget(target)
                    .withMessage(Messages.ERROR_MALFORMED_PATH, target.getPath()).build());
        }

        // 3 - the project actually exists
        if (target.isWellFormed()) {
            TargetStatus projectStatus = getModel().getProjectStatus(target.getProject());
            if (projectStatus.getStatus() != Existence.EXISTS) {
                logs.addEntry(
                        LogEntry.failure().forTarget(target).withMessage(Messages.ERROR_PROJECT_NOT_EXIST).build());
            }
        }

        // 4 - name has length between 0 and 255
        if (name != null && name.length() > LibraryNode.MAX_NAME_SIZE) {
            logs.addEntry(LogEntry.warning().forTarget(target).withMessage(Messages.ERROR_MAX_SIZE, TC_NAME.header)
                    .withImpact(Messages.IMPACT_MAX_SIZE).build());
        }

        // 5 - reference, if exists, has length between 0 and 50
        String reference = testCase.getReference();
        if (!StringUtils.isBlank(reference) && reference.length() > TestCase.MAX_REF_SIZE) {
            logs.addEntry(
                    LogEntry.warning().forTarget(target).withMessage(Messages.ERROR_MAX_SIZE, TC_REFERENCE.header)
                            .withImpact(Messages.IMPACT_MAX_SIZE).build());
        }

        // 6 - natures and types now
        logs.append(checkNatureAndTypeAndFixIfNeeded(target, testCase));

        return logs;
    }

    /**
     * those checks are run for a test step for any type of operations.
     *
     * It checks : - the path of the test case is well formed (failure) - the project exists (failure) - the format of
     * the custom fields (lists, dates and checkbox) (warning)
     *
     *
     *
     * @param target
     * @return
     */
    LogTrain basicTestStepChecks(TestStepTarget target) {

        LogTrain logs = new LogTrain();

        TestCaseTarget testCase = target.getTestCase();

        // 1 - test case owner path must be supplied and and well formed
        if (!testCase.isWellFormed()) {
            logs.addEntry(new LogEntry(target, ImportStatus.FAILURE, Messages.ERROR_MALFORMED_PATH,
                    new String[] { testCase.getPath() }));
        }

        // 2 - the test case must exist
        TargetStatus tcStatus = getModel().getStatus(testCase);
        if (tcStatus.status == TO_BE_DELETED || tcStatus.status == NOT_EXISTS) {
            logs.addEntry(LogEntry.failure().forTarget(target).withMessage(Messages.ERROR_TC_NOT_FOUND).build());
        }

        // 3 - the project actually exists
        if (target.isWellFormed()) {
            TargetStatus projectStatus = getModel().getProjectStatus(target.getProject());
            if (projectStatus.getStatus() != Existence.EXISTS) {
                logs.addEntry(
                        LogEntry.failure().forTarget(target).withMessage(Messages.ERROR_PROJECT_NOT_EXIST).build());
            }
        }

        return logs;

    }

    LogTrain basicTestStepChecks(TestStepTarget target, TestStep testStep) {

        // for now nothing much more to do with the TestStep
        return basicTestStepChecks(target);

    }

    LogTrain validateCallStep(TestStepTarget target, TestStep testStep, TestCaseTarget calledTestCase,
            CallStepParamsInfo paramInfos, ImportMode mode) {

        LogTrain logs = new LogTrain();

        // 1 - the target must exist and be valid
        String errorMessage = checkTestCaseExists(calledTestCase);

        if (errorMessage != null) {
            logMustExistAndBeValidCalledTest(target, mode, logs, errorMessage);
        } else {
            // 2 - there must be no cyclic calls
            if (getModel().wouldCreateCycle(target, calledTestCase)) {
                logs.addEntry(new LogEntry(target, ImportStatus.FAILURE, Messages.ERROR_CYCLIC_STEP_CALLS,
                        new Object[] { target.getTestCase().getPath(), calledTestCase.getPath() }));
            }

            // 3 - check a called dataset
            if (paramInfos.getParamMode() == ParameterAssignationMode.CALLED_DATASET) {
                String dsname = paramInfos.getCalledDatasetName();

                // 3.1 - if a dataset is specified, the name must not exceed the max limit
                if (dsname.length() > FacilityImplHelper.STD_TRUNCATE_SIZE) {
                    LogEntry entry = LogEntry.warning().forTarget(target)
                            .withMessage(Messages.ERROR_MAX_SIZE, StepSheetColumn.TC_STEP_CALL_DATASET.name())
                            .withImpact(Messages.IMPACT_MAX_SIZE).build();

                    logs.addEntry(entry);
                }

                // 3.2 - if a dataset is specified, it must be owned by the called test case
                DatasetTarget dsTarget = new DatasetTarget(calledTestCase, dsname);
                if (!getModel().doesDatasetExists(dsTarget)) {
                    logs.addEntry(
                            LogEntry.warning().forTarget(target).withMessage(Messages.ERROR_DATASET_NOT_FOUND_ST)
                                    .withImpact(Messages.IMPACT_NO_CALL_DATASET).build());
                }
            }
        }

        return logs;

    }

    private String checkTestCaseExists(TestCaseTarget calledTestCase) {

        TargetStatus calledStatus = getModel().getStatus(calledTestCase);

        String mustExistAndBeValidMessage = null;
        if (calledStatus.status == NOT_EXISTS || calledStatus.status == TO_BE_DELETED) {
            mustExistAndBeValidMessage = Messages.ERROR_CALLED_TC_NOT_FOUND;
        } else if (!calledTestCase.isWellFormed()) {
            mustExistAndBeValidMessage = Messages.ERROR_CALLED_STEP_WRONG_FORMAT;
        }

        return mustExistAndBeValidMessage;

    }

    //********************* REQUIREMENTS CHECKS **********************//

    public LogTrain createRequirementVersionChecks(RequirementVersionTarget target, RequirementVersion reqVersion) {
        LogTrain logs = new LogTrain();
        return basicReqVersionTests(target, reqVersion, logs);
    }

    public LogTrain updateRequirementChecks(RequirementVersionTarget target, RequirementVersion reqVersion) {
        LogTrain logs = new LogTrain();
        // 1 - For update checking if requirement version number value isn't empty and > 0
        checkRequirementVersionNumber(target, logs);
        if (logs.hasCriticalErrors()) {
            return logs;
        }
        basicReqVersionTests(target, reqVersion, logs);
        return logs;
    }

    private void checkRequirementVersionNumber(RequirementVersionTarget target, LogTrain logs) {
        if (target.getVersion() == null || target.getVersion() < 1) {
            logs.addEntry(LogEntry.failure().forTarget(target)
                    .withMessage(Messages.ERROR_REQUIREMENT_VERSION_INVALID).build());
        }
    }

    /**
     * Basics check commons to create and update requirements
     * @param target
     * @param reqVersion
     * @param logs
     * @return
     */
    private LogTrain basicReqVersionTests(RequirementVersionTarget target, RequirementVersion reqVersion,
            LogTrain logs) {
        checkMalformedPath(target, logs);
        if (logs.hasCriticalErrors()) {
            return logs;
        }
        checkProjectExists(target, logs);
        checkVersionPath(target, logs);
        checkVersionName(target, reqVersion, logs);
        checkVersionReference(target, reqVersion, logs);
        logs.append(checkCategoryAndFixIfNeeded(target, reqVersion));
        return logs;

    }

    /**
     * Check path to ensure that all element in path are not too long. Truncate if needed...
     * @param target
     * @param logs
     */
    private void checkVersionPath(RequirementVersionTarget target, LogTrain logs) {
        if (!target.isWellFormed()) {
            return;
        }

        boolean hasTruncate = false;

        String path = target.getRequirement().getPath();
        String[] names = PathUtils.splitPath(path);
        for (int i = 1; i < names.length; i++) {//begin to 1 as first split is project name
            String name = names[i];
            if (name.length() > LibraryNode.MAX_NAME_SIZE) {
                names[i] = StringUtils.abbreviate(name, LibraryNode.MAX_NAME_SIZE);
                hasTruncate = true;
            }
        }

        if (hasTruncate) {
            logs.addEntry(LogEntry.warning().forTarget(target).withMessage(Messages.ERROR_MAX_SIZE, REQ_PATH.header)
                    .build());
            rebuildPathAfterTrucate(target, names);
        }

    }

    private void rebuildPathAfterTrucate(RequirementVersionTarget target, String[] names) {
        target.getRequirement().setPath(PathUtils.buildPathFromParts(names));
    }

    private void checkVersionReference(RequirementVersionTarget target, RequirementVersion reqVersion,
            LogTrain logs) {
        String reference = reqVersion.getReference();
        if (!StringUtils.isBlank(reference) && reference.length() > RequirementVersion.MAX_REF_SIZE) {
            logs.addEntry(LogEntry.warning().forTarget(target)
                    .withMessage(Messages.ERROR_MAX_SIZE, REQ_VERSION_REFERENCE.header)
                    .withImpact(Messages.IMPACT_MAX_SIZE).build());
        }
    }

    private void checkVersionName(RequirementVersionTarget target, RequirementVersion reqVersion, LogTrain logs) {
        String name = reqVersion.getName();
        if (name != null && name.length() > LibraryNode.MAX_NAME_SIZE) {
            reqVersion.setName(StringUtils.abbreviate(name, LibraryNode.MAX_NAME_SIZE));
            logs.addEntry(LogEntry.warning().forTarget(target)
                    .withMessage(Messages.ERROR_MAX_SIZE, REQ_VERSION_NAME.header)
                    .withImpact(Messages.IMPACT_MAX_SIZE).build());
        }
    }

    private void checkProjectExists(RequirementVersionTarget target, LogTrain logs) {
        if (target.isWellFormed()) {
            TargetStatus projectStatus = getModel().getProjectStatus(target.getProject());
            if (projectStatus.getStatus() != Existence.EXISTS) {
                logs.addEntry(
                        LogEntry.failure().forTarget(target).withMessage(Messages.ERROR_PROJECT_NOT_EXIST).build());
            }
        }
    }

    private void checkMalformedPath(RequirementVersionTarget target, LogTrain logs) {
        if (!target.isWellFormed() || pathHasEmptyParts(target.getPath())) {
            logs.addEntry(LogEntry.failure().forTarget(target)
                    .withMessage(Messages.ERROR_MALFORMED_PATH, target.getPath()).build());
        }
    }

    private boolean pathHasEmptyParts(String path) {
        String[] splits = PathUtils.splitPath(path);
        for (String split : splits) {
            if (split.length() == 0) {
                return true;
            }
        }
        return false;
    }

    private void logMustExistAndBeValidCalledTest(TestStepTarget target, ImportMode mode, LogTrain logs,
            String message) {
        switch (mode) {
        case CREATE:
            logs.addEntry(LogEntry.warning().forTarget(target).withMessage(message)
                    .withImpact(Messages.IMPACT_CALL_AS_ACTION_STEP).build());
            break;
        case UPDATE: // do default
        default:
            logs.addEntry(LogEntry.failure().forTarget(target).withMessage(message).build());
            break;
        }
    }

    LogTrain basicParameterChecks(ParameterTarget target) {
        String[] fieldPathErrorArgs = new String[] { "TC_OWNER_PATH" }; // that variable is simple convenience for
        // logging
        return basicParameterChecks(target, fieldPathErrorArgs, Messages.ERROR_PARAMETER_OWNER_NOT_FOUND);
    }

    public LogTrain basicParameterValueChecks(ParameterTarget target) {

        String[] fieldPathErrorArgs = new String[] { "TC_PARAMETER_OWNER_PATH" }; // that variable is simple convenience
        // for
        // logging
        return basicParameterChecks(target, fieldPathErrorArgs, Messages.ERROR_DATASET_PARAM_OWNER_NOT_FOUND);

    }

    private LogTrain basicParameterChecks(ParameterTarget target, String[] fieldPathErrorArgs,
            String ownerNotFoundMessage) {
        LogTrain logs = new LogTrain();
        String[] fieldNameErrorArgs = new String[] { "TC_PARAM_NAME" }; // that variable is simple convenience for

        // logging
        TestCaseTarget testCase = target.getOwner();

        basicTestCaseTargetCheck(testCase, logs, fieldPathErrorArgs, ownerNotFoundMessage, target);

        basicParameterChecksValidateName(target, logs, fieldNameErrorArgs);

        return logs;
    }

    LogTrain basicDatasetCheck(DatasetTarget target) {

        LogTrain logs = new LogTrain();
        String[] fieldNameErrorArgs = new String[] { "TC_DATASET_NAME" }; // that variable is simple convenience for
        // logging

        TestCaseTarget testCase = target.getTestCase();

        basicTestCaseTargetCheck(testCase, logs, new String[] { testCase.getPath() }, Messages.ERROR_TC_NOT_FOUND,
                target);

        // 4 - name has length between 1 and 255
        String name = target.getName();
        if (name != null && name.length() > 255) {

            logs.addEntry(
                    LogEntry.warning().forTarget(target).withMessage(Messages.ERROR_MAX_SIZE, fieldNameErrorArgs)
                            .withImpact(Messages.IMPACT_MAX_SIZE).build());
        }
        if (StringUtils.isBlank(name)) {
            logs.addEntry(LogEntry.failure().forTarget(target)
                    .withMessage(Messages.ERROR_FIELD_MANDATORY, fieldNameErrorArgs).build());
        }

        return logs;

    }

    // ************************* private stuffs ****************************************

    private <T extends Target & WithPath> void basicTestCaseTargetCheck(TestCaseTarget testCase, LogTrain logs,
            String[] fieldPathErrorArgs, String tcNotFoundMessage, T target) {
        // 1 - test case owner path must be supplied and and well formed
        if (!testCase.isWellFormed()) {
            logs.addEntry(
                    new LogEntry(target, ImportStatus.FAILURE, Messages.ERROR_MALFORMED_PATH, fieldPathErrorArgs));
        }

        // 2 - the test case must exist
        TargetStatus tcStatus = getModel().getStatus(testCase);
        if (tcStatus.status == TO_BE_DELETED || tcStatus.status == NOT_EXISTS) {
            logs.addEntry(LogEntry.failure().forTarget(target).withMessage(tcNotFoundMessage).build());
        }

        // 3 - the project actually exists
        if (testCase.isWellFormed()) {
            TargetStatus projectStatus = getModel().getProjectStatus(target.getProject());
            if (projectStatus.getStatus() != Existence.EXISTS) {
                logs.addEntry(
                        LogEntry.failure().forTarget(target).withMessage(Messages.ERROR_PROJECT_NOT_EXIST).build());
            }
        }
    }

    private void basicParameterChecksValidateName(ParameterTarget target, LogTrain logs,
            String[] fieldNameErrorArgs) {
        String name = target.getName();
        if (StringUtils.isBlank(name)) {
            logs.addEntry(
                    new LogEntry(target, ImportStatus.FAILURE, Messages.ERROR_FIELD_MANDATORY, fieldNameErrorArgs));
        } else {

            // 4 - name has length between 1 and 255
            if (name.length() > 255) {
                logs.addEntry(LogEntry.warning().forTarget(target)
                        .withMessage(Messages.ERROR_MAX_SIZE, fieldNameErrorArgs)
                        .withImpact(Messages.IMPACT_MAX_SIZE).build());
            }

            // 5 - name does not contain forbidden characters
            String regex = Parameter.NAME_REGEXP;
            name = name.trim();
            target.setName(name);
            Pattern p = Pattern.compile(regex);
            Matcher m = p.matcher(name);
            if (!StringUtils.isBlank(name) && !m.matches() && name.length() < 256) {
                logs.addEntry(new LogEntry(target, ImportStatus.FAILURE,
                        Messages.ERROR_PARAMETER_CONTAINS_FORBIDDEN_CHARACTERS, fieldNameErrorArgs));
            }
        }
    }

    /*
     * This method will check that, in case a nature and/or a type were supplied,
     * this element is consistent with the set of natures/types available in the
     * given project.
     *
     */
    private LogTrain checkNatureAndTypeAndFixIfNeeded(TestCaseTarget target, TestCase testCase) {

        LogTrain logs = new LogTrain();

        if (target.isWellFormed()) {

            TargetStatus projectStatus = getModel().getProjectStatus(target.getProject());
            if (projectStatus.getStatus() == Existence.EXISTS) {

                // 2-1 nature, if specified, must be consistent with the natures of the target project
                if (!natureDefinedAndConsistent(projectStatus, testCase)) {
                    logs.addEntry(LogEntry.warning().forTarget(target)
                            .withMessage(Messages.ERROR_INVALID_NATURE, target.getPath())
                            .withImpact(Messages.IMPACT_DEFAULT_VALUE).build());
                }

                if (!typeDefinedAndConsistent(projectStatus, testCase)) {
                    logs.addEntry(LogEntry.warning().forTarget(target)
                            .withMessage(Messages.ERROR_INVALID_TYPE, target.getPath())
                            .withImpact(Messages.IMPACT_DEFAULT_VALUE).build());
                }
            }
        }

        return logs;
    }

    /*
     * This method will check that, in case a nature and/or a type were supplied,
     * this element is consistent with the set of natures/types available in the
     * given project.
     */
    private LogTrain checkCategoryAndFixIfNeeded(RequirementVersionTarget target, RequirementVersion reqVersion) {

        LogTrain logs = new LogTrain();

        if (target.isWellFormed()) {

            TargetStatus projectStatus = getModel().getProjectStatus(target.getProject());
            if (projectStatus.getStatus() == Existence.EXISTS) {
                //category, if specified, must be consistent with the categories of the target project
                if (!categoryDefinedAndConsistent(projectStatus, reqVersion)) {
                    logs.addEntry(LogEntry.warning().forTarget(target)
                            .withMessage(Messages.ERROR_INVALID_CATEGORY, target.getPath())
                            .withImpact(Messages.IMPACT_DEFAULT_VALUE).build());
                }
            }
        }

        return logs;
    }

    /**
     * Check if a category defined in an imported {@link RequirementVersion} is defined and exist in database.
     * @param projectStatus the target project represented by a {@link ProjectTargetStatus}
     * @param reqVersion the {@link RequirementVersion} being imported
     * @return
     */

    private boolean categoryDefinedAndConsistent(TargetStatus projectStatus, RequirementVersion reqVersion) {
        boolean isConsistent;
        InfoListItem category = reqVersion.getCategory();
        if (category != null) {
            isConsistent = getInfoListItemService().isCategoryConsistent(projectStatus.getId(), category.getCode());
        } else {
            isConsistent = false;
        }
        return isConsistent;
    }

    private boolean natureDefinedAndConsistent(TargetStatus projectStatus, TestCase testCase) {
        boolean isConsistent;
        InfoListItem nature = testCase.getNature();

        if (nature == null) {
            isConsistent = true;
        } else {
            isConsistent = getInfoListItemService().isNatureConsistent(projectStatus.getId(), nature.getCode());
        }

        return isConsistent;
    }

    private boolean typeDefinedAndConsistent(TargetStatus projectStatus, TestCase testCase) {
        boolean isConsistent;
        InfoListItem type = testCase.getType();

        if (type == null) {
            isConsistent = true;
        } else {
            isConsistent = getInfoListItemService().isTypeConsistent(projectStatus.getId(), type.getCode());
        }

        return isConsistent;
    }

}