Skip to content

Commit

Permalink
Add Challonge proxy
Browse files Browse the repository at this point in the history
Fixes #110
  • Loading branch information
micheljung committed Sep 5, 2017
1 parent 4127d53 commit df6df5b
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 2 deletions.
86 changes: 86 additions & 0 deletions src/main/java/com/faforever/api/challonge/ChallongeController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.faforever.api.challonge;

import com.faforever.api.config.FafApiProperties;
import com.faforever.api.config.FafApiProperties.Challonge;
import com.google.common.collect.ImmutableMap;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.util.DefaultUriTemplateHandler;
import org.springframework.web.util.UriComponentsBuilder;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.CompletableFuture;

import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import static org.springframework.web.bind.annotation.RequestMethod.PUT;

/**
* Forwards all requests to the Challonge API, using the configured API key. GET requests are allowed by anyone, all
* other requests require the role {@code ROLE_TOURNAMENT_DIRECTORY}. <p><strong>CAVEAT</strong>: This controller is
* only loaded if a Challonge API key is specified.</p>
*/
@RestController
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@RequestMapping(path = ChallongeController.CHALLONGE_ROUTE)
@ConditionalOnProperty("faf-api.challonge.key")
public class ChallongeController {

public static final String CHALLONGE_READ_CACHE_NAME = "challongeRead";
static final String CHALLONGE_ROUTE = "/challonge";
private final RestTemplate restTemplate;

public ChallongeController(FafApiProperties properties) {
Challonge challonge = properties.getChallonge();

restTemplate = new RestTemplateBuilder()
.rootUri(challonge.getBaseUrl())
.uriTemplateHandler(new ChallongeUriTemplateHandler(challonge.getKey()))
.build();
}

private static String translateRoute(HttpServletRequest request) {
return ((String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE))
.replace(CHALLONGE_ROUTE, "");
}

@Async
@Cacheable(cacheNames = CHALLONGE_READ_CACHE_NAME)
@RequestMapping(path = "/**", method = GET, produces = MediaType.APPLICATION_JSON_VALUE)
public CompletableFuture<ResponseEntity<String>> get(HttpServletRequest request) {
return CompletableFuture.completedFuture(restTemplate.getForEntity(translateRoute(request), String.class, ImmutableMap.of()));
}

@Async
@Secured("ROLE_TOURNAMENT_DIRECTORY")
@RequestMapping(path = "/**", method = {POST, PUT, DELETE}, produces = MediaType.APPLICATION_JSON_VALUE)
public CompletableFuture<ResponseEntity<String>> write(@RequestBody(required = false) Object body, HttpMethod method, HttpServletRequest request) {
return CompletableFuture.completedFuture(restTemplate.exchange(translateRoute(request), method, new HttpEntity<>(body), String.class));
}

@RequiredArgsConstructor
private static class ChallongeUriTemplateHandler extends DefaultUriTemplateHandler {
private final String apiKey;

@Override
protected UriComponentsBuilder initUriComponentsBuilder(String uriTemplate) {
return super.initUriComponentsBuilder(uriTemplate).queryParam("api_key", apiKey);
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/faforever/api/clan/ClansController.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
@RequestMapping(path = ClansController.PATH)
public class ClansController {

public static final String PATH = "/clans";
static final String PATH = "/clans";
private final ClanService clanService;
private final PlayerService playerService;

Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/faforever/api/config/CacheConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import java.util.Arrays;

import static com.faforever.api.challonge.ChallongeController.CHALLONGE_READ_CACHE_NAME;
import static com.faforever.api.featuredmods.FeaturedModService.FEATURED_MODS_CACHE_NAME;
import static com.faforever.api.featuredmods.FeaturedModService.FEATURED_MOD_FILES_CACHE_NAME;
import static com.faforever.api.leaderboard.LeaderboardService.LEADERBOARD_GLOBAL_CACHE_NAME;
Expand All @@ -35,6 +36,7 @@ public CacheManager cacheManager() {
new GuavaCache("map", newBuilder().expireAfterWrite(1, MINUTES).build()),
new GuavaCache("mapVersion", newBuilder().expireAfterWrite(1, MINUTES).build()),
// Other caches
new GuavaCache(CHALLONGE_READ_CACHE_NAME, newBuilder().expireAfterWrite(5, MINUTES).build()),
new GuavaCache(LEADERBOARD_RANKED_1V1_CACHE_NAME, newBuilder().expireAfterWrite(5, MINUTES).build()),
new GuavaCache(LEADERBOARD_GLOBAL_CACHE_NAME, newBuilder().expireAfterWrite(5, MINUTES).build()),
new GuavaCache(FEATURED_MODS_CACHE_NAME, newBuilder().expireAfterWrite(5, MINUTES).build()),
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/com/faforever/api/config/FafApiProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class FafApiProperties {
private Deployment deployment = new Deployment();
private Registration registration = new Registration();
private Mail mail = new Mail();
private Challonge challonge = new Challonge();

@Data
public static class OAuth2 {
Expand Down Expand Up @@ -160,4 +161,10 @@ public static class Registration {
private String fromEmail;
private String fromName;
}

@Data
public static class Challonge {
private String baseUrl = "https://api.challonge.com";
private String key;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import javax.validation.ValidationException;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.concurrent.CompletionException;

@ControllerAdvice
@Slf4j
Expand Down Expand Up @@ -93,11 +94,14 @@ public ErrorResponse processProgrammingError(ProgrammingError ex) {
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public ErrorResponse processException(Exception ex) throws MissingServletRequestPartException {
public ErrorResponse processException(Throwable ex) throws MissingServletRequestPartException {
// If we don't rethrow, oauth authX is broken
if (ex instanceof InsufficientAuthenticationException) {
throw (InsufficientAuthenticationException) ex;
}
if (ex instanceof CompletionException) {
throw (CompletionException) ex;
}

log.warn("Internal server error", ex);

Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/config/application-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ faf-api:
mandrill-api-key: ${MANDRILL_API_KEY}
clan:
website-url-format: ${CLAN_WEBSITE_URL_FORMAT}
challonge:
key: ${CHALLONGE_KEY:}

spring:
datasource:
Expand Down

0 comments on commit df6df5b

Please sign in to comment.