package com.airbnb.chancery.github; import com.airbnb.chancery.GithubFailure; import com.airbnb.chancery.model.RateLimitStats; import com.airbnb.chancery.model.ReferenceCreationRequest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.sun.jersey.api.client.*; import com.yammer.metrics.Metrics; import com.yammer.metrics.core.Timer; import com.yammer.metrics.core.TimerContext; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; import javax.validation.constraints.NotNull; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriBuilder; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.concurrent.TimeUnit; @Slf4j public final class GithubClient { @NotNull private final WebResource resource; @NotNull private final ObjectMapper mapper = new ObjectMapper(); @NonNull private final Timer downloadTimer = Metrics.newTimer(getClass(), "download", TimeUnit.SECONDS, TimeUnit.SECONDS); @NonNull private final Timer referenceCreationTimer = Metrics.newTimer(getClass(), "create-reference", TimeUnit.SECONDS, TimeUnit.SECONDS); public GithubClient(final @NotNull Client client, final @Nullable String oAuth2Token) { client.setFollowRedirects(true); resource = client.resource("https://api.github.com/"); resource.addFilter(new UserAgentFilter()); if (oAuth2Token != null && !oAuth2Token.isEmpty()) { final String authValue = "token " + oAuth2Token; resource.addFilter(new AuthorizationFilter(authValue)); } else { GithubClient.log.warn("No Github oAuth2 token provided"); } } public RateLimitStats getRateLimitData() throws GithubFailure.forRateLimit { try { return resource. uri(new URI("/rate_limit")). accept(MediaType.APPLICATION_JSON_TYPE). get(RateLimitStats.Container.class).getRate(); } catch (URISyntaxException e) { return null; /* mkay? */ } catch (UniformInterfaceException e) { throw new GithubFailure.forRateLimit(e); } } public void createReference(String owner, String repository, String ref, String id) throws GithubFailure.forReferenceCreation { final URI uri = UriBuilder. fromPath("/repos/{a}/{b}/git/refs"). build(owner, repository); final ReferenceCreationRequest req = new ReferenceCreationRequest(ref, id); final TimerContext time = referenceCreationTimer.time(); try { /* Github wants a Content-Length, and Jersey doesn't fancy doing that */ final byte[] payload = mapper.writeValueAsBytes(req); resource.uri(uri). type(MediaType.APPLICATION_JSON_TYPE). post(payload); } catch (JsonProcessingException | UniformInterfaceException e) { throw new GithubFailure.forReferenceCreation(e); } finally { time.stop(); } } public Path download(String owner, String repository, String id) throws IOException, GithubFailure.forDownload { final Path tempPath = Files.createTempFile("com.airbnb.chancery-githubdownload-", null); tempPath.toFile().deleteOnExit(); final URI uri = UriBuilder. fromPath("/repos/{a}/{b}/tarball/{c}"). build(owner, repository, id); log.info("Downloading {}", uri); final TimerContext time = downloadTimer.time(); try { final InputStream inputStream = resource.uri(uri). accept(MediaType.WILDCARD_TYPE). get(InputStream.class); Files.copy(inputStream, tempPath, StandardCopyOption.REPLACE_EXISTING); log.info("Downloaded {}", uri); return tempPath; } catch (UniformInterfaceException e) { throw new GithubFailure.forDownload(e); } finally { time.stop(); } } }