Skip to content

Commit

Permalink
Merge pull request #61 from facebookincubator/hunter/mediaDetailResponse
Browse files Browse the repository at this point in the history
Improve media get response logging and remove default credential provider
  • Loading branch information
sachinkarve authored Jul 16, 2024
2 parents 217f2d1 + 845c936 commit caca655
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 29 deletions.
31 changes: 15 additions & 16 deletions src/main/java/com/meta/cp4m/S3PreProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,16 @@
import com.meta.cp4m.message.Payload;
import com.meta.cp4m.message.ThreadState;
import java.time.Instant;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.S3ClientBuilder;
import software.amazon.awssdk.services.s3.model.*;

public class S3PreProcessor<T extends Message> implements PreProcessor<T> {
Expand All @@ -32,11 +31,11 @@ public class S3PreProcessor<T extends Message> implements PreProcessor<T> {
private final String region;
private final String bucket;
private final @Nullable String textMessageAddition;
private final AwsCredentialsProvider credentials;
private final @Nullable AwsCredentialsProvider credentials;

public S3PreProcessor(
@Nullable String awsAccessKeyID,
@Nullable String awsSecretAccessKey,
String awsAccessKeyID,
String awsSecretAccessKey,
String region,
String bucket,
@Nullable String textMessageAddition) {
Expand All @@ -46,17 +45,13 @@ public S3PreProcessor(
this.bucket = bucket;
this.textMessageAddition = textMessageAddition;

@Nullable StaticCredentialsProvider staticCredentials;
if (!this.awsAccessKeyID.isEmpty() && !this.awsSecretAccessKey.isEmpty()) {
AwsSessionCredentials sessionCredentials =
AwsSessionCredentials.create(this.awsAccessKeyID, this.awsSecretAccessKey, "");
staticCredentials = StaticCredentialsProvider.create(sessionCredentials);
this.credentials = StaticCredentialsProvider.create(sessionCredentials);
} else {
staticCredentials = null;
this.credentials = null;
}

this.credentials =
Objects.requireNonNullElse(staticCredentials, DefaultCredentialsProvider.create());
}

@Override
Expand Down Expand Up @@ -84,14 +79,18 @@ public ThreadState<T> run(ThreadState<T> in) {
public void sendRequest(byte[] media, String senderID, String extension, String mimeType) {
String key = senderID + '_' + Instant.now().toEpochMilli() + '.' + extension;
LOGGER.debug("attempting to upload \"" + key + "\" file to AWS S3");
try (S3Client s3Client =
S3Client.builder()
.region(Region.of(this.region))
.credentialsProvider(this.credentials)
.build()) {
S3ClientBuilder clientBuilder = S3Client.builder().region(Region.of(this.region));
if (this.credentials != null) {
clientBuilder = clientBuilder.credentialsProvider(this.credentials);
}
try (S3Client s3Client = clientBuilder.build()) {

PutObjectRequest request =
PutObjectRequest.builder().bucket(this.bucket).key(key).contentType(mimeType).build();
LOGGER
.atDebug()
.addKeyValue("request", request)
.log("AWS S3 request created for media upload");
PutObjectResponse response = s3Client.putObject(request, RequestBody.fromBytes(media));
LOGGER
.atDebug()
Expand Down
32 changes: 19 additions & 13 deletions src/main/java/com/meta/cp4m/message/WAMessageHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
package com.meta.cp4m.message;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.meta.cp4m.Identifier;
Expand Down Expand Up @@ -82,8 +81,8 @@ private List<ThreadState<WAMessage>> post(Context ctx, WebhookPayload payload) {
case TextWebhookMessage m -> payloadValue = new Payload.Text(m.text().body());
case ImageWebhookMessage m -> {
try {
URI url = this.getUrlFromID(m.image().id());
byte[] media = this.getMediaFromUrl(url);
GetMediaIdBody mediaDetails = this.mediaDetails(m.image().id());
byte[] media = this.getMediaFromUrl(mediaDetails.url());
payloadValue = new Payload.Image(media, m.image().mimeType());
} catch (IOException | URISyntaxException e) {
throw new RuntimeException(e);
Expand All @@ -92,8 +91,8 @@ private List<ThreadState<WAMessage>> post(Context ctx, WebhookPayload payload) {

case DocumentWebhookMessage m -> {
try {
URI url = this.getUrlFromID(m.document().id());
byte[] media = this.getMediaFromUrl(url);
GetMediaIdBody mediaDetails = this.mediaDetails(m.document().id());
byte[] media = this.getMediaFromUrl(mediaDetails.url());
payloadValue = new Payload.Document(media, m.document().mimeType());
} catch (IOException | URISyntaxException e) {
throw new RuntimeException(e);
Expand Down Expand Up @@ -272,22 +271,29 @@ private void markRead(Identifier phoneNumberId, String messageId) {
}
}

private URI getUrlFromID(String mediaID) throws IOException, URISyntaxException {
return Request.get(new URIBuilder(this.baseURL).appendPath(mediaID).build())
GetMediaIdBody mediaDetails(String mediaID) throws IOException, URISyntaxException {
URI getUrl = new URIBuilder(this.baseURL).appendPath(mediaID).build();
return Request.get(getUrl)
.setHeader("Authorization", "Bearer " + accessToken)
.setHeader("appsecret_proof", appSecretProof)
.execute()
.handleResponse(
response -> {
String jsonResponse = EntityUtils.toString(response.getEntity());
GetMediaIdBody parsedResponse;
try {
String jsonResponse = EntityUtils.toString(response.getEntity());
JsonNode jsonNode = MAPPER.readTree(jsonResponse);
return new URIBuilder(jsonNode.get("url").asText());
} catch (URISyntaxException e) {
parsedResponse = MAPPER.readValue(jsonResponse, GetMediaIdBody.class);
} catch (Exception e) {
LOGGER
.atError()
.addKeyValue("url", getUrl)
.addKeyValue("response", jsonResponse)
.setCause(e)
.log("Unable to parse response from media get request");
throw new RuntimeException(e);
}
})
.build();
return parsedResponse;
});
}

private byte[] getMediaFromUrl(URI url) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
*
* 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.webhook.whatsapp;

import com.fasterxml.jackson.annotation.JsonProperty;
import java.net.URI;

public record GetMediaIdBody(
@JsonProperty("messaging_product") String messagingProduct,
@JsonProperty("mime_type") String mimeType,
@JsonProperty("url") URI url,
@JsonProperty("sha256") String sha256,
@JsonProperty("file_size") String fileSize,
@JsonProperty("id") String id) {}
23 changes: 23 additions & 0 deletions src/test/java/com/meta/cp4m/message/WAMessageHandlerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@
import com.google.common.base.Stopwatch;
import com.meta.cp4m.DummyWebServer.ReceivedRequest;
import com.meta.cp4m.Identifier;
import com.meta.cp4m.message.webhook.whatsapp.GetMediaIdBody;
import com.meta.cp4m.message.webhook.whatsapp.SendResponse;
import com.meta.cp4m.message.webhook.whatsapp.Utils;
import io.javalin.http.HandlerType;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
Expand Down Expand Up @@ -271,6 +275,25 @@ void validResponseWithoutContacts() throws IOException {
.isEqualTo("wamid.HBgLMTY1MDUwNzY1MjAVAgARGBI5QTNDQTVCM9Q0Q0Q2RTY3RTcA");
}

@Test
void mediaFetchValid() throws IOException, URISyntaxException {
final String webhookResponse =
"""
{
"messaging_product": "whatsapp",
"url": "https://example.com/image.jpg",
"mime_type": "image/jpeg",
"sha256": "f1234567890",
"file_size": "111",
"id": "1234567890"
}""";
harness.dummyWebServer().response(ctx -> ctx.method().equals(HandlerType.GET), webhookResponse);
harness.start();
WAMessageHandler handler = (WAMessageHandler) harness.handler();
GetMediaIdBody response = handler.mediaDetails("1234567890");
assertThat(response.url()).isEqualTo(new URI("https://example.com/image.jpg"));
}

@Test
void doesNotSendNonTextMessages() throws IOException, InterruptedException {
harness.start();
Expand Down

0 comments on commit caca655

Please sign in to comment.