jenkinsci/hpe-application-automation-tools-plugin

View on GitHub
src/main/java/com/microfocus/application/automation/tools/results/TestResultToALMUploader.java

Summary

Maintainability
A
3 hrs
Test Coverage
/*
 * Certain versions of software accessible here may contain branding from Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company.
 * This software was acquired by Micro Focus on September 1, 2017, and is now offered by OpenText.
 * Any reference to the HP and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE marks are the property of their respective owners.
 * __________________________________________________________________
 * MIT License
 *
 * Copyright 2012-2024 Open Text
 *
 * The only warranties for products and services of Open Text and
 * its affiliates and licensors ("Open Text") are as may be set forth
 * in the express warranty statements accompanying such products and services.
 * Nothing herein should be construed as constituting an additional warranty.
 * Open Text shall not be liable for technical or editorial errors or
 * omissions contained herein. The information contained herein is subject
 * to change without notice.
 *
 * Except as specifically indicated otherwise, this document contains
 * confidential information and a valid license is required for possession,
 * use or copying. If this work is provided to the U.S. Government,
 * consistent with FAR 12.211 and 12.212, Commercial Computer Software,
 * Computer Software Documentation, and Technical Data for Commercial Items are
 * licensed to the U.S. Government under vendor's standard commercial license.
 *
 * 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.microfocus.application.automation.tools.results;

import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernameListBoxModel;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder;
import com.cloudbees.plugins.credentials.matchers.IdMatcher;
import com.microfocus.application.automation.tools.model.AlmServerSettingsModel;
import com.microfocus.application.automation.tools.model.EnumDescription;
import com.microfocus.application.automation.tools.results.service.AlmRestInfo;
import com.microfocus.application.automation.tools.results.service.AlmRestTool;
import com.microfocus.application.automation.tools.results.service.AttachmentUploadService;
import com.microfocus.application.automation.tools.results.service.ExternalEntityUploadLogger;
import com.microfocus.application.automation.tools.results.service.IExternalEntityUploadService;
import com.microfocus.application.automation.tools.settings.AlmServerSettingsGlobalConfiguration;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.matrix.MatrixAggregatable;
import hudson.matrix.MatrixAggregator;
import hudson.matrix.MatrixBuild;
import hudson.model.Action;
import hudson.model.BuildListener;
import hudson.model.AbstractProject;
import hudson.model.Item;
import hudson.model.Queue;
import hudson.model.Result;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.model.queue.Tasks;
import hudson.security.ACL;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildStepMonitor;
import hudson.tasks.Publisher;
import hudson.tasks.Recorder;
import hudson.tasks.test.TestResultAggregator;
import hudson.tasks.test.TestResultProjectAction;
import hudson.util.FormValidation;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.net.HttpURLConnection;
import java.util.List;

import hudson.util.ListBoxModel;
import hudson.util.VariableResolver;
import jenkins.tasks.SimpleBuildStep;
import org.apache.commons.lang.StringUtils;
import org.apache.tools.ant.DirectoryScanner;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;

import com.microfocus.application.automation.tools.model.UploadTestResultToAlmModel;
import com.microfocus.application.automation.tools.results.service.DefaultExternalEntityUploadServiceImpl;

/**

 *
 * @author Jacky Zhu
 */
public class TestResultToALMUploader extends Recorder implements Serializable, MatrixAggregatable, SimpleBuildStep {

    private static final long serialVersionUID = 1L;
    private UploadTestResultToAlmModel uploadTestResultToAlmModel;
    private String almServerName;
    private String credentialsId;
    private String almDomain;
    private String clientType;
    private String almProject;
    private String testingFramework;
    private String testingTool;
    private String almTestFolder;
    private String almTestSetFolder;
    private String almTimeout;
    private String testingResultFile;
    private String testingAttachments;
    private String jenkinsServerUrl;

    // These getters setters work for reading config.xml.
    public UploadTestResultToAlmModel getUploadTestResultToAlmModel() {
        return uploadTestResultToAlmModel;
    }

    public void setUploadTestResultToAlmModel(UploadTestResultToAlmModel uploadTestResultToAlmModel) {
        this.uploadTestResultToAlmModel = uploadTestResultToAlmModel;
    }

    public String getAlmServerName() {
        return almServerName;
    }

    public void setAlmServerName(String almServerName) {
        this.almServerName = almServerName;
    }

    public String getCredentialsId() {
        return credentialsId;
    }

