Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added dynamic rewriter for openApiDocs #1169

Merged
merged 11 commits into from
Jul 5, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.constraints.NotNull;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -60,6 +63,7 @@ public Outcome handleRequest(Exchange exc) throws Exception {
public Map<String, OpenAPIRecord> initializeRuleApiSpecs() {
return router.getRuleManager().getRules().stream()
.filter(this::hasOpenAPIInterceptor)
.peek(this::setSpecRewrites)
.flatMap(this::getRecordEntryStreamOrEmpty)
.collect(Collectors.toMap(
Map.Entry::getKey,
Expand All @@ -69,6 +73,24 @@ public Map<String, OpenAPIRecord> initializeRuleApiSpecs() {
));
}

protected void setSpecRewrites(Rule rule) {
var key = rule.getKey();
//noinspection OptionalGetWithoutIsPresent
getOpenAPIInterceptor(rule).get().getApiProxy().getSpecs().forEach(spec -> {
if (spec.getRewrite() != null) {
setIfNull(spec.getRewrite(), Rewrite::getPort, Rewrite::setPort, key.getPort());
setIfNull(spec.getRewrite(), Rewrite::getHost, Rewrite::setHost, key.getHost());
setIfNull(spec.getRewrite(), Rewrite::getBasePath, Rewrite::setBasePath, key.getPath());
} else {
spec.setRewrite(new Rewrite() {{
setHost(key.getHost());
setPort(key.getPort());
setBasePath(key.getPath());
}});
}
});
}

private Stream<Map.Entry<String, OpenAPIRecord>> getRecordEntryStreamOrEmpty(Rule rule) {
return getOpenAPIInterceptor(rule)
.map(ApiDocsInterceptor::getRecordEntryStream)
Expand All @@ -84,7 +106,7 @@ private boolean hasOpenAPIInterceptor(Rule rule) {
return rule.getInterceptors().stream().anyMatch(ic -> ic instanceof OpenAPIInterceptor);
}

Optional<OpenAPIInterceptor> getOpenAPIInterceptor(Rule rule) {
static Optional<OpenAPIInterceptor> getOpenAPIInterceptor(Rule rule) {
return rule.getInterceptors().stream()
.filter(ic -> ic instanceof OpenAPIInterceptor)
.map(ic -> (OpenAPIInterceptor) ic) // Previous line checks type, so cast should be fine
Expand Down Expand Up @@ -126,4 +148,11 @@ public String getLongDescription() {

return sb.toString();
}
}

public static <T, U> void setIfNull(T rewrite, Function<T, U> getter, BiConsumer<T, U> setter, U defaultValue) {
if(getter.apply(rewrite) != null) return;
if(!defaultValue.equals("*")) {
setter.accept(rewrite, defaultValue);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public String rewritePort(String port) {
}


public int getPort() {
public Integer getPort() {
return port;
}

Expand Down
54 changes: 30 additions & 24 deletions core/src/main/java/com/predic8/membrane/core/util/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,26 @@

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.predic8.membrane.core.http.*;
import jakarta.mail.internet.*;
import org.slf4j.*;

import javax.net.ssl.*;
import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

import static com.predic8.membrane.core.http.MimeType.*;
import com.predic8.membrane.core.http.Response;
import jakarta.mail.internet.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

import static com.predic8.membrane.core.http.MimeType.APPLICATION_JSON;

public class Util {

Expand Down Expand Up @@ -59,16 +66,16 @@ public static HashMap<String, String> parseSimpleJSONResponse(Response g) throws
String name = null;
while (jp.nextToken() != null) {
switch (jp.getCurrentToken()) {
case FIELD_NAME:
name = jp.getCurrentName();
break;
case VALUE_STRING:
values.put(name, jp.getText());
break;
case VALUE_NUMBER_INT:
values.put(name, "" + jp.getLongValue());
default:
break;
case FIELD_NAME:
name = jp.getCurrentName();
break;
case VALUE_STRING:
values.put(name, jp.getText());
break;
case VALUE_NUMBER_INT:
values.put(name, "" + jp.getLongValue());
default:
break;
}
}
}
Expand Down Expand Up @@ -109,7 +116,6 @@ public static ExecutorService createNewThreadPool() {
}

/**
*
* @param string String that might be separated by comma e.g. "a,b,c"
* @return mutable list
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@

import static com.predic8.membrane.core.http.MimeType.APPLICATION_PROBLEM_JSON;
import static com.predic8.membrane.core.interceptor.Outcome.RETURN;
import static com.predic8.membrane.core.openapi.serviceproxy.ApiDocsInterceptor.getOpenAPIInterceptor;
import static com.predic8.membrane.core.openapi.util.TestUtils.createProxy;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.*;

@SuppressWarnings("OptionalGetWithoutIsPresent")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ApiDocsInterceptorTest {

Expand Down Expand Up @@ -69,35 +70,35 @@ public void setUp() throws Exception {
}

@AfterEach
public void tearDown() {
void tearDown() {
router.stop();
}

@Test
public void initTest() throws Exception {
void initTest() throws Exception {
assertEquals(RETURN, interceptor.handleRequest(exc));
}

@Test
public void getOpenApiInterceptorTest() {
assertEquals("OpenAPI", interceptor.getOpenAPIInterceptor(rule).get().getDisplayName());
assertEquals(Optional.empty(), interceptor.getOpenAPIInterceptor(new APIProxy()));
void getOpenApiInterceptorTest() {
assertEquals("OpenAPI", getOpenAPIInterceptor(rule).get().getDisplayName());
assertEquals(Optional.empty(), getOpenAPIInterceptor(new APIProxy()));
}

@Test
public void initializeRuleApiSpecsTest() {
assertEquals(interceptor.getOpenAPIInterceptor(rule).get().getApiProxy().apiRecords, interceptor.initializeRuleApiSpecs());
void initializeRuleApiSpecsTest() {
assertEquals(getOpenAPIInterceptor(rule).get().getApiProxy().apiRecords, interceptor.initializeRuleApiSpecs());
}

@Test
public void initializeEmptyRuleApiSpecsTest() throws Exception {
void initializeEmptyRuleApiSpecsTest() throws Exception {
ApiDocsInterceptor adi = new ApiDocsInterceptor();
adi.init(new Router());
assertEquals(new HashMap<>(), adi.initializeRuleApiSpecs());
}

@Test
public void getHTMLOverview() throws Exception {
void getHTMLOverview() throws Exception {
exc.getRequest().setUri("/api-docs");
Header header = new Header();
header.setAccept("html");
Expand All @@ -107,20 +108,47 @@ public void getHTMLOverview() throws Exception {
}

@Test
public void getSwaggerUI() throws Exception {
void getSwaggerUI() throws Exception {
exc.getRequest().setUri("/api-docs/ui/fruit-shop-api-v2-0-0");
assertEquals(RETURN, interceptor.handleRequest(exc));
assertTrue(exc.getResponse().getBodyAsStringDecoded().contains("Swagger"));
}

@Test
public void getSwaggerUIWrongId() throws Exception {
void getSwaggerUIWrongId() throws Exception {
exc.getRequest().setUri("/api-docs/wrong-id");
assertEquals(RETURN, interceptor.handleRequest(exc));
assertEquals(404, exc.getResponse().getStatusCode());
checkHasValidProblemJSON(exc);
}

@Test
void noRewriterSetSpecRewritesTest() {
assertThrows(NullPointerException.class, () -> getRewrite().getPort());
interceptor.setSpecRewrites(rule);
Rewrite rewrite = getRewrite();
assertEquals(2000, rewrite.getPort());
assertEquals("", rewrite.getHost());
}

@Test
void rewriterSetSpecRewritesTest() {
getOpenAPIInterceptor(rule).get().getApiProxy().getSpecs().get(0).setRewrite(new Rewrite() {{
setPort(3000);
setHost("localhost");
setBasePath("/foo");
}});
interceptor.setSpecRewrites(rule);
Rewrite rewrite = getRewrite();
assertEquals(3000, rewrite.getPort());
assertEquals("/foo", rewrite.getBasePath());
assertEquals("localhost", rewrite.getHost());
}

private Rewrite getRewrite() {
return getOpenAPIInterceptor(rule).get().getApiProxy().getSpecs().get(0).getRewrite();
}

private void checkHasValidProblemJSON(Exchange exc) throws IOException {
assertEquals(APPLICATION_PROBLEM_JSON, exc.getResponse().getHeader().getContentType());
assertTrue(exc.getResponse().isJSON());
Expand All @@ -130,4 +158,37 @@ private void checkHasValidProblemJSON(Exchange exc) throws IOException {
assertTrue(json.has("title"));
assertTrue(json.has("type"));
}

@Test
void testSetPortIfNull() {
Rewrite rewrite = new Rewrite();

ApiDocsInterceptor.setIfNull(rewrite, Rewrite::getPort, Rewrite::setPort, 8080);
assertEquals(8080, rewrite.getPort());

ApiDocsInterceptor.setIfNull(rewrite, Rewrite::getPort, Rewrite::setPort, 9090);
assertEquals(8080, rewrite.getPort());
}

@Test
void testSetHostIfNull() {
Rewrite rewrite = new Rewrite();

ApiDocsInterceptor.setIfNull(rewrite, Rewrite::getHost, Rewrite::setHost, "localhost");
assertEquals("localhost", rewrite.getHost());

ApiDocsInterceptor.setIfNull(rewrite, Rewrite::getHost, Rewrite::setHost, "127.0.0.1");
assertEquals("localhost", rewrite.getHost());
}

@Test
void testSetBasePathIfNull() {
Rewrite rewrite = new Rewrite();

ApiDocsInterceptor.setIfNull(rewrite, Rewrite::getBasePath, Rewrite::setBasePath, "/api");
assertEquals("/api", rewrite.getBasePath());

ApiDocsInterceptor.setIfNull(rewrite, Rewrite::getBasePath, Rewrite::setBasePath, "/new-api");
assertEquals("/api", rewrite.getBasePath());
}
}
18 changes: 12 additions & 6 deletions distribution/conf/proxies.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@
<router>

<api port="2000">
<response>
<greaser>
<greaseJson shuffleFields="true" addAdditionalFields="true" />
</greaser>
</response>
<return />
<apiDocs />
</api>

<api port="2001" host="sdnasndasdasdasdasdasd">

<openapi location="https://developer.lufthansa.com/swagger/export/21516">
<rewrite basePath="/foo" port="20000"/>
</openapi>
</api>

<api port="2003">
<openapi location="https://raw.githubusercontent.com/openai/openai-openapi/master/openapi.yaml"/>
</api>

</router>
Expand Down