├── .travis.yml ├── .gitignore ├── src ├── main │ ├── resources │ │ └── application.properties │ └── java │ │ └── com │ │ └── dkaedv │ │ └── glghproxy │ │ ├── githubentity │ │ ├── AccessToken.java │ │ └── HookRequest.java │ │ ├── controller │ │ ├── UserController.java │ │ ├── OrgsController.java │ │ ├── UsersController.java │ │ ├── RedirectsController.java │ │ ├── LoginController.java │ │ └── ReposController.java │ │ ├── gitlabclient │ │ ├── GitlabSessionProvider.java │ │ └── OAuthClient.java │ │ ├── LoggingFilter.java │ │ ├── Application.java │ │ └── converter │ │ └── GitlabToGithubConverter.java └── test │ └── java │ └── com │ └── dkaedv │ └── glghproxy │ └── converter │ └── GitlabToGithubConverterTest.java ├── Dockerfile ├── pom.xml └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.classpath 3 | /.project 4 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Base URL of Gitlab server 2 | #gitlabUrl=https://yourgitlabserver 3 | -------------------------------------------------------------------------------- /src/main/java/com/dkaedv/glghproxy/githubentity/AccessToken.java: -------------------------------------------------------------------------------- 1 | package com.dkaedv.glghproxy.githubentity; 2 | 3 | public class AccessToken { 4 | private String access_token; 5 | private String scope = "repo"; 6 | private String token_type = "bearer"; 7 | 8 | public AccessToken() {} 9 | 10 | public AccessToken(String access_token) { 11 | this.access_token = access_token; 12 | } 13 | public String getAccess_token() { 14 | return access_token; 15 | } 16 | public String getScope() { 17 | return scope; 18 | } 19 | public String getToken_type() { 20 | return token_type; 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return "access_token=" + access_token + "&scope=" + scope + "&token_type=" + token_type; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM anapsix/alpine-java:8_jdk_unlimited 2 | 3 | WORKDIR /home/app 4 | 5 | # install dumb-init; helps dockerized java handle signals properly 6 | # using ADD avoids installing openssl dependency 7 | ADD https://github.com/Yelp/dumb-init/releases/download/v1.1.3/dumb-init_1.1.3_amd64 /usr/bin/dumb-init 8 | RUN chmod +x /usr/bin/dumb-init 9 | ENTRYPOINT ["dumb-init", "--"] 10 | 11 | # process will run as non-root `app` user 12 | RUN addgroup app \ 13 | && adduser -s /bin/bash -D app -G app \ 14 | && chown -R app:app . 15 | 16 | # install maven 17 | RUN wget http://mirrors.ocf.berkeley.edu/apache/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.tar.gz \ 18 | && tar zxvf apache-maven-3.3.9-bin.tar.gz \ 19 | && rm -f apache-maven-3.3.9-bin.tar.gz 20 | 21 | COPY . ./ 22 | 23 | USER app 24 | 25 | EXPOSE 8080 26 | 27 | # start with spring boot 28 | CMD apache-maven-3.3.9/bin/mvn spring-boot:run -DgitlabUrl="$GITLAB_URL" 29 | -------------------------------------------------------------------------------- /src/main/java/com/dkaedv/glghproxy/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.dkaedv.glghproxy.controller; 2 | 3 | import java.io.IOException; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import org.eclipse.egit.github.core.Repository; 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.web.bind.annotation.RequestHeader; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RequestParam; 12 | import org.springframework.web.bind.annotation.ResponseBody; 13 | 14 | @Controller 15 | @RequestMapping("/api/v3/user") 16 | public class UserController { 17 | 18 | @RequestMapping("/repos") 19 | @ResponseBody 20 | public List getReposForCurrentUser( 21 | @RequestParam String per_page, 22 | @RequestParam String page, 23 | @RequestHeader("Authorization") String authorization) throws IOException { 24 | 25 | return Collections.emptyList(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/dkaedv/glghproxy/gitlabclient/GitlabSessionProvider.java: -------------------------------------------------------------------------------- 1 | package com.dkaedv.glghproxy.gitlabclient; 2 | 3 | import javax.annotation.PostConstruct; 4 | 5 | import org.apache.commons.logging.Log; 6 | import org.apache.commons.logging.LogFactory; 7 | import org.gitlab.api.GitlabAPI; 8 | import org.gitlab.api.TokenType; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.stereotype.Component; 11 | 12 | @Component 13 | public class GitlabSessionProvider { 14 | private final static Log LOG = LogFactory.getLog(GitlabSessionProvider.class); 15 | 16 | @Value("${gitlabUrl}") 17 | private String gitlabUrl; 18 | 19 | public GitlabAPI connect(String authorizationHeader) { 20 | String token = authorizationHeader.replaceAll("token ", ""); 21 | 22 | GitlabAPI api = GitlabAPI.connect(gitlabUrl, token, TokenType.ACCESS_TOKEN); 23 | api.ignoreCertificateErrors(true); 24 | return api; 25 | } 26 | 27 | @PostConstruct 28 | public void logUrl() { 29 | LOG.info("Using Gitlab Base URL: " + gitlabUrl); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/dkaedv/glghproxy/githubentity/HookRequest.java: -------------------------------------------------------------------------------- 1 | package com.dkaedv.glghproxy.githubentity; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | public class HookRequest { 7 | private Integer id; 8 | private String name; 9 | private boolean active; 10 | private List events; 11 | private Map config; 12 | public Integer getId() { 13 | return id; 14 | } 15 | public void setId(Integer id) { 16 | this.id = id; 17 | } 18 | public String getName() { 19 | return name; 20 | } 21 | public void setName(String name) { 22 | this.name = name; 23 | } 24 | public boolean isActive() { 25 | return active; 26 | } 27 | public void setActive(boolean active) { 28 | this.active = active; 29 | } 30 | public List getEvents() { 31 | return events; 32 | } 33 | public void setEvents(List events) { 34 | this.events = events; 35 | } 36 | public Map getConfig() { 37 | return config; 38 | } 39 | public void setConfig(Map config) { 40 | this.config = config; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/dkaedv/glghproxy/controller/OrgsController.java: -------------------------------------------------------------------------------- 1 | package com.dkaedv.glghproxy.controller; 2 | 3 | import java.io.IOException; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import org.apache.commons.logging.Log; 8 | import org.apache.commons.logging.LogFactory; 9 | import org.eclipse.egit.github.core.Repository; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.web.bind.annotation.PathVariable; 12 | import org.springframework.web.bind.annotation.RequestHeader; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RequestParam; 15 | import org.springframework.web.bind.annotation.ResponseBody; 16 | 17 | @Controller 18 | @RequestMapping("/api/v3/orgs") 19 | public class OrgsController { 20 | private final static Log LOG = LogFactory.getLog(OrgsController.class); 21 | 22 | @RequestMapping("/{orgname}/repos") 23 | @ResponseBody 24 | public List getReposForOrg( 25 | @PathVariable String orgname, 26 | @RequestParam String per_page, 27 | @RequestParam String page, 28 | @RequestHeader("Authorization") String authorization) throws IOException { 29 | 30 | LOG.info("Received request: orgname=" + orgname + ", per_page=" + per_page + ", page=" + page + ", authorization=" + authorization); 31 | 32 | return Collections.emptyList(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/dkaedv/glghproxy/LoggingFilter.java: -------------------------------------------------------------------------------- 1 | package com.dkaedv.glghproxy; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.Filter; 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.FilterConfig; 8 | import javax.servlet.ServletException; 9 | import javax.servlet.ServletRequest; 10 | import javax.servlet.ServletResponse; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | 14 | import org.apache.commons.logging.Log; 15 | import org.apache.commons.logging.LogFactory; 16 | import org.springframework.stereotype.Component; 17 | 18 | @Component 19 | public class LoggingFilter implements Filter { 20 | private final static Log LOG = LogFactory.getLog(LoggingFilter.class); 21 | 22 | @Override 23 | public void destroy() { 24 | } 25 | 26 | @Override 27 | public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { 28 | HttpServletResponse response = (HttpServletResponse) res; 29 | HttpServletRequest request = (HttpServletRequest) req; 30 | 31 | String query = (request.getQueryString() == null) ? "" : "?" + request.getQueryString(); 32 | LOG.info("Request to " + request.getRequestURI() + query); 33 | 34 | chain.doFilter(req, res); 35 | 36 | request.getPathInfo(); 37 | } 38 | 39 | @Override 40 | public void init(FilterConfig arg0) throws ServletException { 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/dkaedv/glghproxy/Application.java: -------------------------------------------------------------------------------- 1 | package com.dkaedv.glghproxy; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.builder.SpringApplicationBuilder; 6 | import org.springframework.boot.context.web.SpringBootServletInitializer; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; 9 | 10 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 11 | 12 | @SpringBootApplication 13 | public class Application extends SpringBootServletInitializer { 14 | 15 | /** 16 | * The date format used by GitHub. No millis! JIRA DVCS Connector fails parsing 17 | * dates when we have millis, which results in the timezone not being applied. 18 | */ 19 | public static final String GITHUB_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ"; 20 | 21 | public static void main(String[] args) { 22 | System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true"); 23 | SpringApplication.run(Application.class, args); 24 | } 25 | 26 | @Override 27 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 28 | return application.sources(Application.class); 29 | } 30 | 31 | @Bean 32 | public Jackson2ObjectMapperBuilder jacksonBuilder() { 33 | Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); 34 | builder 35 | .indentOutput(true) 36 | .simpleDateFormat(GITHUB_DATE_FORMAT) 37 | .propertyNamingStrategy(new PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy()); 38 | 39 | return builder; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/com/dkaedv/glghproxy/converter/GitlabToGithubConverterTest.java: -------------------------------------------------------------------------------- 1 | package com.dkaedv.glghproxy.converter; 2 | 3 | import org.eclipse.egit.github.core.PullRequest; 4 | import org.gitlab.api.models.GitlabMergeRequest; 5 | import org.gitlab.api.models.GitlabUser; 6 | import org.junit.Test; 7 | 8 | import static org.junit.Assert.*; 9 | 10 | public class GitlabToGithubConverterTest { 11 | 12 | @Test 13 | public void shouldConvertPullRequest() { 14 | GitlabMergeRequest mergeRequest = new GitlabMergeRequest(); 15 | GitlabUser user = new GitlabUser(); 16 | user.setEmail("hanswurscht@test.com"); 17 | user.setId(5); 18 | mergeRequest.setAssignee(user); 19 | mergeRequest.setAuthor(user); 20 | mergeRequest.setId(15); 21 | mergeRequest.setIid(3); 22 | mergeRequest.setState("merged"); 23 | 24 | PullRequest pull = GitlabToGithubConverter.convertMergeRequest(mergeRequest, "http://gitlab", "testns", "test"); 25 | 26 | assertEquals("hanswurscht@test.com", pull.getAssignee().getEmail()); 27 | assertEquals("http://gitlab/testns/test/merge_requests/3", pull.getHtmlUrl()); 28 | 29 | } 30 | 31 | @Test 32 | public void shouldConvertMergedPullRequestWithNullAssignee() { 33 | GitlabMergeRequest mergeRequest = new GitlabMergeRequest(); 34 | GitlabUser user = new GitlabUser(); 35 | user.setEmail("hanswurscht@test.com"); 36 | user.setUsername("hanswurscht"); 37 | user.setId(5); 38 | mergeRequest.setAuthor(user); 39 | mergeRequest.setId(15); 40 | mergeRequest.setIid(3); 41 | mergeRequest.setState("merged"); 42 | 43 | PullRequest pull = GitlabToGithubConverter.convertMergeRequest(mergeRequest, "http://gitlab", "testns", "test"); 44 | 45 | assertEquals("hanswurscht@test.com", pull.getMergedBy().getEmail()); 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/dkaedv/glghproxy/gitlabclient/OAuthClient.java: -------------------------------------------------------------------------------- 1 | package com.dkaedv.glghproxy.gitlabclient; 2 | 3 | import javax.net.ssl.HostnameVerifier; 4 | import javax.net.ssl.HttpsURLConnection; 5 | import javax.net.ssl.SSLSession; 6 | 7 | import org.apache.commons.logging.Log; 8 | import org.apache.commons.logging.LogFactory; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.stereotype.Component; 11 | import org.springframework.util.LinkedMultiValueMap; 12 | import org.springframework.util.MultiValueMap; 13 | import org.springframework.web.client.RestTemplate; 14 | 15 | import com.dkaedv.glghproxy.githubentity.AccessToken; 16 | 17 | @Component 18 | public class OAuthClient { 19 | private final static Log LOG = LogFactory.getLog(OAuthClient.class); 20 | 21 | @Value("${gitlabUrl}") 22 | private String gitlabUrl; 23 | 24 | public AccessToken requestAccessToken(String client_id, String client_secret, String code, String callbackUrl) { 25 | HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { 26 | public boolean verify(String hostname, SSLSession session) { 27 | return true; 28 | } 29 | }); 30 | 31 | RestTemplate rest = new RestTemplate(); 32 | 33 | MultiValueMap requestParams = new LinkedMultiValueMap(); 34 | requestParams.add("client_id", client_id); 35 | requestParams.add("client_secret", client_secret); 36 | requestParams.add("code", code); 37 | requestParams.add("grant_type", "authorization_code"); 38 | requestParams.add("redirect_uri", callbackUrl); 39 | 40 | LOG.info("Requesting access token with params" + requestParams); 41 | 42 | AccessToken token = rest.postForObject(gitlabUrl + "/oauth/token", requestParams, AccessToken.class); 43 | 44 | LOG.info("Received token"); 45 | 46 | return token; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/dkaedv/glghproxy/controller/UsersController.java: -------------------------------------------------------------------------------- 1 | package com.dkaedv.glghproxy.controller; 2 | 3 | import java.io.IOException; 4 | import java.util.List; 5 | 6 | import org.eclipse.egit.github.core.Repository; 7 | import org.eclipse.egit.github.core.User; 8 | import org.gitlab.api.GitlabAPI; 9 | import org.gitlab.api.models.GitlabProject; 10 | import org.gitlab.api.models.GitlabUser; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.http.HttpStatus; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.web.bind.annotation.PathVariable; 16 | import org.springframework.web.bind.annotation.RequestHeader; 17 | import org.springframework.web.bind.annotation.RequestMapping; 18 | import org.springframework.web.bind.annotation.RequestParam; 19 | import org.springframework.web.bind.annotation.ResponseBody; 20 | 21 | import com.dkaedv.glghproxy.converter.GitlabToGithubConverter; 22 | import com.dkaedv.glghproxy.gitlabclient.GitlabSessionProvider; 23 | 24 | @Controller 25 | @RequestMapping("/api/v3/users") 26 | public class UsersController { 27 | @Autowired 28 | private GitlabSessionProvider gitlab; 29 | 30 | @RequestMapping("/{username}/repos") 31 | @ResponseBody 32 | public List getReposForUser( 33 | @PathVariable String username, 34 | @RequestParam String per_page, 35 | @RequestParam String page, 36 | @RequestHeader("Authorization") String authorization) throws IOException { 37 | 38 | GitlabAPI api = gitlab.connect(authorization); 39 | List projects = api.getProjects(); 40 | 41 | return GitlabToGithubConverter.convertRepositories(projects); 42 | } 43 | 44 | @RequestMapping("/{username}") 45 | public ResponseEntity getUser( 46 | @PathVariable String username, 47 | @RequestHeader("Authorization") String authorization) throws IOException { 48 | 49 | GitlabAPI api = gitlab.connect(authorization); 50 | List users = api.findUsers(username); 51 | 52 | if (users.size() >= 1) { 53 | return new ResponseEntity(GitlabToGithubConverter.convertUser(users.get(0)), HttpStatus.OK); 54 | } else { 55 | return new ResponseEntity(HttpStatus.NOT_FOUND); 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.dkaedv 6 | glghproxy 7 | 0.0.1-SNAPSHOT 8 | war 9 | 10 | glghproxy 11 | GitLab GitHub Proxy 12 | 13 | 14 | org.springframework.boot 15 | spring-boot-starter-parent 16 | 1.2.5.RELEASE 17 | 18 | 19 | 20 | 21 | UTF-8 22 | 1.7 23 | 7.0.64 24 | 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-web 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-tomcat 34 | provided 35 | 36 | 37 | org.gitlab 38 | java-gitlab-api 39 | 1.1.9 40 | 41 | 42 | org.eclipse.mylyn.github 43 | org.eclipse.egit.github.core 44 | 4.0.2.201509141540-r 45 | 46 | 47 | org.apache.commons 48 | commons-lang3 49 | 3.4 50 | 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-test 55 | test 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-maven-plugin 64 | 65 | 66 | 67 | 68 | 69 | 70 | egit 71 | Eclipse egit 72 | https://repo.eclipse.org/content/repositories/egit-releases/ 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/main/java/com/dkaedv/glghproxy/controller/RedirectsController.java: -------------------------------------------------------------------------------- 1 | package com.dkaedv.glghproxy.controller; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.web.bind.annotation.PathVariable; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import org.springframework.web.servlet.HandlerMapping; 10 | import org.springframework.util.AntPathMatcher; 11 | 12 | @Controller 13 | public class RedirectsController { 14 | 15 | @Value("${gitlabUrl}") 16 | private String gitlabUrl; 17 | 18 | @RequestMapping("/{namespace}") 19 | public String namespace( 20 | @PathVariable String namespace 21 | ) { 22 | 23 | return "redirect:" + gitlabUrl + "/" + namespace; 24 | } 25 | 26 | @RequestMapping("/{namespace}/{repo}") 27 | public String repoHomepage( 28 | @PathVariable String namespace, 29 | @PathVariable String repo 30 | ) { 31 | 32 | return "redirect:" + gitlabUrl + "/" + namespace + "/" + repo; 33 | } 34 | 35 | @RequestMapping("/{namespace}/{repo}/commit/{sha}") 36 | public String commit( 37 | @PathVariable String namespace, 38 | @PathVariable String repo, 39 | @PathVariable String sha 40 | ) { 41 | 42 | return "redirect:" + gitlabUrl + "/" + namespace + "/" + repo + "/commit/" + sha; 43 | } 44 | 45 | @RequestMapping("/{namespace}/{repo}/tree/**") 46 | public String repoTree( 47 | @PathVariable String namespace, 48 | @PathVariable String repo, 49 | HttpServletRequest request 50 | //@PathVariable String branch 51 | ) { 52 | String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE); 53 | String bestMatchPattern = (String ) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); 54 | AntPathMatcher apm = new AntPathMatcher(); 55 | String branch = apm.extractPathWithinPattern(bestMatchPattern, path); 56 | 57 | return "redirect:" + gitlabUrl + "/" + namespace + "/" + repo + "/tree/" + branch; 58 | } 59 | 60 | @RequestMapping("/{namespace}/{repo}/compare") 61 | public String compare( 62 | @PathVariable String namespace, 63 | @PathVariable String repo 64 | ) { 65 | 66 | return "redirect:" + gitlabUrl + "/" + namespace + "/" + repo + "/compare"; 67 | } 68 | 69 | @RequestMapping("/{namespace}/{repo}/compare/{spec}") 70 | public String compare( 71 | @PathVariable String namespace, 72 | @PathVariable String repo, 73 | @PathVariable String spec 74 | ) { 75 | 76 | return "redirect:" + gitlabUrl + "/" + namespace + "/" + repo + "/compare/" + spec; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/dkaedv/glghproxy/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package com.dkaedv.glghproxy.controller; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.net.MalformedURLException; 5 | import java.net.URL; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RequestMethod; 14 | import org.springframework.web.bind.annotation.RequestParam; 15 | import org.springframework.web.bind.annotation.ResponseBody; 16 | 17 | import com.dkaedv.glghproxy.gitlabclient.OAuthClient; 18 | 19 | @Controller 20 | @RequestMapping("/login/oauth") 21 | public class LoginController { 22 | @Value("${gitlabUrl}") 23 | private String gitlabUrl; 24 | 25 | @Autowired 26 | private OAuthClient oauthClient; 27 | 28 | private String redirectUri; 29 | 30 | /** 31 | * Step 1 - OAuth request from client application (e.g. JIRA) 32 | * @throws MalformedURLException 33 | */ 34 | @RequestMapping("/authorize") 35 | public String authorize( 36 | @RequestParam String scope, 37 | @RequestParam String client_id, 38 | @RequestParam String redirect_uri, 39 | HttpServletRequest request) throws UnsupportedEncodingException, MalformedURLException { 40 | 41 | // Save redirect uri 42 | this.redirectUri = redirect_uri; 43 | 44 | String callbackUrl = buildCallbackUrl(request); 45 | 46 | return "redirect:" + gitlabUrl + "/oauth/authorize?client_id=" + client_id + "&response_type=code&redirect_uri=" + callbackUrl; 47 | } 48 | 49 | private String buildCallbackUrl(HttpServletRequest request) throws MalformedURLException { 50 | return new URL(new URL(request.getRequestURL().toString()), "./authorize_callback").toString(); 51 | } 52 | 53 | @RequestMapping("/authorize_callback") 54 | public String gitlabCallback( 55 | @RequestParam String code 56 | ) { 57 | 58 | String answer = "redirect:" + this.redirectUri + "&code=" + code; 59 | 60 | // Clear redirect uri 61 | this.redirectUri = null; 62 | 63 | return answer; 64 | } 65 | 66 | 67 | /** 68 | * Step 3 - Client application exchanges code for an access token. 69 | */ 70 | @RequestMapping(value = "/access_token", method = RequestMethod.POST) 71 | @ResponseBody 72 | public String accessToken( 73 | @RequestParam String client_id, 74 | @RequestParam String client_secret, 75 | @RequestParam String code, 76 | HttpServletRequest request 77 | ) throws MalformedURLException { 78 | 79 | return oauthClient.requestAccessToken(client_id, client_secret, code, buildCallbackUrl(request)).toString(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitLab-GitHub Proxy (glghproxy) [![Build Status](https://travis-ci.org/dka23/gitlab-github-proxy.svg?branch=master)](https://travis-ci.org/dka23/gitlab-github-proxy) 2 | 3 | Proxy to provide GitHub-like API on top of Gitlab. Especially designed to use the **JIRA DVCS connector** with Gitlab. 4 | 5 | **VERY IMPORTANT CONFIGURATION REQUIREMENTS:** 6 | 7 | 1. The address of glghproxy on your network MUST be DNS resolvable AND routable from BOTH a) JIRA server, and b) end-user browsers. 8 | 2. Additionally, glghproxy MUST operate on tcp/80. 9 | 10 | ## Setup 11 | 12 | ### Step 1. Launch glghproxy using Maven and Spring boot from CLI 13 | 14 | **NOTICE:** The default Spring listen port is tcp/8080, 15 | so specifying it again as a java argument is redundant, 16 | but makes the problem explicit. 17 | You can choose to setup a port forward in front of it, 18 | or (not recommended) run the service as root so you can listen on tcp/80. 19 | 20 | ```bash 21 | mvn spring-boot:run -DgitlabUrl="http://yourgitlabserver.yourcompany.com" -Dserver.port=8080 22 | ``` 23 | 24 | Alternatively, you can launch using Docker, and its resident service will proxy tcp/80 -> tcp/8080 for you: 25 | ```bash 26 | docker build -t glghproxy . 27 | docker run -p 80:8080 glghproxy 28 | ``` 29 | 30 | For the hostname (ie. `glghproxy`) you'll need to add a DNS entry or a `/etc/hosts` override. 31 | However the latter will only work if glghproxy, JIRA, and the browser are operating on the same machine. 32 | That can be nice for testing, (e.g., with a locally installed [containerized] trial version of JIRA) but 33 | be aware that if you are using docker containers you'll need to ensure the hostname resolves to an IP that 34 | is resolvable from all sides--in that case, the `docker0` interface ip is recommended. 35 | 36 | **WARNING:** By default, JIRA and glghproxy want to operate on the same tcp/8080 port. If you are not 37 | using containers, you'll have to resolve this conflict yourself. 38 | 39 | ### Step 2. Generate new Application in GitLab 40 | 41 | - Browse to your Gitlab Applications tab (`/profile/applications`) 42 | - On the `Add New Application` tab, fill in the `Name` field with your choice (ie. `glghproxy`) 43 | - Fill in the `Redirect URI` with the address you've chosen for this proxy service. 44 | 45 | ``` 46 | http://glghproxy/login/oauth/authorize_callback 47 | ``` 48 | - Click the `Save application` button. 49 | - Make note of the `Application Id` and `Secret` on the following page. 50 | 51 | 52 | ### Step 3. Add a new DVCS account in JIRA 53 | 54 | - Browse to your JIRA Administration > DVCS accounts page (`/secure/admin/ConfigureDvcsOrganizations.jspa`) 55 | - Click the `Link Bitbucket Cloud or Github account` button. 56 | - On the `Add New Account` popup, select `Github Enterprise` from the `Host` dropdown menu. 57 | - Fill in `Team or User Account` field with the name of the Gitlab group or username containing the repositories you want JIRA to integrate. 58 | **NOTICE:** You will need to add multiple DVCS accounts if your repositories are spread across more than one group or username. 59 | - Fill in `Host URL` with the address you've chosen for this proxy service. (ie. `http://glghproxy`) 60 | - Fill in `Client ID` with the *Application Id* generated for you by Gitlab earlier. 61 | - Fill in `Client Secret` with the *Secret* generated for you by Gitlab earlier. 62 | - The `Auto Link New Repositories` and `Enable Smart Commits` checkboxes are compatible and are safe to configure to your liking. 63 | - Click the `Add` button and then the `Continue` button when prompted. 64 | - It should say `Connecting to Github Enterprise to configure your account...`. 65 | - Then it should say `Linking new account ...`. 66 | - Then you should see your newly added account listed and the JIRA DVCS feature should operate normally from here. 67 | -------------------------------------------------------------------------------- /src/main/java/com/dkaedv/glghproxy/controller/ReposController.java: -------------------------------------------------------------------------------- 1 | package com.dkaedv.glghproxy.controller; 2 | 3 | import java.io.IOException; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import org.apache.commons.logging.Log; 8 | import org.apache.commons.logging.LogFactory; 9 | import org.eclipse.egit.github.core.Comment; 10 | import org.eclipse.egit.github.core.PullRequest; 11 | import org.eclipse.egit.github.core.RepositoryBranch; 12 | import org.eclipse.egit.github.core.RepositoryCommit; 13 | import org.eclipse.egit.github.core.RepositoryHook; 14 | import org.eclipse.egit.github.core.event.Event; 15 | import org.gitlab.api.GitlabAPI; 16 | import org.gitlab.api.models.GitlabBranch; 17 | import org.gitlab.api.models.GitlabCommit; 18 | import org.gitlab.api.models.GitlabCommitDiff; 19 | import org.gitlab.api.models.GitlabMergeRequest; 20 | import org.gitlab.api.models.GitlabNote; 21 | import org.gitlab.api.models.GitlabProject; 22 | import org.gitlab.api.models.GitlabProjectHook; 23 | import org.gitlab.api.models.GitlabUser; 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.beans.factory.annotation.Value; 26 | import org.springframework.http.HttpStatus; 27 | import org.springframework.http.converter.HttpMessageNotReadableException; 28 | import org.springframework.stereotype.Controller; 29 | import org.springframework.web.bind.annotation.ExceptionHandler; 30 | import org.springframework.web.bind.annotation.PathVariable; 31 | import org.springframework.web.bind.annotation.RequestBody; 32 | import org.springframework.web.bind.annotation.RequestHeader; 33 | import org.springframework.web.bind.annotation.RequestMapping; 34 | import org.springframework.web.bind.annotation.RequestMethod; 35 | import org.springframework.web.bind.annotation.RequestParam; 36 | import org.springframework.web.bind.annotation.ResponseBody; 37 | import org.springframework.web.bind.annotation.ResponseStatus; 38 | 39 | import com.dkaedv.glghproxy.converter.GitlabToGithubConverter; 40 | import com.dkaedv.glghproxy.githubentity.HookRequest; 41 | import com.dkaedv.glghproxy.gitlabclient.GitlabSessionProvider; 42 | import com.fasterxml.jackson.databind.ObjectMapper; 43 | 44 | @Controller 45 | @RequestMapping("/api/v3/repos") 46 | public class ReposController { 47 | private static final Log LOG = LogFactory.getLog(ReposController.class); 48 | 49 | @Autowired 50 | private GitlabSessionProvider gitlab; 51 | 52 | @Value("${gitlabUrl}") 53 | private String gitlabUrl; 54 | 55 | @Autowired 56 | ObjectMapper objectMapper; 57 | 58 | @RequestMapping("/{namespace}/{repo}/branches") 59 | @ResponseBody 60 | public List getBranches( 61 | @PathVariable String namespace, 62 | @PathVariable String repo, 63 | @RequestParam String per_page, 64 | @RequestParam String page, 65 | @RequestHeader("Authorization") String authorization 66 | ) throws IOException { 67 | 68 | GitlabAPI api = gitlab.connect(authorization); 69 | List glbranches = api.getBranches(namespace + "/" + repo); 70 | 71 | return GitlabToGithubConverter.convertBranches(glbranches); 72 | } 73 | 74 | @RequestMapping("/{namespace}/{repo}/commits/{sha}") 75 | @ResponseBody 76 | public RepositoryCommit getCommit( 77 | @PathVariable String namespace, 78 | @PathVariable String repo, 79 | @PathVariable String sha, 80 | @RequestHeader("Authorization") String authorization 81 | ) throws IOException { 82 | 83 | GitlabAPI api = gitlab.connect(authorization); 84 | GitlabCommit glcommit = api.getCommit(namespace + "/" + repo, sha); 85 | List gldiffs = api.getCommitDiffs(namespace + "/" + repo, sha); 86 | List users = api.findUsers(glcommit.getAuthorEmail()); 87 | 88 | return GitlabToGithubConverter.convertCommit(glcommit, gldiffs, users.size() >= 1 ? users.get(0) : null); 89 | } 90 | 91 | @RequestMapping("/{namespace}/{repo}/events") 92 | @ResponseBody 93 | public List getEvents( 94 | @PathVariable String namespace, 95 | @PathVariable String repo, 96 | @RequestHeader("Authorization") String authorization 97 | ) throws IOException { 98 | 99 | GitlabAPI api = gitlab.connect(authorization); 100 | List glmergerequests = api.getMergeRequests(namespace + "/" + repo); 101 | 102 | return GitlabToGithubConverter.convertMergeRequestsToEvents(glmergerequests, gitlabUrl, namespace, repo); 103 | } 104 | 105 | @RequestMapping("/{namespace}/{repo}/pulls") 106 | @ResponseBody 107 | public List getPulls( 108 | @PathVariable String namespace, 109 | @PathVariable String repo, 110 | @RequestParam String state, 111 | @RequestParam String sort, 112 | @RequestParam String direction, 113 | @RequestParam String per_page, 114 | @RequestParam String page, 115 | @RequestHeader("Authorization") String authorization 116 | ) throws IOException { 117 | 118 | GitlabAPI api = gitlab.connect(authorization); 119 | List glmergerequests = api.getMergeRequests(namespace + "/" + repo); 120 | 121 | List mergeRequests = GitlabToGithubConverter.convertMergeRequests(glmergerequests, gitlabUrl, namespace, repo); 122 | //LOG.info(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(mergeRequests)); 123 | return mergeRequests; 124 | } 125 | 126 | @RequestMapping("/{namespace}/{repo}/pulls/{id}") 127 | @ResponseBody 128 | public PullRequest getPull( 129 | @PathVariable String namespace, 130 | @PathVariable String repo, 131 | @PathVariable Integer id, 132 | @RequestHeader("Authorization") String authorization 133 | ) throws IOException { 134 | 135 | GitlabAPI api = gitlab.connect(authorization); 136 | GitlabMergeRequest mergeRequest = findMergeRequestByProjectAndIid(namespace, repo, id, api); 137 | 138 | return GitlabToGithubConverter.convertMergeRequest(mergeRequest, gitlabUrl, namespace, repo); 139 | } 140 | 141 | 142 | @RequestMapping("/{namespace}/{repo}/pulls/{id}/commits") 143 | @ResponseBody 144 | public List getCommitsOnPullRequest( 145 | @PathVariable String namespace, 146 | @PathVariable String repo, 147 | @PathVariable Integer id, 148 | @RequestHeader("Authorization") String authorization 149 | ) throws IOException { 150 | 151 | GitlabAPI api = gitlab.connect(authorization); 152 | GitlabMergeRequest mergeRequest = findMergeRequestByProjectAndIid(namespace, repo, id, api); 153 | List commits = api.getCommits(mergeRequest); 154 | 155 | return GitlabToGithubConverter.convertCommits(commits); 156 | } 157 | 158 | private GitlabMergeRequest findMergeRequestByProjectAndIid(String namespace, String repo, Integer id, GitlabAPI api) throws IOException { 159 | List mergeRequests = api.getMergeRequests(namespace + "/" + repo); 160 | for (GitlabMergeRequest mergeRequest : mergeRequests) { 161 | if (mergeRequest.getIid().equals(id)) { 162 | return mergeRequest; 163 | } 164 | } 165 | 166 | return null; 167 | } 168 | 169 | /** 170 | * In Github, each Merge Request is automatically also an issue. Therefore we return its comments here. 171 | */ 172 | @RequestMapping("/{namespace}/{repo}/issues/{id}/comments") 173 | @ResponseBody 174 | public List getCommentsOnPullRequest( 175 | @PathVariable String namespace, 176 | @PathVariable String repo, 177 | @PathVariable Integer id, 178 | @RequestHeader("Authorization") String authorization 179 | ) throws IOException { 180 | 181 | GitlabAPI api = gitlab.connect(authorization); 182 | GitlabMergeRequest mergeRequest = findMergeRequestByProjectAndIid(namespace, repo, id, api); 183 | List notes = api.getNotes(mergeRequest); 184 | 185 | return GitlabToGithubConverter.convertComments(notes); 186 | } 187 | 188 | /** 189 | * Github additionally has review comments on merge requests (those on the diff). Gitlab has those also, 190 | * but doesn't distinguish in its API. Therefore return an empty list here. 191 | */ 192 | @RequestMapping("/{namespace}/{repo}/pulls/{id}/comments") 193 | @ResponseBody 194 | public List getReviewCommentsOnPullRequest( 195 | @PathVariable String namespace, 196 | @PathVariable String repo, 197 | @PathVariable Integer id, 198 | @RequestHeader("Authorization") String authorization 199 | ) throws IOException { 200 | 201 | return Collections.emptyList(); 202 | } 203 | 204 | @RequestMapping(value = "/{namespace}/{repo}/hooks", method = RequestMethod.GET) 205 | @ResponseBody 206 | public List getHooks( 207 | @PathVariable String namespace, 208 | @PathVariable String repo, 209 | @RequestParam("access_token") String authorization 210 | ) throws IOException { 211 | 212 | GitlabAPI api = gitlab.connect(authorization); 213 | List hooks = api.getProjectHooks(namespace + "/" + repo); 214 | 215 | return GitlabToGithubConverter.convertHooks(hooks); 216 | } 217 | 218 | @RequestMapping(value = "/{namespace}/{repo}/hooks", method = RequestMethod.POST) 219 | @ResponseBody 220 | @ResponseStatus(value = HttpStatus.CREATED) 221 | public RepositoryHook addHook( 222 | @PathVariable String namespace, 223 | @PathVariable String repo, 224 | @RequestParam("access_token") String authorization, 225 | @RequestBody HookRequest hook 226 | ) throws IOException { 227 | 228 | GitlabAPI api = gitlab.connect(authorization); 229 | GitlabProjectHook createdHook = api.addProjectHook( 230 | namespace + "/" + repo, 231 | hook.getConfig().get("url"), 232 | hook.getEvents().contains("push"), 233 | false, 234 | hook.getEvents().contains("pull_request")); 235 | 236 | return GitlabToGithubConverter.convertHook(createdHook); 237 | } 238 | 239 | @RequestMapping(value = "/{namespace}/{repo}/hooks/{hookId}", method = RequestMethod.DELETE) 240 | @ResponseBody 241 | public void deleteHook( 242 | @PathVariable String namespace, 243 | @PathVariable String repo, 244 | @PathVariable String hookId, 245 | @RequestParam("access_token") String authorization 246 | ) throws IOException { 247 | 248 | GitlabAPI api = gitlab.connect(authorization); 249 | GitlabProject project = api.getProject(namespace + "/" + repo); 250 | api.deleteProjectHook(project, hookId); 251 | } 252 | 253 | @ExceptionHandler 254 | @ResponseStatus(HttpStatus.BAD_REQUEST) 255 | public void handle(HttpMessageNotReadableException e) { 256 | LOG.warn("Returning HTTP 400 Bad Request", e); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/main/java/com/dkaedv/glghproxy/converter/GitlabToGithubConverter.java: -------------------------------------------------------------------------------- 1 | package com.dkaedv.glghproxy.converter; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.eclipse.egit.github.core.Comment; 9 | import org.eclipse.egit.github.core.Commit; 10 | import org.eclipse.egit.github.core.CommitFile; 11 | import org.eclipse.egit.github.core.CommitUser; 12 | import org.eclipse.egit.github.core.Milestone; 13 | import org.eclipse.egit.github.core.PullRequest; 14 | import org.eclipse.egit.github.core.PullRequestMarker; 15 | import org.eclipse.egit.github.core.Repository; 16 | import org.eclipse.egit.github.core.RepositoryBranch; 17 | import org.eclipse.egit.github.core.RepositoryCommit; 18 | import org.eclipse.egit.github.core.RepositoryHook; 19 | import org.eclipse.egit.github.core.TypedResource; 20 | import org.eclipse.egit.github.core.User; 21 | import org.eclipse.egit.github.core.event.Event; 22 | import org.eclipse.egit.github.core.event.PullRequestPayload; 23 | import org.gitlab.api.models.GitlabBranch; 24 | import org.gitlab.api.models.GitlabCommit; 25 | import org.gitlab.api.models.GitlabCommitDiff; 26 | import org.gitlab.api.models.GitlabMergeRequest; 27 | import org.gitlab.api.models.GitlabMilestone; 28 | import org.gitlab.api.models.GitlabNote; 29 | import org.gitlab.api.models.GitlabProject; 30 | import org.gitlab.api.models.GitlabProjectHook; 31 | import org.gitlab.api.models.GitlabUser; 32 | 33 | import com.fasterxml.jackson.core.JsonProcessingException; 34 | import com.fasterxml.jackson.databind.ObjectMapper; 35 | 36 | public class GitlabToGithubConverter { 37 | public static RepositoryBranch convertBranch(GitlabBranch glbranch) { 38 | RepositoryBranch branch = new RepositoryBranch(); 39 | branch.setName(glbranch.getName()); 40 | 41 | TypedResource commit = new TypedResource(); 42 | commit.setType(TypedResource.TYPE_COMMIT); 43 | commit.setSha(glbranch.getCommit().getId()); 44 | 45 | branch.setCommit(commit); 46 | return branch; 47 | } 48 | 49 | public static List convertBranches(List glbranches) { 50 | List branches = new ArrayList<>(glbranches.size()); 51 | 52 | for (GitlabBranch glbranch : glbranches) { 53 | RepositoryBranch branch = convertBranch(glbranch); 54 | branches.add(branch); 55 | } 56 | return branches; 57 | } 58 | 59 | public static RepositoryCommit convertCommit(GitlabCommit glcommit, List gldiffs, GitlabUser gluser) { 60 | RepositoryCommit repoCommit = new RepositoryCommit(); 61 | 62 | repoCommit.setSha(glcommit.getId()); 63 | 64 | Commit commit = new Commit(); 65 | commit.setMessage(glcommit.getTitle()); 66 | 67 | CommitUser commitUser = new CommitUser(); 68 | commitUser.setName(glcommit.getAuthorName()); 69 | commitUser.setEmail(glcommit.getAuthorEmail()); 70 | commitUser.setDate(glcommit.getCreatedAt()); 71 | commit.setAuthor(commitUser); 72 | commit.setCommitter(commitUser); 73 | 74 | repoCommit.setCommit(commit); 75 | 76 | User user = new User(); 77 | user.setEmail(glcommit.getAuthorEmail()); 78 | user.setLogin(gluser != null ? gluser.getUsername() : null); 79 | repoCommit.setAuthor(user); 80 | repoCommit.setCommitter(user); 81 | 82 | List parentIds = glcommit.getParentIds(); 83 | if (parentIds != null) { 84 | List parents = new ArrayList<>(parentIds.size()); 85 | for (String parentSha : parentIds) { 86 | Commit parent = new Commit(); 87 | parent.setSha(parentSha); 88 | parents.add(parent); 89 | } 90 | repoCommit.setParents(parents); 91 | } 92 | 93 | if (gldiffs != null) { 94 | List files = new ArrayList<>(gldiffs.size()); 95 | for (GitlabCommitDiff diff : gldiffs) { 96 | convertCommitFile(files, diff); 97 | } 98 | repoCommit.setFiles(files); 99 | } 100 | 101 | return repoCommit; 102 | } 103 | 104 | private static void convertCommitFile(List files, GitlabCommitDiff diff) { 105 | int additions = StringUtils.countMatches(diff.getDiff(), "\n+") - StringUtils.countMatches(diff.getDiff(), "\n+++"); 106 | int deletions = StringUtils.countMatches(diff.getDiff(), "\n-") - StringUtils.countMatches(diff.getDiff(), "\n---"); 107 | 108 | if (diff.getNewFile()) { 109 | CommitFile file = new CommitFile(); 110 | file.setStatus("added"); 111 | file.setFilename(diff.getNewPath()); 112 | file.setAdditions(additions); 113 | file.setChanges(additions); 114 | files.add(file); 115 | } else if (diff.getDeletedFile()) { 116 | CommitFile file = new CommitFile(); 117 | file.setStatus("removed"); 118 | file.setFilename(diff.getOldPath()); 119 | file.setDeletions(deletions); 120 | file.setChanges(deletions); 121 | files.add(file); 122 | } else if (diff.getRenamedFile()) { 123 | CommitFile oldFile = new CommitFile(); 124 | oldFile.setStatus("removed"); 125 | oldFile.setFilename(diff.getOldPath()); 126 | oldFile.setDeletions(deletions); 127 | oldFile.setChanges(deletions); 128 | files.add(oldFile); 129 | 130 | CommitFile newFile = new CommitFile(); 131 | newFile.setStatus("added"); 132 | newFile.setFilename(diff.getNewPath()); 133 | newFile.setDeletions(additions); 134 | newFile.setChanges(additions); 135 | files.add(newFile); 136 | } else { 137 | CommitFile file = new CommitFile(); 138 | file.setStatus("modified"); 139 | file.setFilename(diff.getNewPath()); 140 | file.setAdditions(additions); 141 | file.setDeletions(deletions); 142 | file.setChanges(additions + deletions); 143 | files.add(file); 144 | } 145 | } 146 | 147 | public static Repository convertRepository(GitlabProject project) { 148 | Repository repo = new Repository(); 149 | 150 | repo.setId(project.getId()); 151 | repo.setName(project.getName()); 152 | 153 | User user = new User(); 154 | user.setLogin(project.getNamespace().getName()); 155 | repo.setOwner(user); 156 | 157 | return repo; 158 | } 159 | 160 | public static List convertRepositories(List projects) { 161 | List repos = new ArrayList<>(projects.size()); 162 | 163 | for (GitlabProject project : projects) { 164 | repos.add(convertRepository(project)); 165 | } 166 | 167 | return repos; 168 | } 169 | 170 | public static List convertMergeRequests(List glmergerequests, String gitlabUrl, String namespace, String repo) { 171 | List pulls = new ArrayList<>(glmergerequests.size()); 172 | 173 | for (GitlabMergeRequest glmr : glmergerequests) { 174 | pulls.add(convertMergeRequest(glmr, gitlabUrl, namespace, repo)); 175 | } 176 | 177 | return pulls; 178 | } 179 | 180 | public static PullRequest convertMergeRequest(GitlabMergeRequest glmr, String gitlabUrl, String namespace, String repo) { 181 | PullRequest pull = new PullRequest(); 182 | 183 | pull.setAssignee(convertUser(glmr.getAssignee())); 184 | pull.setUser(convertUser(glmr.getAuthor())); 185 | pull.setCreatedAt(glmr.getCreatedAt()); 186 | pull.setBody(glmr.getDescription()); 187 | pull.setId(glmr.getId()); 188 | pull.setMilestone(convertMilestone(glmr.getMilestone())); 189 | pull.setNumber(glmr.getIid()); 190 | pull.setHead(createPullRequestMarker(glmr.getSourceBranch(), namespace, repo)); 191 | pull.setBase(createPullRequestMarker(glmr.getTargetBranch(), namespace, repo)); 192 | convertMergeRequestState(pull, glmr); 193 | pull.setTitle(glmr.getTitle()); 194 | 195 | if (glmr.getUpdatedAt() != null) { 196 | pull.setUpdatedAt(glmr.getUpdatedAt()); 197 | } else { 198 | pull.setUpdatedAt(glmr.getCreatedAt()); 199 | } 200 | 201 | pull.setHtmlUrl(gitlabUrl + "/" + namespace + "/" + repo + "/merge_requests/" + glmr.getIid()); 202 | 203 | //LOG.info("Converted merge request " + convertToJson(glmr) + " to pull request " + convertToJson(pull)); 204 | 205 | return pull; 206 | } 207 | 208 | private static void convertMergeRequestState(PullRequest pull, GitlabMergeRequest glmr) { 209 | if ("opened".equals(glmr.getState()) || "reopened".equals(glmr.getState())) { 210 | pull.setState("open"); 211 | pull.setMerged(false); 212 | } else if ("closed".equals(glmr.getState())) { 213 | pull.setState("closed"); 214 | pull.setMerged(false); 215 | pull.setClosedAt(glmr.getUpdatedAt()); 216 | } else if ("merged".equals(glmr.getState())) { 217 | pull.setState("closed"); 218 | pull.setMerged(true); 219 | pull.setClosedAt(glmr.getUpdatedAt()); 220 | pull.setMergedAt(glmr.getUpdatedAt()); 221 | 222 | if (glmr.getAssignee() != null) { 223 | pull.setMergedBy(convertUser(glmr.getAssignee())); 224 | } else { 225 | pull.setMergedBy(convertUser(glmr.getAuthor())); 226 | } 227 | } else { 228 | throw new RuntimeException("Unknown MR state: " + glmr.getState()); 229 | } 230 | } 231 | 232 | private static PullRequestMarker createPullRequestMarker(String branch, String namespace, String reponame) { 233 | PullRequestMarker marker = new PullRequestMarker(); 234 | marker.setLabel(branch); 235 | marker.setRef(branch); 236 | 237 | 238 | Repository repo = new Repository(); 239 | repo.setName(reponame); 240 | User owner = new User(); 241 | owner.setLogin(namespace); 242 | repo.setOwner(owner); 243 | 244 | marker.setRepo(repo); 245 | 246 | return marker; 247 | } 248 | 249 | private static Milestone convertMilestone(GitlabMilestone glmilestone) { 250 | if (glmilestone == null) { 251 | return null; 252 | } 253 | 254 | Milestone milestone = new Milestone(); 255 | 256 | milestone.setCreatedAt(glmilestone.getCreatedDate()); 257 | milestone.setDescription(glmilestone.getDescription()); 258 | milestone.setDueOn(glmilestone.getDueDate()); 259 | milestone.setState(glmilestone.getState()); 260 | milestone.setTitle(glmilestone.getTitle()); 261 | 262 | return milestone; 263 | } 264 | 265 | public static User convertUser(GitlabUser gluser) { 266 | if (gluser == null) { 267 | return null; 268 | } 269 | 270 | User user = new User(); 271 | user.setId(gluser.getId()); 272 | user.setLogin(gluser.getUsername()); 273 | user.setAvatarUrl(gluser.getAvatarUrl()); 274 | user.setBio(gluser.getBio()); 275 | user.setEmail(gluser.getEmail()); 276 | user.setName(gluser.getName()); 277 | user.setCreatedAt(gluser.getCreatedAt()); 278 | user.setType(User.TYPE_USER); 279 | 280 | return user; 281 | } 282 | 283 | public static List convertCommits(List glcommits) { 284 | List commits = new ArrayList<>(glcommits.size()); 285 | 286 | for (GitlabCommit glcommit : glcommits) { 287 | commits.add(convertCommit(glcommit, null, null)); 288 | } 289 | 290 | return commits; 291 | } 292 | 293 | public static List convertComments(List glnotes) { 294 | List comments = new ArrayList<>(glnotes.size()); 295 | 296 | for (GitlabNote glnote : glnotes) { 297 | comments.add(convertComment(glnote)); 298 | } 299 | 300 | return comments; 301 | } 302 | 303 | private static Comment convertComment(GitlabNote glnote) { 304 | Comment comment = new Comment(); 305 | 306 | comment.setUser(convertUser(glnote.getAuthor())); 307 | comment.setBody(glnote.getBody()); 308 | comment.setCreatedAt(glnote.getCreatedAt()); 309 | comment.setId(glnote.getId()); 310 | 311 | return comment; 312 | } 313 | 314 | public static List convertHooks(List glhooks) { 315 | List hooks = new ArrayList<>(glhooks.size()); 316 | 317 | for (GitlabProjectHook glhook : glhooks) { 318 | hooks.add(convertHook(glhook)); 319 | } 320 | 321 | return hooks; 322 | } 323 | 324 | public static RepositoryHook convertHook(GitlabProjectHook glhook) { 325 | RepositoryHook hook = new RepositoryHook(); 326 | 327 | hook.setCreatedAt(glhook.getCreatedAt()); 328 | hook.setName("web"); // Always "web" for webhooks ... 329 | hook.setUrl(glhook.getUrl()); 330 | hook.setActive(glhook.getPushEvents() || glhook.isMergeRequestsEvents()); 331 | hook.setId(Integer.valueOf(glhook.getId())); 332 | 333 | hook.setConfig(new HashMap()); 334 | hook.getConfig().put("url", glhook.getUrl()); 335 | 336 | return hook; 337 | } 338 | 339 | private static String convertToJson(Object o) { 340 | try { 341 | return new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(o); 342 | } catch (JsonProcessingException e) { 343 | throw new RuntimeException(e); 344 | } 345 | } 346 | 347 | public static List convertMergeRequestsToEvents(List glmergerequests, String gitlabUrl, String namespace, String repo) { 348 | List events = new ArrayList<>(glmergerequests.size()); 349 | 350 | for (GitlabMergeRequest glmergerequest : glmergerequests) { 351 | events.add(convertMergeRequestToEvent(glmergerequest, gitlabUrl, namespace, repo)); 352 | } 353 | 354 | return events; 355 | } 356 | 357 | private static Event convertMergeRequestToEvent(GitlabMergeRequest glmergerequest, String gitlabUrl, String namespace, String repo) { 358 | Event event = new Event(); 359 | 360 | event.setType(Event.TYPE_PULL_REQUEST); 361 | event.setCreatedAt(glmergerequest.getUpdatedAt()); 362 | 363 | PullRequestPayload payload = new PullRequestPayload(); 364 | payload.setPullRequest(convertMergeRequest(glmergerequest, gitlabUrl, namespace, repo)); 365 | payload.setNumber(payload.getPullRequest().getNumber()); 366 | 367 | event.setPayload(payload); 368 | 369 | event.setId(glmergerequest.getId() + "-" + glmergerequest.getUpdatedAt().getTime()); 370 | 371 | return event; 372 | } 373 | } 374 | --------------------------------------------------------------------------------