    public void setCredentialsId(String credentialsId) {
        this.credentialsId = credentialsId;
    }

    public String getAlmDomain() {
        return almDomain;
    }

    public void setAlmDomain(String almDomain) {
        this.almDomain = almDomain;
    }

    public String getClientType() {
        return clientType;
    }

    public void setClientType(String clientType) {
        this.clientType = clientType;
    }

    public String getAlmProject() {
        return almProject;
    }

    public void setAlmProject(String almProject) {
        this.almProject = almProject;
    }

    public String getTestingFramework() {
        return testingFramework;
    }

    public void setTestingFramework(String testingFramework) {
        this.testingFramework = testingFramework;
    }

    public String getTestingTool() {
        return testingTool;
    }

    public void setTestingTool(String testingTool) {
        this.testingTool = testingTool;
    }

    public String getAlmTestFolder() {
        return almTestFolder;
    }

    public void setAlmTestFolder(String almTestFolder) {
        this.almTestFolder = almTestFolder;
    }

    public String getAlmTestSetFolder() {
        return almTestSetFolder;
    }

    public void setAlmTestSetFolder(String almTestSetFolder) {
        this.almTestSetFolder = almTestSetFolder;
    }

    public String getAlmTimeout() {
        return almTimeout;
    }

    public void setAlmTimeout(String almTimeout) {
        this.almTimeout = almTimeout;
    }

    public String getTestingResultFile() {
        return testingResultFile;
    }

    public void setTestingResultFile(String testingResultFile) {
        this.testingResultFile = testingResultFile;
    }

    public String getTestingAttachments() {
        return testingAttachments;
    }

    public void setTestingAttachments(String testingAttachments) {
        this.testingAttachments = testingAttachments;
    }

    public String getJenkinsServerUrl() {
        return jenkinsServerUrl;
    }

    public void setJenkinsServerUrl(String jenkinsServerUrl) {
        this.jenkinsServerUrl = jenkinsServerUrl;
    }

    @DataBoundConstructor
    public TestResultToALMUploader(
            String almServerName,
            String credentialsId,
            String almDomain,
            String clientType,
            String almProject,
            String testingFramework,
            String testingTool,
            String almTestFolder,
            String almTestSetFolder,
            String almTimeout,
            String testingResultFile,
            String testingAttachments,
            String jenkinsServerUrl) {

        this.almServerName = almServerName;
        this.credentialsId = credentialsId;
        this.almDomain = almDomain;
        this.clientType = clientType;
        this.almProject = almProject;
        this.testingFramework = testingFramework;
        this.testingTool = testingTool;
        this.almTestFolder = almTestFolder;
        this.almTestSetFolder = almTestSetFolder;
        this.almTimeout = almTimeout;
        this.testingResultFile = testingResultFile;
        this.testingAttachments = testingAttachments;
        this.jenkinsServerUrl = jenkinsServerUrl;
    }

