Package sagan.projects.support

Source Code of sagan.projects.support.GhPagesWebhookController

package sagan.projects.support;

import sagan.projects.Project;
import sagan.support.github.GitHubClient;

import java.io.IOException;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.expression.Expression;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.social.github.api.GitHub;
import org.springframework.stereotype.Controller;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import static java.lang.String.format;
import static org.springframework.web.bind.annotation.RequestMethod.POST;

/**
* Controller that handles requests from GitHub webhook set up at <a
* href="https://github.com/spring-projects/gh-pages#readme">the shared gh-pages
* repository</a> and notifies project leads to merge those new changes into their own
* projects. This notification happens by adding a new GH Issue to each project under the
* spring-projects org that has a gh-pages branch.
*
* The {token} path variable establishes a shared secret between the webhook and this
* controller, ensuring that only GitHub can POST and thus create new issues in the
* various Spring project repositories.
*/
@Controller
@RequestMapping("/webhook/gh-pages/{token}")
class GhPagesWebhookController {

    private static final Log logger = LogFactory.getLog(GhPagesWebhookController.class);

    private final ProjectMetadataService projectMetadataService;
    private final GitHub gitHub;
    private final String template;
    private final ObjectMapper objectMapper;

    @Value("${WEBHOOK_ACCESS_TOKEN:default}")
    private String accessToken;

    @Autowired
    public GhPagesWebhookController(ProjectMetadataService service, GitHub gitHub,
                                    ObjectMapper objectMapper) throws IOException {
        this.projectMetadataService = service;
        this.gitHub = gitHub;
        this.template = StreamUtils.copyToString(
                new ClassPathResource("notifications/gh-pages-updated.md").getInputStream(),
                Charset.defaultCharset());
        this.objectMapper = objectMapper;
    }

    @SuppressWarnings("unchecked")
    @RequestMapping(method = POST, headers = "content-type=application/x-www-form-urlencoded")
    @ResponseBody
    public HttpEntity<String> processUpdate(@RequestParam String payload, @PathVariable String token)
            throws IOException {
        HttpHeaders headers = new HttpHeaders();
        if (!accessToken.equals(token)) {
            headers.set("Status", "403 Forbidden");
            return new HttpEntity<>("{ \"message\": \"Forbidden\" }\n", headers);
        }
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression spel = parser.parseExpression(template, new TemplateParserContext());

        Map<?, ?> push;
        try {
            push = this.objectMapper.readValue(payload, Map.class);
            logger.info("Recieved new webhook payload for push with head_commit message: "
                    + ((Map<?, ?>) push.get("head_commit")).get("message"));
        } catch (JsonParseException ex) {
            headers.set("Status", "400 Bad Request");
            return new HttpEntity<>("{ \"message\": \"Bad Request\" }\n", headers);
        }

        StringBuilder commits = new StringBuilder();
        for (Map<?, ?> commit : (List<Map<?, ?>>) push.get("commits")) {
            commits.append(format(" - %s (%s)\n", commit.get("message"), commit.get("id")));
        }
        Map<String, Object> root = new HashMap<>();
        root.put("push", push);
        root.put("commits", commits);
        projectMetadataService.getProjects().stream()
                .filter(project -> hasGhPagesBranch(project))
                .forEach(project -> createGitHubIssue(project, this.objectMapper, root, spel));
        headers.set("Status", "200 OK");
        return new HttpEntity<>("{ \"message\": \"Successfully processed update\" }\n", headers);
    }

    private boolean hasGhPagesBranch(Project project) {
        if (project.hasSite() && project.getSiteUrl().startsWith("http://projects.spring.io")) {
            String ghPagesBranchUrl = format("%s/repos/%s/%s/branches/gh-pages",
                    GitHubClient.API_URL_BASE, "spring-projects", project.getId());
            try {
                HttpHeaders headers = gitHub.restOperations().headForHeaders(ghPagesBranchUrl);
                return "200 OK".equals(headers.getFirst("Status"));
            } catch (Exception ex) {
                // RestTemplate call above logs at WARN level if anything goes wrong
            }
        }
        return false;
    }

    private void createGitHubIssue(Project project, ObjectMapper jsonMapper, Map<String, Object> root, Expression spel) {
        root.put("project", project);
        Map<String, String> newIssue = new HashMap<>();
        newIssue.put("title", "Please merge the latest changes to common gh-pages");
        newIssue.put("body", spel.getValue(root, String.class));
        String projectIssuesUrl =
                format("%s/repos/spring-projects/%s/issues", GitHubClient.API_URL_BASE, project.getId());
        try {
            URI newIssueUrl =
                    gitHub.restOperations().postForLocation(projectIssuesUrl,
                            jsonMapper.writeValueAsString(newIssue));
            logger.info("Notification of new gh-pages changes created at " + newIssueUrl);
        } catch (RuntimeException | JsonProcessingException ex) {
            logger.warn("Unable to POST new issue to " + projectIssuesUrl);
        }
    }

}
TOP

Related Classes of sagan.projects.support.GhPagesWebhookController

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.