org.ligoj.app.plugin.req.squash.SquashPluginResource.java Source code

Java tutorial

Introduction

Here is the source code for org.ligoj.app.plugin.req.squash.SquashPluginResource.java

Source

/*
 * Licensed under MIT (https://github.com/ligoj/ligoj/blob/master/LICENSE)
 */
package org.ligoj.app.plugin.req.squash;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.ligoj.app.api.SubscriptionStatusWithData;
import org.ligoj.app.plugin.req.ReqResource;
import org.ligoj.app.plugin.req.ReqServicePlugin;
import org.ligoj.app.resource.plugin.AbstractToolPluginResource;
import org.ligoj.app.resource.plugin.BitBucketTag;
import org.ligoj.app.resource.plugin.BitBucketTags;
import org.ligoj.bootstrap.core.curl.CurlProcessor;
import org.ligoj.bootstrap.core.curl.CurlRequest;
import org.ligoj.bootstrap.core.validation.ValidationJsonException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * Squash TM resource.
 */
@Path(SquashPluginResource.URL)
@Service
@Produces(MediaType.APPLICATION_JSON)
public class SquashPluginResource extends AbstractToolPluginResource implements ReqServicePlugin {

    private static final TypeReference<TableItem<SquashProject>> VALUE_TYPE_REF = new TypeReference<>() {
        // Nothing to override
    };

    /**
     * Squash TM version tags
     */
    private static final String VERSION_TAG_START = "<label>Version</label><span>";
    private static final String VERSION_TAG_END = "</span>";

    /**
     * Plug-in key.
     */
    public static final String URL = ReqResource.SERVICE_URL + "/squash";

    /**
     * Plug-in key.
     */
    public static final String KEY = URL.replace('/', ':').substring(1);

    /**
     * Public server URL used to fetch the last available version of the product.
     */
    @Value("${service-req-squash-server:https://api.bitbucket.org}")
    private String publicServer;

    /**
     * Squash TM user name able to connect to instance.
     */
    public static final String PARAMETER_USER = KEY + ":user";

    /**
     * Squash TM user password able to connect to instance.
     */
    public static final String PARAMETER_PASSWORD = KEY + ":password";

    /**
     * Squash TM project's identifier, an integer
     */
    public static final String PARAMETER_PROJECT = KEY + ":project";

    /**
     * Web site URL
     */
    public static final String PARAMETER_URL = KEY + ":url";

    @Override
    public void link(final int subscription) throws IOException {
        final Map<String, String> parameters = subscriptionResource.getParameters(subscription);

        // Validate the project settings
        validateProject(parameters);
    }

    /**
     * Validate the project connectivity.
     *
     * @param parameters
     *            the project parameters.
     * @return project details.
     */
    protected SquashProject validateProject(final Map<String, String> parameters) throws IOException {
        // Get project's configuration
        final int id = Integer.parseInt(ObjectUtils.defaultIfNull(parameters.get(PARAMETER_PROJECT), "0"));
        final SquashProject result = getProject(parameters, id);

        if (result == null) {
            // Invalid id
            throw new ValidationJsonException(PARAMETER_PROJECT, "squash-project", id);
        }

        return result;
    }

    /**
     * Validate the basic REST connectivity to Squash.
     *
     * @param parameters
     *            the server parameters.
     * @return the detected Squash version.
     */
    protected String validateAdminAccess(final Map<String, String> parameters) {
        final String url = StringUtils.appendIfMissing(parameters.get(PARAMETER_URL), "/");

        // Check access
        CurlProcessor.validateAndClose(url + "login", PARAMETER_URL, "squash-connection");

        // Authentication request
        try (SquashCurlProcessor curl = new SquashCurlProcessor()) {
            if (!curl.process(authenticate(parameters, url))) {
                throw new ValidationJsonException(PARAMETER_USER, "squash-login");
            }

            // Check the user has enough rights to access to the administration page
            final CurlRequest admin = new CurlRequest("GET", url + "administration", null);
            admin.setSaveResponse(true);
            if (!curl.process(admin)) {
                throw new ValidationJsonException(PARAMETER_USER, "squash-admin");
            }
            return getVersion(admin.getResponse());
        }
    }

