diff options
author | Martin Polden <mpolden@mpolden.no> | 2018-06-27 09:24:29 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-27 09:24:29 +0200 |
commit | 9c1f55f862eb46743f41b2a55edc50d10a72c06f (patch) | |
tree | 28c5865060d03f0b47594a55e9409a57be863232 /controller-server | |
parent | 3a0a7788ee38f63b26bd842fceb5175d15476c61 (diff) | |
parent | c9451b78686b923704126728f56c973c9767709e (diff) |
Merge pull request #6286 from vespa-engine/mpolden/rewrite-statuspage-proxy
Rewrite StatusPage proxy
Diffstat (limited to 'controller-server')
11 files changed, 389 insertions, 148 deletions
diff --git a/controller-server/pom.xml b/controller-server/pom.xml index ade85a845fd..93204845fbe 100644 --- a/controller-server/pom.xml +++ b/controller-server/pom.xml @@ -49,13 +49,6 @@ <dependency> <groupId>com.yahoo.vespa</groupId> - <artifactId>container-jersey2</artifactId> - <version>${project.version}</version> - <scope>provided</scope> - </dependency> - - <dependency> - <groupId>com.yahoo.vespa</groupId> <artifactId>serviceview</artifactId> <version>${project.version}</version> <scope>provided</scope> @@ -121,6 +114,11 @@ </dependency> <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient</artifactId> + </dependency> + + <dependency> <groupId>com.yahoo.vespa</groupId> <artifactId>config-model-api</artifactId> <version>${project.version}</version> @@ -217,12 +215,6 @@ </dependency> <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <scope>test</scope> - </dependency> - - <dependency> <groupId>com.yahoo.vespa</groupId> <artifactId>testutil</artifactId> <version>${project.version}</version> @@ -235,6 +227,12 @@ <scope>test</scope> </dependency> + <dependency> + <groupId>com.github.tomakehurst</groupId> + <artifactId>wiremock-standalone</artifactId> + <scope>test</scope> + </dependency> + </dependencies> <build> diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClient.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClient.java new file mode 100644 index 00000000000..93213172048 --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClient.java @@ -0,0 +1,117 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.restapi.statuspage; + +import com.yahoo.slime.Slime; +import com.yahoo.vespa.config.SlimeUtils; +import org.apache.http.HttpStatus; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.time.Duration; +import java.util.Objects; +import java.util.Optional; + +/** + * A basic client for the StatusPage API. + * + * @author mpolden + */ +public class StatusPageClient { + + private static final Duration requestTimeout = Duration.ofSeconds(30); + + private final URI url; + private final String key; + + private StatusPageClient(URI url, String key) { + this.url = Objects.requireNonNull(url, "url cannot be null"); + this.key = Objects.requireNonNull(key, "key cannot be null"); + } + + /** GET given page and return response body as slime */ + public Slime get(String page, Optional<String> since) { + HttpGet get = new HttpGet(pageUrl(page, since)); + try (CloseableHttpClient client = client()) { + try (CloseableHttpResponse response = client.execute(get)) { + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + throw new IllegalArgumentException("Received status " + response.getStatusLine().getStatusCode() + + " from StatusPage"); + } + byte[] body = EntityUtils.toByteArray(response.getEntity()); + return SlimeUtils.jsonToSlime(body); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + URI pageUrl(String page, Optional<String> since) { + if (!allowAccess(page)) { + throw new IllegalArgumentException("Invalid resource: '" + page + "'"); + } + URIBuilder builder = new URIBuilder(url) + .setPath("/api/v2/" + page + ".json") + .setParameter("api_key", key); + since.ifPresent(s -> builder.setParameter("since", s)); + try { + return builder.build(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + public static StatusPageClient create(URI url, String secret) { + String[] parts = secret.split(":"); + if (parts.length != 2) { + throw new IllegalArgumentException("Invalid secret"); + } + String pageId = parts[0]; + String apiKey = parts[1]; + if (isDefault(url)) { + // Rewrite URL to include page ID + try { + url = new URIBuilder(url).setHost(pageId + "." + url.getHost()).build(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + return new StatusPageClient(url, apiKey); + } + + private static CloseableHttpClient client() { + HttpClientBuilder builder = HttpClients.custom(); + RequestConfig requestConfig = RequestConfig.custom() + .setConnectionRequestTimeout((int) requestTimeout.toMillis()) + .setConnectTimeout((int) requestTimeout.toMillis()) + .setSocketTimeout((int) requestTimeout.toMillis()) + .build(); + return builder.setDefaultRequestConfig(requestConfig) + .setUserAgent("vespa-statuspage-client") + .build(); + } + + /** Returns whether given page is allowed to be accessed */ + private static boolean allowAccess(String page) { + switch (page) { + case "incidents": + case "scheduled-maintenances": + return true; + } + return false; + } + + private static boolean isDefault(URI url) { + return "statuspage.io".equals(url.getHost()); + } + +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyHandler.java new file mode 100644 index 00000000000..ad69681fe7e --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyHandler.java @@ -0,0 +1,67 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.restapi.statuspage; + +import com.google.inject.Inject; +import com.yahoo.container.jdisc.HttpRequest; +import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.container.jdisc.LoggingRequestHandler; +import com.yahoo.container.jdisc.secretstore.SecretStore; +import com.yahoo.slime.Slime; +import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse; +import com.yahoo.vespa.hosted.controller.restapi.Path; +import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse; +import com.yahoo.vespa.hosted.controller.statuspage.config.StatuspageConfig; +import com.yahoo.yolean.Exceptions; + +import java.net.URI; +import java.util.Optional; +import java.util.logging.Level; + +/** + * Proxies requests from the controller to StatusPage. + * + * @author andreer + * @author mpolden + */ +@SuppressWarnings("unused") // Handler +public class StatusPageProxyHandler extends LoggingRequestHandler { + + private static final String secretKey = "vespa_hosted.controller.statuspage_api_key"; + + private final SecretStore secretStore; + private final URI apiUrl; + + @Inject + public StatusPageProxyHandler(Context parentCtx, SecretStore secretStore, StatuspageConfig config) { + super(parentCtx); + this.secretStore = secretStore; + this.apiUrl = URI.create(config.apiUrl()); + } + + @Override + public HttpResponse handle(HttpRequest request) { + try { + switch (request.getMethod()) { + case GET: return handleGET(request); + default: return ErrorResponse.methodNotAllowed("Method '" + request.getMethod() + "' is not supported"); + } + } catch (IllegalArgumentException e) { + return ErrorResponse.badRequest(Exceptions.toMessageString(e)); + } catch (RuntimeException e) { + log.log(Level.WARNING, "Unexpected error handling '" + request.getUri() + "'", e); + return ErrorResponse.internalServerError(Exceptions.toMessageString(e)); + } + } + + private HttpResponse handleGET(HttpRequest request) { + Path path = new Path(request.getUri().getPath()); + if (!path.matches("/statuspage/v1/{page}")) { + return ErrorResponse.notFoundError("Nothing at " + path); + } + StatusPageClient client = StatusPageClient.create(apiUrl, secretStore.getSecret(secretKey)); + Optional<String> since = Optional.ofNullable(request.getProperty("since")); + Slime statusPageResponse = client.get(path.get("page"), since); + return new SlimeJsonResponse(statusPageResponse); + } + +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/restapi/impl/StatusPageResource.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/restapi/impl/StatusPageResource.java deleted file mode 100644 index 4c479267f2c..00000000000 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/restapi/impl/StatusPageResource.java +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.restapi.impl; - -import com.fasterxml.jackson.databind.JsonNode; -import com.google.inject.Inject; -import com.yahoo.container.jaxrs.annotation.Component; -import com.yahoo.container.jdisc.secretstore.SecretStore; - -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.UriBuilder; - -/** - * Proxies requests from controller to https://xxx.statuspage.io/api/v2/yyy.json?api_key=zzz[&since=YYYY-MM-DDThh:mm[:ss]±hh:mm] - * - * @author andreer - */ -@Path("/v1/") -@Produces(MediaType.APPLICATION_JSON) -public class StatusPageResource implements com.yahoo.vespa.hosted.controller.api.statuspage.StatusPageResource { - - private final Client client; - private final SecretStore secretStore; - - @Inject - public StatusPageResource(@Component SecretStore secretStore) { - this(secretStore, ClientBuilder.newClient()); - } - - protected StatusPageResource(SecretStore secretStore, Client client) { - this.secretStore = secretStore; - this.client = client; - } - - protected UriBuilder statusPageURL(String page, String since) { - String[] secrets = secretStore.getSecret("vespa_hosted.controller.statuspage_api_key").split(":"); - UriBuilder uriBuilder = UriBuilder.fromUri("https://" + secrets[0] + ".statuspage.io/api/v2/" + page + ".json?api_key=" + secrets[1]); - if (since != null) { - uriBuilder.queryParam("since", since); - } - - return uriBuilder; - } - - @Override - public JsonNode statusPage(String page, String since) { - WebTarget target = client.target(statusPageURL(page, since)); - return target.request().get(JsonNode.class); - } - -} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/restapi/impl/package-info.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/restapi/impl/package-info.java deleted file mode 100644 index dca8a22a313..00000000000 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/restapi/impl/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -/** - * @author Tony Vaagenes - */ -@ExportPackage -package com.yahoo.vespa.hosted.restapi.impl; - -import com.yahoo.osgi.annotation.ExportPackage; diff --git a/controller-server/src/main/resources/configdefinitions/statuspage.def b/controller-server/src/main/resources/configdefinitions/statuspage.def new file mode 100644 index 00000000000..db8b034f99b --- /dev/null +++ b/controller-server/src/main/resources/configdefinitions/statuspage.def @@ -0,0 +1,4 @@ +# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=vespa.hosted.controller.statuspage.config + +apiUrl string default=https://statuspage.io diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MockSecretStore.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MockSecretStore.java new file mode 100644 index 00000000000..2f934e69798 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/MockSecretStore.java @@ -0,0 +1,48 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller; + +import com.yahoo.component.AbstractComponent; +import com.yahoo.container.jdisc.secretstore.SecretStore; + +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +/** + * @author mpolden + */ +public class MockSecretStore extends AbstractComponent implements SecretStore { + + private final Map<String, TreeMap<Integer, String>> secrets = new HashMap<>(); + + public MockSecretStore setSecret(String name, String value, int version) { + TreeMap<Integer, String> values = secrets.getOrDefault(name, new TreeMap<>()); + values.put(version, value); + secrets.put(name, values); + return this; + } + + public MockSecretStore setSecret(String name, String value) { + return setSecret(name, value, 1); + } + + public MockSecretStore clear() { + secrets.clear(); + return this; + } + + @Override + public String getSecret(String key) { + TreeMap<Integer, String> values = secrets.get(key); + if (values == null || values.isEmpty()) { + return null; + } + return values.lastEntry().getValue(); + } + + @Override + public String getSecret(String key, int version) { + return secrets.getOrDefault(key, new TreeMap<>()).get(version); + } + +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilterTest.java index 93f3a4e39db..ee9c65013a8 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/athenz/filter/AthenzPrincipalFilterTest.java @@ -39,11 +39,9 @@ import static com.yahoo.vespa.athenz.tls.SignatureAlgorithm.SHA256_WITH_RSA; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static java.util.stream.Collectors.joining; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; /** * @author bjorncs @@ -146,9 +144,9 @@ public class AthenzPrincipalFilterTest { } private static void assertUnauthorized(ResponseHandlerMock responseHandler, String expectedMessageSubstring) { - assertThat(responseHandler.response, notNullValue()); - assertThat(responseHandler.response.getStatus(), equalTo(UNAUTHORIZED)); - assertThat(responseHandler.getResponseContent(), containsString(expectedMessageSubstring)); + assertNotNull(responseHandler.response);; + assertEquals(UNAUTHORIZED, responseHandler.response.getStatus()); + assertTrue(responseHandler.getResponseContent().contains(expectedMessageSubstring)); } private static class ResponseHandlerMock implements ResponseHandler { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClientTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClientTest.java new file mode 100644 index 00000000000..378e4298552 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageClientTest.java @@ -0,0 +1,44 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.restapi.statuspage; + +import org.junit.Test; + +import java.net.URI; +import java.util.Optional; + +import static org.junit.Assert.*; + +/** + * @author mpolden + */ +public class StatusPageClientTest { + + @Test + public void test_url_building() { + { + URI apiUrl = URI.create("https://statuspage.io"); + String secret = "testpage:testkey"; + assertEquals("https://testpage.statuspage.io/api/v2/incidents.json?api_key=testkey", + StatusPageClient.create(apiUrl, secret).pageUrl("incidents", Optional.empty()).toString()); + assertEquals("https://testpage.statuspage.io/api/v2/scheduled-maintenances.json?api_key=testkey&since=2015-01-01T00%3A00%2B00%3A00", + StatusPageClient.create(apiUrl, secret).pageUrl("scheduled-maintenances", + Optional.of("2015-01-01T00:00+00:00")).toString()); + } + + { + URI apiUrl = URI.create("http://foo.bar"); + assertEquals("http://foo.bar/api/v2/incidents.json?api_key=testkey", + StatusPageClient.create(apiUrl, "testpage:testkey").pageUrl("incidents", Optional.empty()).toString()); + } + + { + try { + URI apiUrl = URI.create("http://foo.bar"); + assertEquals("http://foo.bar/api/v2/incidents.json?api_key=testkey", + StatusPageClient.create(apiUrl, "").pageUrl("incidents", Optional.empty()).toString()); + fail("Expected exception"); + } catch (IllegalArgumentException ignored) {} + } + } + +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyApiTest.java new file mode 100644 index 00000000000..181e4386df6 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/statuspage/StatusPageProxyApiTest.java @@ -0,0 +1,93 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.restapi.statuspage; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.yahoo.application.Networking; +import com.yahoo.application.container.JDisc; +import com.yahoo.application.container.handler.Request; +import com.yahoo.application.container.handler.Response; +import com.yahoo.vespa.hosted.controller.MockSecretStore; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; + +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.okJson; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static org.junit.Assert.assertEquals; + +/** + * @author mpolden + */ +public class StatusPageProxyApiTest { + + private JDisc container; + + @Before + public void startContainer() { + container = JDisc.fromServicesXml(servicesXml(), Networking.disable); + secretStore().setSecret("vespa_hosted.controller.statuspage_api_key", "page-id:secret"); + } + + @After + public void stopContainer() { + container.close(); + secretStore().clear(); + } + + @Rule + public final WireMockRule wireMock = new WireMockRule(options().dynamicPort(), true); + + @Test + public void test_proxy() throws Exception { + // Invalid requests + assertResponse("/statuspage/v1/", "{\"error-code\":\"NOT_FOUND\",\"message\":\"Nothing at path '/statuspage/v1'\"}", 404); + assertResponse("/statuspage/v1/invalid", "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Invalid resource: 'invalid'\"}", 400); + assertResponse(Request.Method.POST, "/statuspage/v1/invalid", "{\"error-code\":\"METHOD_NOT_ALLOWED\",\"message\":\"Method 'POST' is not supported\"}", 405); + + // Mock responses from StatusPage + wireMock.stubFor(get(urlEqualTo("/api/v2/incidents.json?api_key=secret")) + .willReturn(okJson("{\"incidents\":[]}"))); + wireMock.stubFor(get(urlEqualTo("/api/v2/scheduled-maintenances.json?api_key=secret")) + .willReturn(okJson("{\"scheduled_maintenances\":[]}"))); + + assertResponse("/statuspage/v1/incidents", "{\"incidents\":[]}", 200); + assertResponse("/statuspage/v1/scheduled-maintenances", "{\"scheduled_maintenances\":[]}", 200); + } + + private void assertResponse(String path, String expectedResponse, int expectedStatusCode) throws IOException { + assertResponse(Request.Method.GET, path, expectedResponse, expectedStatusCode); + } + + private void assertResponse(Request.Method method, String path, String expectedResponse, int expectedStatusCode) throws IOException { + Response response = container.handleRequest(new Request("http://localhost:8080" + path, new byte[0], method)); + assertEquals(expectedResponse, response.getBodyAsString()); + assertEquals("Status code", expectedStatusCode, response.getStatus()); + assertEquals("application/json; charset=UTF-8", response.getHeaders().getFirst("Content-Type")); + } + + private MockSecretStore secretStore() { + return (MockSecretStore) container.components().getComponent(MockSecretStore.class.getName()); + } + + private String servicesXml() { + String statusPageApiUrl = "http://127.0.0.1:" + wireMock.port(); + return "<jdisc version='1.0'>\n" + + " <config name='vespa.hosted.controller.statuspage.config.statuspage'>\n" + + " <apiUrl>" + statusPageApiUrl + "</apiUrl>\n" + + " </config>\n" + + " <component id='com.yahoo.vespa.hosted.controller.MockSecretStore'/>\n" + + " <handler id='com.yahoo.vespa.hosted.controller.restapi.statuspage.StatusPageProxyHandler'>\n" + + " <binding>http://*/statuspage/v1/*</binding>\n" + + " </handler>\n" + + " <http>\n" + + " <server id='default' port='8080'/>\n" + + " </http>\n" + + "</jdisc>"; + } + +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/restapi/impl/StatusPageResourceTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/restapi/impl/StatusPageResourceTest.java deleted file mode 100644 index 429e1ea2b8d..00000000000 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/restapi/impl/StatusPageResourceTest.java +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.restapi.impl; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.yahoo.container.jdisc.secretstore.SecretStore; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Invocation; -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.UriBuilder; -import java.io.IOException; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -/** - * @author frodelu - */ -public class StatusPageResourceTest { - - private StatusPageResource statusPage; - - @Before - public void setup() throws IOException { - - Client mockClient = Mockito.mock(Client.class); - WebTarget mockTarget = Mockito.mock(WebTarget.class); - Invocation.Builder mockRequest = Mockito.mock(Invocation.Builder.class); - SecretStore secretStore = Mockito.mock(SecretStore.class); - - Mockito.when(mockClient.target(Mockito.any(UriBuilder.class))).thenReturn(mockTarget); - Mockito.when(mockTarget.request()).thenReturn(mockRequest); - Mockito.when(mockRequest.get(JsonNode.class)).thenReturn( - new ObjectMapper().readTree("{\"page\":{\"name\":\"Vespa\"}}")); - Mockito.when(secretStore.getSecret(Mockito.any(String.class))).thenReturn("testpage:testkey"); - - statusPage = new StatusPageResource(secretStore, mockClient); - } - - - @Test - public void default_url() { - UriBuilder uri = statusPage.statusPageURL("incidents", null); - assertNotNull("URI not initialized", uri); - assertEquals("https://testpage.statuspage.io/api/v2/incidents.json?api_key=testkey", uri.toTemplate()); - } - - @Test - public void url_with_since_param() { - UriBuilder uri = statusPage.statusPageURL("incidents", "2015-01-01T00:00+00:00"); - assertNotNull("URI not initialized", uri); - assertEquals("https://testpage.statuspage.io/api/v2/incidents.json?api_key=testkey&since=2015-01-01T00%3A00%2B00%3A00", uri.toTemplate()); - } - - @Test - public void valid_status_page() { - JsonNode result = statusPage.statusPage("incidents", null); - assertNotNull("No result from StatusPage.io", result); - assertEquals("Vespa", result.get("page").get("name").asText()); - } -} |