Skip to content

Commit

Permalink
allow for using multiple services with the same path
Browse files Browse the repository at this point in the history
  • Loading branch information
hunterjackson committed Dec 20, 2023
1 parent 997ba46 commit da8bcf6
Show file tree
Hide file tree
Showing 9 changed files with 308 additions and 98 deletions.
6 changes: 0 additions & 6 deletions src/main/java/com/meta/cp4m/message/WAMessageHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,6 @@ public class WAMessageHandler implements MessageHandler<WAMessage> {
}
};

public WAMessageHandler(String verifyToken, String appSecret, String accessToken) {
this.verifyToken = verifyToken;
this.appSecret = appSecret;
this.accessToken = accessToken;
}

public WAMessageHandler(WAMessengerConfig config) {
this.verifyToken = config.verifyToken();
this.accessToken = config.accessToken();
Expand Down
87 changes: 87 additions & 0 deletions src/test/java/com/meta/cp4m/DummyWebServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.meta.cp4m;

import io.javalin.Javalin;
import io.javalin.http.HandlerType;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import org.checkerframework.checker.nullness.qual.Nullable;

public class DummyWebServer implements AutoCloseable {
private final Javalin javalin;
private final BlockingQueue<ReceivedRequest> receivedRequests = new LinkedBlockingDeque<>();

private DummyWebServer() {
this.javalin =
Javalin.create()
.addHandler(
HandlerType.GET,
"/<path>",
ctx ->
receivedRequests.put(
new ReceivedRequest(
ctx.path(),
ctx.body(),
ctx.contentType(),
ctx.headerMap(),
ctx.queryParamMap())))
.addHandler(
HandlerType.POST,
"/<path>",
ctx ->
receivedRequests.put(
new ReceivedRequest(
ctx.path(),
ctx.body(),
ctx.contentType(),
ctx.headerMap(),
ctx.queryParamMap())))
.start(0);
}

public static DummyWebServer create() {
return new DummyWebServer();
}

public @Nullable ReceivedRequest poll() {
return receivedRequests.poll();
}

public @Nullable ReceivedRequest poll(long milliseconds) throws InterruptedException {
return receivedRequests.poll(milliseconds, TimeUnit.MILLISECONDS);
}

public ReceivedRequest take(long milliseconds) throws InterruptedException {
return Objects.requireNonNull(receivedRequests.poll(milliseconds, TimeUnit.MILLISECONDS));
}

public int port() {
return javalin.port();
}

public Javalin javalin() {
return javalin;
}

@Override
public void close() {
javalin.close();
}

public record ReceivedRequest(
String path,
String body,
@Nullable String contentType,
Map<String, String> headerMap,
Map<String, java.util.List<String>> stringListMap) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ void inPipeline() throws IOException, URISyntaxException, InterruptedException {

// TODO: create test harness
Request request =
FBMessageRouteDetailsTest.createMessageRequest(FBMessageRouteDetailsTest.SAMPLE_MESSAGE, runner);
FBMessageHandlerTest.createMessageRequest(FBMessageHandlerTest.SAMPLE_MESSAGE, runner);
HttpResponse response = request.execute().returnResponse();
assertThat(response.getCode()).isEqualTo(200);
@Nullable OutboundRequest or = HuggingFaceLlamaRequests.poll(500, TimeUnit.MILLISECONDS);
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/com/meta/cp4m/llm/OpenAIPluginTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ void inPipeline() throws IOException, URISyntaxException, InterruptedException {

// TODO: create test harness
Request request =
FBMessageRouteDetailsTest.createMessageRequest(FBMessageRouteDetailsTest.SAMPLE_MESSAGE, runner);
FBMessageHandlerTest.createMessageRequest(FBMessageHandlerTest.SAMPLE_MESSAGE, runner);
HttpResponse response = request.execute().returnResponse();
assertThat(response.getCode()).isEqualTo(200);
@Nullable OutboundRequest or = openAIRequests.poll(500, TimeUnit.MILLISECONDS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

public class FBMessageRouteDetailsTest {
public class FBMessageHandlerTest {

/** Example message collected directly from the messenger webhook */
public static final String SAMPLE_MESSAGE =
Expand All @@ -59,15 +59,19 @@ public class FBMessageRouteDetailsTest {
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final String SAMPLE_MESSAGE_HMAC =
"sha256=8620d18213fa2612d16117b65168ef97404fa13189528014c5362fec31215985";
private static JsonNode PARSED_SAMPLE_MESSAGE;
private Javalin app;
private BlockingQueue<OutboundRequest> requests;
public static final JsonNode PARSED_SAMPLE_MESSAGE;

@BeforeAll
static void beforeAll() throws JsonProcessingException {
PARSED_SAMPLE_MESSAGE = MAPPER.readTree(SAMPLE_MESSAGE);
static {
try {
PARSED_SAMPLE_MESSAGE = MAPPER.readTree(SAMPLE_MESSAGE);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}

private Javalin app;
private BlockingQueue<OutboundRequest> requests;

private static HttpResponse getRequest(String path, int port, Map<String, String> params)
throws IOException, URISyntaxException {
URIBuilder uriBuilder =
Expand Down
59 changes: 59 additions & 0 deletions src/test/java/com/meta/cp4m/message/HandlerTestUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.meta.cp4m.message;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.meta.cp4m.Identifier;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.function.Function;
import org.apache.hc.client5.http.fluent.Request;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Method;
import org.apache.hc.core5.net.URIBuilder;

public final class HandlerTestUtils {

private static final ObjectMapper MAPPER = new ObjectMapper();

// static class, do not instantiate
private HandlerTestUtils() {}

public static Function<Identifier, URI> baseURLFactory(String path, int port) {
return identifier -> {
try {
return URIBuilder.localhost().setPort(port).appendPath(path).setScheme("http").build();
} catch (UnknownHostException | URISyntaxException e) {
throw new RuntimeException(e);
}
};
}

public static Function<JsonNode, Request> MessageRequestFactory(
Method method, String path, String appSecret, int port)
throws UnknownHostException, URISyntaxException {
Request request =
Request.create(
method,
URIBuilder.localhost().setScheme("http").appendPath(path).setPort(port).build());
return jn -> {
try {
String body = MAPPER.writeValueAsString(jn);
return request
.bodyString(body, ContentType.APPLICATION_JSON)
.setHeader("X-Hub-Signature-256", "sha256=" + MetaHandlerUtils.hmac(body, appSecret));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
};
}
}
140 changes: 140 additions & 0 deletions src/test/java/com/meta/cp4m/message/MultiServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.meta.cp4m.message;

import static org.assertj.core.api.Assertions.assertThat;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.meta.cp4m.DummyWebServer;
import com.meta.cp4m.DummyWebServer.ReceivedRequest;
import com.meta.cp4m.Identifier;
import com.meta.cp4m.Service;
import com.meta.cp4m.ServicesRunner;
import com.meta.cp4m.llm.DummyLLMPlugin;
import com.meta.cp4m.store.MemoryStore;
import com.meta.cp4m.store.MemoryStoreConfig;
import java.net.URI;
import java.util.List;
import java.util.function.Function;
import org.apache.hc.client5.http.fluent.Request;
import org.apache.hc.core5.http.Method;
import org.junit.jupiter.api.Test;

public class MultiServiceTest {

private static final ObjectMapper MAPPER = new ObjectMapper();

private static final String META_PATH = "/meta";
private final DummyWebServer metaWebServer = DummyWebServer.create();

private final Function<Identifier, URI> baseURLFactory =
HandlerTestUtils.baseURLFactory(META_PATH, metaWebServer.port());

@Test
void waAndFBTest() throws Exception {
final String path = "/path";
final String fb1VerifyToken = "fb1VerifyToken";
final String fb1AppSecret = "fb1AppSecret";
final String fb1PageAccessToken = "fb1PageAccessToken";

MemoryStore<FBMessage> fb1Store = MemoryStoreConfig.of(1, 1).toStore();
FBMessageHandler fb1Handler =
FBMessengerConfig.of(fb1VerifyToken, fb1AppSecret, fb1PageAccessToken)
.toMessageHandler()
.baseURLFactory(baseURLFactory);
DummyLLMPlugin<FBMessage> fb1Plugin = new DummyLLMPlugin<>("i'm a fb1 dummy");
Service<FBMessage> fb1Service = new Service<>(fb1Store, fb1Handler, fb1Plugin, path);

final String fb2VerifyToken = "fb2VerifyToken";
final String fb2AppSecret = "fb2AppSecret";
final String fb2PageAccessToken = "fb2PageAccessToken";

MemoryStore<FBMessage> fb2Store = MemoryStoreConfig.of(1, 1).toStore();
FBMessageHandler fb2Handler =
FBMessengerConfig.of(fb2VerifyToken, fb2AppSecret, fb2PageAccessToken)
.toMessageHandler()
.baseURLFactory(baseURLFactory);
DummyLLMPlugin<FBMessage> fb2Plugin = new DummyLLMPlugin<>("i'm a fb2 dummy");
Service<FBMessage> fb2Service = new Service<>(fb2Store, fb2Handler, fb2Plugin, path);

final String wa1VerifyToken = "wa1VerifyToken";
final String wa1AppSecret = "wa1AppSecret";
final String wa1PageAccessToken = "wa1PageAccessToken";
MemoryStore<WAMessage> wa1Store = MemoryStoreConfig.of(1, 1).toStore();
WAMessageHandler wa1Handler =
WAMessengerConfig.of(wa1VerifyToken, wa1AppSecret, wa1PageAccessToken)
.toMessageHandler()
.baseUrlFactory(baseURLFactory);
DummyLLMPlugin<WAMessage> wa1Plugin = new DummyLLMPlugin<>("i'm a wa1 dummy");
Service<WAMessage> wa1Service = new Service<>(wa1Store, wa1Handler, wa1Plugin, path);

final String wa2VerifyToken = "wa2VerifyToken";
final String wa2AppSecret = "wa2AppSecret";
final String wa2PageAccessToken = "wa2PageAccessToken";
MemoryStore<WAMessage> wa2Store = MemoryStoreConfig.of(1, 1).toStore();
WAMessageHandler wa2Handler =
WAMessengerConfig.of(wa2VerifyToken, wa2AppSecret, wa2PageAccessToken)
.toMessageHandler()
.baseUrlFactory(baseURLFactory);
DummyLLMPlugin<WAMessage> wa2Plugin = new DummyLLMPlugin<>("i'm a wa2 dummy");
Service<WAMessage> wa2Service = new Service<>(wa2Store, wa2Handler, wa2Plugin, path);

ServicesRunner runner =
ServicesRunner.newInstance()
.service(fb1Service)
.service(fb2Service)
.service(wa1Service)
.service(wa2Service)
.port(0)
.start();

// FB1 test
Function<JsonNode, Request> fb1RequestFactory =
HandlerTestUtils.MessageRequestFactory(Method.POST, path, fb1AppSecret, runner.port());
fb1RequestFactory.apply(FBMessageHandlerTest.PARSED_SAMPLE_MESSAGE).execute();
fb1Plugin.take(500);
ReceivedRequest receivedRequest = metaWebServer.take(500);
assertThat(receivedRequest.path()).isEqualTo(META_PATH);
assertThat(receivedRequest.body()).contains("i'm a fb1 dummy");

// FB2 test
Function<JsonNode, Request> fb2RequestFactory =
HandlerTestUtils.MessageRequestFactory(Method.POST, path, fb2AppSecret, runner.port());
fb2RequestFactory.apply(FBMessageHandlerTest.PARSED_SAMPLE_MESSAGE).execute();
fb2Plugin.take(500);
receivedRequest = metaWebServer.take(500);
assertThat(receivedRequest.path()).isEqualTo(META_PATH);
assertThat(receivedRequest.body()).contains("i'm a fb2 dummy");

// WA1 test
Function<JsonNode, Request> wa1RequestFactory =
HandlerTestUtils.MessageRequestFactory(Method.POST, path, wa1AppSecret, runner.port());
wa1RequestFactory.apply(MAPPER.readTree(WAMessageHandlerTest.VALID)).execute();
wa1Plugin.take(500);
receivedRequest = metaWebServer.take(500);
ReceivedRequest receivedRequest2 = metaWebServer.take(500);
assertThat(receivedRequest.path()).isEqualTo(META_PATH);
assertThat(receivedRequest2.path()).isEqualTo(META_PATH);
assertThat(List.of(receivedRequest, receivedRequest2))
.satisfiesOnlyOnce(r -> assertThat(r.body()).contains("i'm a wa1 dummy"));

// WA2 test
Function<JsonNode, Request> wa2RequestFactory =
HandlerTestUtils.MessageRequestFactory(Method.POST, path, wa2AppSecret, runner.port());
wa2RequestFactory.apply(MAPPER.readTree(WAMessageHandlerTest.VALID)).execute();
wa2Plugin.take(500);
receivedRequest = metaWebServer.take(500);
receivedRequest2 = metaWebServer.take(500);
assertThat(receivedRequest.path()).isEqualTo(META_PATH);
assertThat(receivedRequest2.path()).isEqualTo(META_PATH);
assertThat(List.of(receivedRequest, receivedRequest2))
.satisfiesOnlyOnce(r -> assertThat(r.body()).contains("i'm a wa2 dummy"));
}
}
Loading

0 comments on commit da8bcf6

Please sign in to comment.