    /**
     * Create and return an authenticate request.
     */
    private CurlRequest authenticate(final Map<String, String> parameters, final String url) {
        return new CurlRequest(HttpMethod.POST, StringUtils.appendIfMissing(url, "/") + "login",
                "username=" + parameters.get(PARAMETER_USER) + "&password="
                        + StringUtils.trimToEmpty(parameters.get(PARAMETER_PASSWORD)),
                SquashCurlProcessor.LOGIN_CALLBACK,
                "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
    }

    /**
     * Return a Squash's resource. Return <code>null</code> when the resource is not found.
     */
    protected String getResource(final Map<String, String> parameters, final String resource) {
        return getResource(new SquashCurlProcessor(), parameters, parameters.get(PARAMETER_URL), resource);
    }

    /**
     * Return a Squash's resource. Return <code>null</code> when the resource is not found.
     */
    protected String getResource(final CurlProcessor processor, final Map<String, String> parameters,
            final String url, final String resource) {
        // Get the resource using the preempted authentication
        final CurlRequest request = new CurlRequest(HttpMethod.GET,
                StringUtils.appendIfMissing(url, "/") + resource, null);
        request.setSaveResponse(true);

        // Execute the requests
        processor.process(authenticate(parameters, url), request);
        processor.close();
        return request.getResponse();
    }

    /**
     * Redirect to the home page of the linked project. Send a redirect code with the relevant cookies used by Squash TM
     * since there is no way to force the link to a desired project.
     *
     * @return The response redirection to go to the right project.
     */
    @GET
    @Path("redirect/{subscription:\\d+}")
    public Response redirect(@PathParam("subscription") final int subscription) throws URISyntaxException {
        final Map<String, String> parameters = subscriptionResource.getParameters(subscription);
        final ResponseBuilder responseBuilder = Response.status(Status.FOUND).location(new URI(
                StringUtils.appendIfMissing(parameters.get(PARAMETER_URL), "/") + "requirement-workspace/"));
        responseBuilder.cookie(
                new NewCookie("jstree_open", "%23RequirementLibrary-" + parameters.get(PARAMETER_PROJECT), "/",
                        null, null, -1, false),
                new NewCookie("jstree_select", "%23RequirementLibrary-" + parameters.get(PARAMETER_PROJECT), "/",
                        null, null, -1, false));
        return responseBuilder.build();
    }

    @Override
    public String getVersion(final Map<String, String> parameters) {
        // Get the version from the raw HTML of the administration page
        return getVersion(getResource(parameters, "administration"));
    }

    private String getVersion(final String adminPage) {
        // Get the version from the raw HTML of the administration page
        final String page = ObjectUtils.defaultIfNull(adminPage, VERSION_TAG_START + VERSION_TAG_END);
        final int versionIndex = page.indexOf(VERSION_TAG_START) + VERSION_TAG_START.length();
        return page.substring(versionIndex, page.indexOf(VERSION_TAG_END, versionIndex));
    }

    /**
     * Return all Squash TM projects without limit.
     */
    protected List<SquashProject> getProjects(final Map<String, String> parameters) throws IOException {
        return getProjectsDataTables(parameters, null);
    }

    /**
     * Return all Squash TM projects without limit and an optional criteria.
     */
    @SuppressWarnings("unchecked")
    protected List<SquashProject> getProjectsDataTables(final Map<String, String> parameters, final String criteria)
            throws IOException {
        return ((TableItem<SquashProject>) new ObjectMapper()
                .readValue(
                        StringUtils.defaultIfEmpty(
                                getResource(parameters,
                                        "generic-projects?sEcho=4&iDisplayStart=0&iDisplayLength=100000"
                                                + (criteria == null ? "" : "&sSearch=" + criteria)),
                                "{\"aaData\":[]}"),
                        VALUE_TYPE_REF)).getAaData();
    }

    /**
     * Return Squash project from its identifier.
     */
    protected SquashProject getProject(final Map<String, String> parameters, final int id) throws IOException {
        return getProjects(parameters).stream().filter(project -> project.getId().equals(id)).findFirst()
                .orElse(null);
    }

    /**
     * Search the Squash TM the projects matching to the given criteria. Name only is considered.
     *
     * @param node
     *            the node to be tested with given parameters.
     * @param criteria
     *            the search criteria.
     * @return project names matching the criteria.
     */
    @GET
    @Path("{node}/{criteria}")
    @Consumes(MediaType.APPLICATION_JSON)
    public List<SquashProject> findAllByName(@PathParam("node") final String node,
            @PathParam("criteria") final String criteria) throws IOException {

        // Prepare the context, an ordered set of projects
        final Map<String, String> parameters = pvResource.getNodeParameters(node);

        // Get the projects and parse them
        return getProjectsDataTables(parameters, criteria);
    }

    @Override
    public String getKey() {
        return KEY;
    }

    @Override
    public String getLastVersion() throws IOException {
        try (CurlProcessor curl = new CurlProcessor()) {
            final String tagsAsJson = curl.get(publicServer
                    + "/2.0/repositories/nx/squashtest-tm/refs/tags?pagelen=1&q=name~%22squash-tm-%22&sort=-target.date",
                    "Content-Type:application/json");
            return StringUtils.removeStart(new ObjectMapper()
                    .readValue(StringUtils.defaultIfEmpty(tagsAsJson, "{\"values\":[]}"), BitBucketTags.class)
                    .getValues().stream().findFirst().map(BitBucketTag::getName).orElse("squash-tm-"),
                    "squash-tm-");
        }
    }

    @Override
    public SubscriptionStatusWithData checkSubscriptionStatus(final Map<String, String> parameters)
            throws IOException {
        final SubscriptionStatusWithData nodeStatusWithData = new SubscriptionStatusWithData();
        nodeStatusWithData.put("project", validateProject(parameters));
        return nodeStatusWithData;
    }

    @Override
    public boolean checkStatus(final Map<String, String> parameters) {
        // Status is UP <=> Administration access is UP
        validateAdminAccess(parameters);
        return true;
    }

}