    @Override
    public void perform(Run<?, ?> build, FilePath workspace, Launcher launcher,
                        TaskListener listener) throws InterruptedException, IOException {

        ExternalEntityUploadLogger logger = new ExternalEntityUploadLogger(listener.getLogger());

        // Credentials id maybe can't be blank
        if (StringUtils.isBlank(credentialsId)) {
            logger.log("INFO: credentials is not configured.");
            build.setResult(Result.UNSTABLE);
            return;
        }
        UsernamePasswordCredentials credentials = getCredentialsById(credentialsId, build, logger);


        logger.log(String.format("INFO: 'Upload test result to ALM' Post Build Step is being invoked by %s.",
                credentials.getUsername()));

        List<String> importedTestsetIds = null;

        uploadTestResultToAlmModel = new UploadTestResultToAlmModel(
                almServerName,
                credentials.getUsername(),
                credentials.getPassword().getPlainText(),
                almDomain,
                clientType,
                almProject,
                testingFramework,
                testingTool,
                almTestFolder,
                almTestSetFolder,
                almTimeout,
                testingResultFile,
                testingAttachments,
                jenkinsServerUrl);

        VariableResolver<String> varResolver = new VariableResolver.ByMap<String>(build.getEnvironment(listener));

        String serverUrl = getAlmServerUrl(uploadTestResultToAlmModel.getAlmServerName());
        String runUrl = "";
        String tempUrl = Util.replaceMacro(uploadTestResultToAlmModel.getJenkinsServerUrl(), varResolver);
        if(tempUrl != null && tempUrl.length() >0 ) {
            if(tempUrl.charAt(tempUrl.length() -1) != '/') {
                runUrl= tempUrl + "/" + build.getUrl();
            } else  {
                runUrl = tempUrl + build.getUrl();
            }
        }

        File root = build.getRootDir();
        DirectoryScanner ds = new DirectoryScanner();
        ds.setBasedir(root);
        ds.setIncludes( new String[] {uploadTestResultToAlmModel.getTestingResultFile()});
        ds.scan();
        if (ds.getIncludedFilesCount() == 0) {
            logger.log("INFO: No Test Report found.");
            build.setResult(Result.UNSTABLE);
        } else {
            logger.log("INFO: "+ ds.getIncludedFilesCount() +" test result file found.");
            String[] files = ds.getIncludedFiles();

            AlmRestInfo loginInfo = new AlmRestInfo(
                    serverUrl,
                    Util.replaceMacro(uploadTestResultToAlmModel.getAlmDomain(), varResolver),
                    clientType,
                    Util.replaceMacro(uploadTestResultToAlmModel.getAlmProject(), varResolver),
                    uploadTestResultToAlmModel.getAlmUserName(),
                    uploadTestResultToAlmModel.getAlmPassword(),
                    Util.replaceMacro(uploadTestResultToAlmModel.getAlmTestSetFolder(), varResolver)
            );
            AlmRestTool almRestTool = new AlmRestTool(loginInfo, logger);

            for (String fileName : files) {
                String fullpath = root.getAbsolutePath() + File.separator + fileName;
                logger.log("INFO: Start to upload "+fullpath);
                IExternalEntityUploadService service = new DefaultExternalEntityUploadServiceImpl(almRestTool, workspace, logger);
                try {
                    importedTestsetIds = service.uploadExternalTestSet(loginInfo,
                            fullpath,
                            Util.replaceMacro(uploadTestResultToAlmModel.getAlmTestSetFolder(), varResolver),
                            Util.replaceMacro(uploadTestResultToAlmModel.getAlmTestFolder(), varResolver),
                            uploadTestResultToAlmModel.getTestingFramework(),
                            uploadTestResultToAlmModel.getTestingTool(),
                            String.valueOf(build.getNumber()),
                            build.getParent().getDisplayName(),
                            runUrl
                    );
                    logger.log("INFO: Uploaded "+fullpath + ".");
                } catch (Exception e) {
                    logger.log("ERR: There's exception while uploading " + fullpath + ". " + e.getMessage());
                    build.setResult(Result.UNSTABLE);
                }
            }

            // Upload attachment.
            String testingAttachment = uploadTestResultToAlmModel.getTestingAttachments();
            if (importedTestsetIds != null
                    && !importedTestsetIds.isEmpty()
                    && testingAttachment != null
                    && !testingAttachment.isEmpty()) {

                AttachmentUploadService.init(build, workspace, almRestTool.getRestClient(), logger);
                if (!AttachmentUploadService.getInstance().upload(testingAttachment, "test-sets", importedTestsetIds.get(0))) {
                    build.setResult(Result.UNSTABLE);
                }
            }
        }
        logger.log("INFO: 'Upload test result to ALM' Completed.");
    }

    /**
     * Get user name password credentials by id.
     */
    private UsernamePasswordCredentials getCredentialsById(String credentialsId, Run<?, ?> run, ExternalEntityUploadLogger logger) {
        UsernamePasswordCredentials credentials = CredentialsProvider.findCredentialById(credentialsId,
                StandardUsernamePasswordCredentials.class,
                run,
                URIRequirementBuilder.create().build());

        if (credentials == null) {
            logger.log("Can not find credentials with the credentialsId:" + credentialsId);
        }
        return credentials;
    }

    private String getAlmServerUrl(String almServerName) {
        AlmServerSettingsModel[] almServers = AlmServerSettingsGlobalConfiguration.getInstance().getInstallations();
        if(almServers != null && almServers.length >0) {
            for(AlmServerSettingsModel almServerModel: almServers) {
                if(almServerName.equalsIgnoreCase(almServerModel.getAlmServerName())) {
                    return almServerModel.getAlmServerUrl();
                }
            }
        }
        return "";
    }

    /**
     * This works for jelly
     * <f:option selected="${almServer.almServerName==instance.almServerSettingsModel.almServerName}" value="${almServer.almServerName}">
     * @return
     */
    public AlmServerSettingsModel getAlmServerSettingsModel() {
        for (AlmServerSettingsModel almServer : getDescriptor().getAlmServers()) {
            if (this.almServerName.equals(almServer.getAlmServerName())) {
                return almServer;
            }
        }
        return null;
    }

    @Override
    public DescriptorImpl getDescriptor() {
        return (DescriptorImpl) super.getDescriptor();
    }

    @Override
    public Action getProjectAction(AbstractProject<?, ?> project) {

        return new TestResultProjectAction(project);
    }

    @Override
    public MatrixAggregator createAggregator(
            MatrixBuild build,
            Launcher launcher,
            BuildListener listener) {

        return new TestResultAggregator(build, launcher, listener);
    }

    @Override
    public BuildStepMonitor getRequiredMonitorService() {

        return BuildStepMonitor.BUILD;
    }

    @Extension
    // To expose this builder in the Snippet Generator.
    @Symbol("uploadResultToALM")
    public static class DescriptorImpl extends BuildStepDescriptor<Publisher> {

        public DescriptorImpl() {

            load();
        }

        @Override
        public String getDisplayName() {

            return "Upload test result to ALM";
        }

        @Override
        public boolean isApplicable(
                @SuppressWarnings("rawtypes") Class<? extends AbstractProject> jobType) {

            return true;
        }

        public boolean hasAlmServers() {
            return AlmServerSettingsGlobalConfiguration.getInstance().hasAlmServers();
        }

        public AlmServerSettingsModel[] getAlmServers() {
            return AlmServerSettingsGlobalConfiguration.getInstance().getInstallations();
        }

        public FormValidation doCheckAlmUserName(@QueryParameter String value) {
            if (StringUtils.isBlank(value)) {
                return FormValidation.error("User name must be set");
            }

            return FormValidation.ok();
        }
        public FormValidation doCheckAlmDomain(@QueryParameter String value) {
            if (StringUtils.isBlank(value)) {
                return FormValidation.error("Domain must be set");
            }

            return FormValidation.ok();
        }

        public FormValidation doCheckAlmProject(@QueryParameter String value) {
            if (StringUtils.isBlank(value)) {
                return FormValidation.error("Project must be set");
            }

            return FormValidation.ok();
        }

        public FormValidation doCheckAlmTestFolder(@QueryParameter String value) {
            if (StringUtils.isBlank(value)) {
                return FormValidation.error("TestFolder are missing");
            }

            return FormValidation.ok();
        }

        public FormValidation doCheckAlmTestSetFolder(@QueryParameter String value) {
            if (StringUtils.isBlank(value)) {
                return FormValidation.error("TestSetFolder are missing");
            }

            return FormValidation.ok();
        }

        public FormValidation doCheckTestingResultFile(@QueryParameter String value) {
            if (StringUtils.isBlank(value)) {
                return FormValidation.error("Testing result file must be set");
            }

            return FormValidation.ok();
        }

        public List<EnumDescription> getTestingFrameworks() {
            return UploadTestResultToAlmModel.testingFrameworks;
        }

        /**
         * To fill in the credentials drop down list which's field is 'credentialsId'.
         * This method's name works with tag <c:select/>.
         */
        public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Item project,
                                                     @QueryParameter String credentialsId) {
            if (project == null || !project.hasPermission(Item.CONFIGURE)) {
                return new StandardUsernameListBoxModel().includeCurrentValue(credentialsId);
            }
            return new StandardUsernameListBoxModel()
                    .includeEmptyValue()
                    .includeAs(
                            project instanceof Queue.Task ? Tasks.getAuthenticationOf((Queue.Task) project) : ACL.SYSTEM,
                            project,
                            StandardUsernamePasswordCredentials.class,
                            URIRequirementBuilder.create().build())
                    .includeCurrentValue(credentialsId);
        }

        public FormValidation doCheckCredentialsId(@AncestorInPath Item project, @QueryParameter String value) {
            for (ListBoxModel.Option o : CredentialsProvider.listCredentials(
                    StandardUsernamePasswordCredentials.class,
                    project,
                    project instanceof Queue.Task ? Tasks.getAuthenticationOf((Queue.Task) project) : ACL.SYSTEM,
                    URIRequirementBuilder.create().build(),
                    new IdMatcher(value))) {

                if (StringUtils.equals(value, o.value)) {
                    return FormValidation.ok();
                }
            }
            // no credentials available, can't check
            return FormValidation.warning("Cannot find any credentials with id " + value);
        }

    }
}