From 4602ed3e6664dda5033fbfa7efe05a4dedede84d Mon Sep 17 00:00:00 2001 From: averikitsch Date: Mon, 24 Oct 2022 15:55:55 -0700 Subject: [PATCH 1/5] add v2 ocr sample --- functions/v2/ocr/ocr-process-image/pom.xml | 136 +++++++++++++ .../main/java/functions/OcrProcessImage.java | 188 ++++++++++++++++++ .../functions/OcrTranslateApiMessage.java | 74 +++++++ .../eventpojos/StorageObjectData.java | 61 ++++++ .../java/functions/OcrProcessImageTest.java | 82 ++++++++ functions/v2/ocr/ocr-save-result/pom.xml | 131 ++++++++++++ .../main/java/functions/OcrSaveResult.java | 82 ++++++++ .../functions/OcrTranslateApiMessage.java | 74 +++++++ .../java/functions/eventpojos/Message.java | 31 +++ .../eventpojos/MessagePublishedData.java | 24 +++ .../java/functions/OcrSaveResultTest.java | 118 +++++++++++ functions/v2/ocr/ocr-translate-text/pom.xml | 130 ++++++++++++ .../functions/OcrTranslateApiMessage.java | 74 +++++++ .../main/java/functions/OcrTranslateText.java | 128 ++++++++++++ .../java/functions/eventpojos/Message.java | 13 ++ .../eventpojos/MessagePublishedData.java | 7 + .../java/functions/OcrTranslateTextTest.java | 111 +++++++++++ 17 files changed, 1464 insertions(+) create mode 100644 functions/v2/ocr/ocr-process-image/pom.xml create mode 100644 functions/v2/ocr/ocr-process-image/src/main/java/functions/OcrProcessImage.java create mode 100644 functions/v2/ocr/ocr-process-image/src/main/java/functions/OcrTranslateApiMessage.java create mode 100644 functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/StorageObjectData.java create mode 100644 functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java create mode 100644 functions/v2/ocr/ocr-save-result/pom.xml create mode 100644 functions/v2/ocr/ocr-save-result/src/main/java/functions/OcrSaveResult.java create mode 100644 functions/v2/ocr/ocr-save-result/src/main/java/functions/OcrTranslateApiMessage.java create mode 100644 functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/Message.java create mode 100644 functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/MessagePublishedData.java create mode 100644 functions/v2/ocr/ocr-save-result/src/test/java/functions/OcrSaveResultTest.java create mode 100644 functions/v2/ocr/ocr-translate-text/pom.xml create mode 100644 functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateApiMessage.java create mode 100644 functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateText.java create mode 100644 functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/Message.java create mode 100644 functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/MessagePublishedData.java create mode 100644 functions/v2/ocr/ocr-translate-text/src/test/java/functions/OcrTranslateTextTest.java diff --git a/functions/v2/ocr/ocr-process-image/pom.xml b/functions/v2/ocr/ocr-process-image/pom.xml new file mode 100644 index 00000000000..6fa5b2d298a --- /dev/null +++ b/functions/v2/ocr/ocr-process-image/pom.xml @@ -0,0 +1,136 @@ + + + + + + 4.0.0 + + com.example.cloud.functions + functions-ocr-process-image + + + com.google.cloud.samples + shared-configuration + 1.2.0 + + + + 11 + 11 + UTF-8 + + + + + + com.google.cloud + libraries-bom + 25.4.0 + pom + import + + + + + + + com.google.cloud.functions + functions-framework-api + 1.0.4 + provided + + + io.cloudevents + cloudevents-core + 2.3.0 + + + org.projectlombok + lombok + 1.18.24 + + + com.google.cloud + google-cloud-vision + + + com.google.cloud + google-cloud-translate + + + com.google.cloud + google-cloud-pubsub + + + + + + junit + junit + 4.13.2 + test + + + com.google.truth + truth + 1.1.3 + test + + + com.google.guava + guava-testlib + 31.1-jre + test + + + + + + + + com.google.cloud.functions + function-maven-plugin + 0.10.0 + + + functions.OcrProcessImage + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M7 + + ${skipTests} + sponge_log + false + + + + + diff --git a/functions/v2/ocr/ocr-process-image/src/main/java/functions/OcrProcessImage.java b/functions/v2/ocr/ocr-process-image/src/main/java/functions/OcrProcessImage.java new file mode 100644 index 00000000000..bed90b6985f --- /dev/null +++ b/functions/v2/ocr/ocr-process-image/src/main/java/functions/OcrProcessImage.java @@ -0,0 +1,188 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package functions; + +// [START functions_ocr_process] + +import com.google.cloud.functions.CloudEventsFunction; +import com.google.cloud.pubsub.v1.Publisher; +import com.google.cloud.translate.v3.DetectLanguageRequest; +import com.google.cloud.translate.v3.DetectLanguageResponse; +import com.google.cloud.translate.v3.LocationName; +import com.google.cloud.translate.v3.TranslationServiceClient; +import com.google.cloud.vision.v1.AnnotateImageRequest; +import com.google.cloud.vision.v1.AnnotateImageResponse; +import com.google.cloud.vision.v1.Feature; +import com.google.cloud.vision.v1.Image; +import com.google.cloud.vision.v1.ImageAnnotatorClient; +import com.google.cloud.vision.v1.ImageSource; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.protobuf.ByteString; +import com.google.pubsub.v1.ProjectTopicName; +import com.google.pubsub.v1.PubsubMessage; +import functions.eventpojos.StorageObjectData; +import io.cloudevents.CloudEvent; +import java.io.IOException; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; + +// [END functions_ocr_process] + +// [START functions_ocr_setup] +public class OcrProcessImage implements CloudEventsFunction { + // TODO set these environment variables + private static final String PROJECT_ID = System.getenv("GCP_PROJECT"); + private static final String TRANSLATE_TOPIC_NAME = System.getenv("TRANSLATE_TOPIC"); + private static final String[] TO_LANGS = System.getenv("TO_LANG").split(","); + + private static final Logger logger = Logger.getLogger(OcrProcessImage.class.getName()); + private static final String LOCATION_NAME = LocationName.of(PROJECT_ID, "global").toString(); + private Publisher publisher; + + public OcrProcessImage() throws IOException { + publisher = Publisher.newBuilder(ProjectTopicName.of(PROJECT_ID, TRANSLATE_TOPIC_NAME)).build(); + } + + // Create custom deserializer to handle timestamps in event data + class DateDeserializer implements JsonDeserializer { + @Override + public OffsetDateTime deserialize( + JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + return OffsetDateTime.parse(json.getAsString()); + } + } + Gson gson = + new GsonBuilder().registerTypeAdapter(OffsetDateTime.class, new DateDeserializer()).create(); + // [END functions_ocr_setup] + + // [START functions_ocr_process] + @Override + public void accept(CloudEvent event) { + // Unmarshal data from CloudEvent + StorageObjectData gcsEvent = + gson.fromJson( + new String(event.getData().toBytes(), StandardCharsets.UTF_8), StorageObjectData.class); + String bucket = gcsEvent.getBucket(); + if (bucket == null) { + throw new IllegalArgumentException("Missing bucket parameter"); + } + String filename = gcsEvent.getName(); + if (filename == null) { + throw new IllegalArgumentException("Missing name parameter"); + } + + detectText(bucket, filename); + } + // [END functions_ocr_process] + + // [START functions_ocr_detect] + private void detectText(String bucket, String filename) { + logger.info("Looking for text in image " + filename); + + List visionRequests = new ArrayList<>(); + String gcsPath = String.format("gs://%s/%s", bucket, filename); + + ImageSource imgSource = ImageSource.newBuilder().setGcsImageUri(gcsPath).build(); + Image img = Image.newBuilder().setSource(imgSource).build(); + + Feature textFeature = Feature.newBuilder().setType(Feature.Type.TEXT_DETECTION).build(); + AnnotateImageRequest visionRequest = + AnnotateImageRequest.newBuilder().addFeatures(textFeature).setImage(img).build(); + visionRequests.add(visionRequest); + + // Detect text in an image using the Cloud Vision API + AnnotateImageResponse visionResponse; + try (ImageAnnotatorClient client = ImageAnnotatorClient.create()) { + visionResponse = client.batchAnnotateImages(visionRequests).getResponses(0); + if (visionResponse == null || !visionResponse.hasFullTextAnnotation()) { + logger.info(String.format("Image %s contains no text", filename)); + return; + } + + if (visionResponse.hasError()) { + // Log error + logger.log( + Level.SEVERE, "Error in vision API call: " + visionResponse.getError().getMessage()); + return; + } + } catch (IOException e) { + // Log error (since IOException cannot be thrown by a Cloud Function) + logger.log(Level.SEVERE, "Error detecting text: " + e.getMessage(), e); + return; + } + + String text = visionResponse.getFullTextAnnotation().getText(); + logger.info("Extracted text from image: " + text); + + // Detect language using the Cloud Translation API + DetectLanguageRequest languageRequest = + DetectLanguageRequest.newBuilder() + .setParent(LOCATION_NAME) + .setMimeType("text/plain") + .setContent(text) + .build(); + DetectLanguageResponse languageResponse; + try (TranslationServiceClient client = TranslationServiceClient.create()) { + languageResponse = client.detectLanguage(languageRequest); + } catch (IOException e) { + // Log error (since IOException cannot be thrown by a function) + logger.log(Level.SEVERE, "Error detecting language: " + e.getMessage(), e); + return; + } + + if (languageResponse.getLanguagesCount() == 0) { + logger.info("No languages were detected for text: " + text); + return; + } + + String languageCode = languageResponse.getLanguages(0).getLanguageCode(); + logger.info(String.format("Detected language %s for file %s", languageCode, filename)); + + // Send a Pub/Sub translation request for every language we're going to translate to + for (String targetLanguage : TO_LANGS) { + logger.info("Sending translation request for language " + targetLanguage); + OcrTranslateApiMessage message = new OcrTranslateApiMessage(text, filename, targetLanguage); + ByteString byteStr = ByteString.copyFrom(message.toPubsubData()); + PubsubMessage pubsubApiMessage = PubsubMessage.newBuilder().setData(byteStr).build(); + try { + publisher.publish(pubsubApiMessage).get(); + } catch (InterruptedException | ExecutionException e) { + // Log error + logger.log(Level.SEVERE, "Error publishing translation request: " + e.getMessage(), e); + return; + } + } + } + // [END functions_ocr_detect] + + // [START functions_ocr_process] + // [START functions_ocr_setup] +} +// [END functions_ocr_setup] +// [END functions_ocr_process] diff --git a/functions/v2/ocr/ocr-process-image/src/main/java/functions/OcrTranslateApiMessage.java b/functions/v2/ocr/ocr-process-image/src/main/java/functions/OcrTranslateApiMessage.java new file mode 100644 index 00000000000..cd880b9a3c7 --- /dev/null +++ b/functions/v2/ocr/ocr-process-image/src/main/java/functions/OcrTranslateApiMessage.java @@ -0,0 +1,74 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package functions; + +// [START functions_ocr_translate_pojo] + +import com.google.gson.Gson; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; + +// Object for storing OCR translation requests +public class OcrTranslateApiMessage { + private static final Gson gson = new Gson(); + + private String text; + private String filename; + private String lang; + + public OcrTranslateApiMessage(String text, String filename, String lang) { + if (text == null) { + throw new IllegalArgumentException("Missing text parameter"); + } + if (filename == null) { + throw new IllegalArgumentException("Missing filename parameter"); + } + if (lang == null) { + throw new IllegalArgumentException("Missing lang parameter"); + } + + this.text = text; + this.filename = filename; + this.lang = lang; + } + + public String getText() { + return text; + } + + public String getFilename() { + return filename; + } + + public String getLang() { + return lang; + } + + public static OcrTranslateApiMessage fromPubsubData(byte[] data) { + String jsonStr = new String(Base64.getDecoder().decode(data), StandardCharsets.UTF_8); + Map jsonMap = gson.fromJson(jsonStr, Map.class); + + return new OcrTranslateApiMessage( + jsonMap.get("text"), jsonMap.get("filename"), jsonMap.get("lang")); + } + + public byte[] toPubsubData() { + return gson.toJson(this).getBytes(StandardCharsets.UTF_8); + } +} +// [END functions_ocr_translate_pojo] diff --git a/functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/StorageObjectData.java b/functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/StorageObjectData.java new file mode 100644 index 00000000000..e8957d3cbea --- /dev/null +++ b/functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/StorageObjectData.java @@ -0,0 +1,61 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package functions.eventpojos; + +import java.time.OffsetDateTime; +import java.util.Map; + +// Represents an object within Cloud Storage +// https://cloud.google.com/storage/docs/json_api/v1/objects +@lombok.Data +public class StorageObjectData { + private String bucket; + private String cacheControl; + private Long componentCount; + private String contentDisposition; + private String contentEncoding; + private String contentLanguage; + private String contentType; + private String crc32C; + private CustomerEncryption customerEncryption; + private String etag; + private Boolean eventBasedHold; + private Long generation; + private String id; + private String kind; + private String kmsKeyName; + private String md5Hash; + private String mediaLink; + private Map metadata; + private Long metageneration; + private String name; + private OffsetDateTime retentionExpirationTime; + private String selfLink; + private Long size; + private String storageClass; + private Boolean temporaryHold; + private OffsetDateTime timeCreated; + private OffsetDateTime timeDeleted; + private OffsetDateTime timeStorageClassUpdated; + private OffsetDateTime updated; + + @lombok.Data + public class CustomerEncryption { + private String encryptionAlgorithm; + private String keySha256; + } +} diff --git a/functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java b/functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java new file mode 100644 index 00000000000..b2abda15d53 --- /dev/null +++ b/functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java @@ -0,0 +1,82 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package functions; + +import com.google.common.testing.TestLogHandler; +import com.google.common.truth.Truth; +import com.google.gson.Gson; +import functions.eventpojos.GcsEvent; +import functions.eventpojos.StorageObjectData; +import io.cloudevents.CloudEvent; +import io.cloudevents.core.builder.CloudEventBuilder; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; + +public class OcrProcessImageTest { + private static String FUNCTIONS_BUCKET = "nodejs-docs-samples-tests"; + + private static final Logger logger = Logger.getLogger( + OcrProcessImage.class.getName()); + + private static final TestLogHandler LOG_HANDLER = new TestLogHandler(); + + private static OcrProcessImage sampleUnderTest; + + @BeforeClass + public static void setUpClass() throws IOException { + sampleUnderTest = new OcrProcessImage(); + logger.addHandler(LOG_HANDLER); + } + + @After + public void afterTest() { + LOG_HANDLER.clear(); + } + + @Test(expected = IllegalArgumentException.class) + public void functionsOcrProcess_shouldValidateParams() throws IOException, URISyntaxException { + StorageObjectData data = new StorageObjectData(); + Gson gson = new Gson(); + CloudEvent event = CloudEventBuilder.v1().withId("000").withType("google.cloud.storage.object.v1.finalized").withSource(new URI("curl-command")).withData("application/json", gson.toJson(data).getBytes()).build(); + + sampleUnderTest.accept(event); + } + + @Test + public void functionsOcrProcess_shouldDetectText() throws IOException, URISyntaxException { + StorageObjectData data = new StorageObjectData(); + data.setBucket(FUNCTIONS_BUCKET); + data.setName("wakeupcat.jpg"); + Gson gson = new Gson(); + CloudEvent event = CloudEventBuilder.v1().withId("000").withType("google.cloud.storage.object.v1.finalized").withSource(new URI("curl-command")).withData("application/json", gson.toJson(data).getBytes()).build(); + + sampleUnderTest.accept(event); + + List logs = LOG_HANDLER.getStoredLogRecords(); + Truth.assertThat(logs.get(1).getMessage()).contains( + "Extracted text from image: Wake up human!"); + Truth.assertThat(logs.get(2).getMessage()).contains( + "Detected language en for file wakeupcat.jpg"); + } +} diff --git a/functions/v2/ocr/ocr-save-result/pom.xml b/functions/v2/ocr/ocr-save-result/pom.xml new file mode 100644 index 00000000000..7c0b311bc86 --- /dev/null +++ b/functions/v2/ocr/ocr-save-result/pom.xml @@ -0,0 +1,131 @@ + + + + + + 4.0.0 + + com.example.cloud.functions + functions-ocr-save-result + + + com.google.cloud.samples + shared-configuration + 1.2.0 + + + + 11 + 11 + UTF-8 + + + + + + com.google.cloud + libraries-bom + 26.1.3 + pom + import + + + + + + + com.google.cloud.functions + functions-framework-api + 1.0.4 + provided + + + com.google.cloud + google-cloud-storage + + + io.cloudevents + cloudevents-core + 2.4.0 + + + org.projectlombok + lombok + 1.18.24 + + + com.google.code.gson + gson + 2.9.1 + + + + junit + junit + 4.13.2 + test + + + com.google.truth + truth + 1.1.3 + test + + + com.google.guava + guava-testlib + 31.1-jre + test + + + + + + + + com.google.cloud.functions + function-maven-plugin + 0.10.0 + + + functions.OcrSaveResult + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M7 + + ${skipTests} + sponge_log + false + + + + + diff --git a/functions/v2/ocr/ocr-save-result/src/main/java/functions/OcrSaveResult.java b/functions/v2/ocr/ocr-save-result/src/main/java/functions/OcrSaveResult.java new file mode 100644 index 00000000000..4f2fae681a6 --- /dev/null +++ b/functions/v2/ocr/ocr-save-result/src/main/java/functions/OcrSaveResult.java @@ -0,0 +1,82 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package functions; + +// [START functions_ocr_save] + +import com.google.cloud.functions.CloudEventsFunction; +import com.google.cloud.storage.BlobId; +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import functions.eventpojos.MessagePublishedData; +import io.cloudevents.CloudEvent; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.time.OffsetDateTime; +import java.util.logging.Logger; + +public class OcrSaveResult implements CloudEventsFunction { + // TODO set this environment variable + private static final String RESULT_BUCKET = System.getenv("RESULT_BUCKET"); + + private static final Storage STORAGE = StorageOptions.getDefaultInstance().getService(); + private static final Logger logger = Logger.getLogger(OcrSaveResult.class.getName()); + + // Configure Gson with custom deserializer to handle timestamps in event data + class DateDeserializer implements JsonDeserializer { + @Override + public OffsetDateTime deserialize( + JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + return OffsetDateTime.parse(json.getAsString()); + } + } + + Gson gson = + new GsonBuilder().registerTypeAdapter(OffsetDateTime.class, new DateDeserializer()).create(); + + @Override + public void accept(CloudEvent event) { + // Unmarshal data from CloudEvent + MessagePublishedData data = + gson.fromJson( + new String(event.getData().toBytes(), StandardCharsets.UTF_8), + MessagePublishedData.class); + OcrTranslateApiMessage ocrMessage = + OcrTranslateApiMessage.fromPubsubData( + data.getMessage().getData().getBytes(StandardCharsets.UTF_8)); + + logger.info("Received request to save file " + ocrMessage.getFilename()); + + String newFileName = + String.format("%s_to_%s.txt", ocrMessage.getFilename(), ocrMessage.getLang()); + + // Save file to RESULT_BUCKET with name newFileName + logger.info(String.format("Saving result to %s in bucket %s", newFileName, RESULT_BUCKET)); + BlobInfo blobInfo = BlobInfo.newBuilder(BlobId.of(RESULT_BUCKET, newFileName)).build(); + STORAGE.create(blobInfo, ocrMessage.getText().getBytes(StandardCharsets.UTF_8)); + logger.info("File saved"); + } +} +// [END functions_ocr_save] diff --git a/functions/v2/ocr/ocr-save-result/src/main/java/functions/OcrTranslateApiMessage.java b/functions/v2/ocr/ocr-save-result/src/main/java/functions/OcrTranslateApiMessage.java new file mode 100644 index 00000000000..cd880b9a3c7 --- /dev/null +++ b/functions/v2/ocr/ocr-save-result/src/main/java/functions/OcrTranslateApiMessage.java @@ -0,0 +1,74 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package functions; + +// [START functions_ocr_translate_pojo] + +import com.google.gson.Gson; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; + +// Object for storing OCR translation requests +public class OcrTranslateApiMessage { + private static final Gson gson = new Gson(); + + private String text; + private String filename; + private String lang; + + public OcrTranslateApiMessage(String text, String filename, String lang) { + if (text == null) { + throw new IllegalArgumentException("Missing text parameter"); + } + if (filename == null) { + throw new IllegalArgumentException("Missing filename parameter"); + } + if (lang == null) { + throw new IllegalArgumentException("Missing lang parameter"); + } + + this.text = text; + this.filename = filename; + this.lang = lang; + } + + public String getText() { + return text; + } + + public String getFilename() { + return filename; + } + + public String getLang() { + return lang; + } + + public static OcrTranslateApiMessage fromPubsubData(byte[] data) { + String jsonStr = new String(Base64.getDecoder().decode(data), StandardCharsets.UTF_8); + Map jsonMap = gson.fromJson(jsonStr, Map.class); + + return new OcrTranslateApiMessage( + jsonMap.get("text"), jsonMap.get("filename"), jsonMap.get("lang")); + } + + public byte[] toPubsubData() { + return gson.toJson(this).getBytes(StandardCharsets.UTF_8); + } +} +// [END functions_ocr_translate_pojo] diff --git a/functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/Message.java b/functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/Message.java new file mode 100644 index 00000000000..9f11b604df7 --- /dev/null +++ b/functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/Message.java @@ -0,0 +1,31 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package functions.eventpojos; + +import java.time.OffsetDateTime; +import java.util.Map; + +// Represents a PubSub message +// https://cloud.google.com/pubsub/docs/reference/rest/v1/PubsubMessage +@lombok.Data +public class Message { + private Map attributes; + private String data; + private String messageID; + private String orderingKey; + private OffsetDateTime publishTime; +} \ No newline at end of file diff --git a/functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/MessagePublishedData.java b/functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/MessagePublishedData.java new file mode 100644 index 00000000000..cb02c1bec81 --- /dev/null +++ b/functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/MessagePublishedData.java @@ -0,0 +1,24 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package functions.eventpojos; + +// The event data when a message is published to a topic. +@lombok.Data +public class MessagePublishedData { + private Message message; + private String subscription; +} diff --git a/functions/v2/ocr/ocr-save-result/src/test/java/functions/OcrSaveResultTest.java b/functions/v2/ocr/ocr-save-result/src/test/java/functions/OcrSaveResultTest.java new file mode 100644 index 00000000000..20a26def270 --- /dev/null +++ b/functions/v2/ocr/ocr-save-result/src/test/java/functions/OcrSaveResultTest.java @@ -0,0 +1,118 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package functions; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.cloud.storage.BlobInfo; +import com.google.cloud.storage.Storage; +import com.google.cloud.storage.StorageOptions; +import com.google.common.testing.TestLogHandler; +import com.google.common.truth.Truth; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import functions.eventpojos.MessagePublishedData; +import functions.eventpojos.Message; +import io.cloudevents.CloudEvent; +import io.cloudevents.core.builder.CloudEventBuilder; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Base64; +import java.util.List; +import java.util.UUID; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class OcrSaveResultTest { + private static String RESULT_BUCKET = System.getenv("RESULT_BUCKET"); + + private static final Logger logger = Logger.getLogger(OcrSaveResult.class.getName()); + + private static final TestLogHandler LOG_HANDLER = new TestLogHandler(); + + private static final Gson gson = new Gson(); + + private static final Storage STORAGE = StorageOptions.getDefaultInstance().getService(); + private static final String RANDOM_STRING = UUID.randomUUID().toString(); + + @BeforeClass + public static void setUpClass() { + assertThat(RESULT_BUCKET).isNotNull(); + logger.addHandler(LOG_HANDLER); + } + + @After + public void afterTest() { + LOG_HANDLER.clear(); + } + + @AfterClass + public static void tearDownClass() { + String deletedFilename = String.format("test-%s.jpg_to_es.txt", RANDOM_STRING); + STORAGE.delete(RESULT_BUCKET, deletedFilename); + } + + @Test(expected = IllegalArgumentException.class) + public void functionsOcrSave_shouldValidateParams() throws IOException, URISyntaxException { + MessagePublishedData data = new MessagePublishedData(); + Message message = new Message(); + message.setData(new String(Base64.getEncoder().encode("{}".getBytes()))); + data.setMessage(message); + + CloudEvent event = CloudEventBuilder.v1().withId("000").withType("google.cloud.pubsub.topic.v1.messagePublished").withSource(new URI("curl-command")).withData("application/json", gson.toJson(data).getBytes()).build(); + + new OcrSaveResult().accept(event); + } + + @Test + public void functionsOcrSave_shouldPublishTranslatedText() throws IOException, URISyntaxException { + String text = "Wake up human!"; + String filename = String.format("test-%s.jpg", RANDOM_STRING); + String lang = "es"; + + JsonObject dataJson = new JsonObject(); + dataJson.addProperty("text", text); + dataJson.addProperty("filename", filename); + dataJson.addProperty("lang", lang); + + MessagePublishedData data = new MessagePublishedData(); + Message message = new Message(); + message.setData(new String(Base64.getEncoder().encode(gson.toJson(dataJson).getBytes()))); + data.setMessage(message); + CloudEvent event = CloudEventBuilder.v1().withId("000").withType("google.cloud.pubsub.topic.v1.messagePublished").withSource(new URI("curl-command")).withData("application/json", gson.toJson(data).getBytes()).build(); + + new OcrSaveResult().accept(event); + + String resultFilename = filename + "_to_es.txt"; + + // Check log messages + List logs = LOG_HANDLER.getStoredLogRecords(); + String expectedMessage = String.format( + "Saving result to %s in bucket %s", resultFilename, RESULT_BUCKET); + Truth.assertThat(LOG_HANDLER.getStoredLogRecords().get(1).getMessage()).isEqualTo( + expectedMessage); + + // Check that file was written + BlobInfo resultBlob = STORAGE.get(RESULT_BUCKET, resultFilename); + assertThat(resultBlob).isNotNull(); + } +} diff --git a/functions/v2/ocr/ocr-translate-text/pom.xml b/functions/v2/ocr/ocr-translate-text/pom.xml new file mode 100644 index 00000000000..8cc2aeda734 --- /dev/null +++ b/functions/v2/ocr/ocr-translate-text/pom.xml @@ -0,0 +1,130 @@ + + + + + + 4.0.0 + + com.example.cloud.functions + functions-ocr-translate-text + + + com.google.cloud.samples + shared-configuration + 1.2.0 + + + + 11 + 11 + UTF-8 + + + + + + com.google.cloud + libraries-bom + 25.4.0 + pom + import + + + + + + + com.google.cloud.functions + functions-framework-api + 1.0.4 + provided + + + com.google.cloud + google-cloud-translate + + + com.google.cloud + google-cloud-pubsub + + + io.cloudevents + cloudevents-core + 2.3.0 + + + org.projectlombok + lombok + 1.18.24 + + + + junit + junit + 4.13.2 + test + + + com.google.truth + truth + 1.1.3 + test + + + com.google.guava + guava-testlib + 31.1-jre + test + + + + + + + + com.google.cloud.functions + function-maven-plugin + 0.10.0 + + + functions.OcrTranslateText + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M7 + + ${skipTests} + sponge_log + false + + + + + diff --git a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateApiMessage.java b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateApiMessage.java new file mode 100644 index 00000000000..cd880b9a3c7 --- /dev/null +++ b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateApiMessage.java @@ -0,0 +1,74 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package functions; + +// [START functions_ocr_translate_pojo] + +import com.google.gson.Gson; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; + +// Object for storing OCR translation requests +public class OcrTranslateApiMessage { + private static final Gson gson = new Gson(); + + private String text; + private String filename; + private String lang; + + public OcrTranslateApiMessage(String text, String filename, String lang) { + if (text == null) { + throw new IllegalArgumentException("Missing text parameter"); + } + if (filename == null) { + throw new IllegalArgumentException("Missing filename parameter"); + } + if (lang == null) { + throw new IllegalArgumentException("Missing lang parameter"); + } + + this.text = text; + this.filename = filename; + this.lang = lang; + } + + public String getText() { + return text; + } + + public String getFilename() { + return filename; + } + + public String getLang() { + return lang; + } + + public static OcrTranslateApiMessage fromPubsubData(byte[] data) { + String jsonStr = new String(Base64.getDecoder().decode(data), StandardCharsets.UTF_8); + Map jsonMap = gson.fromJson(jsonStr, Map.class); + + return new OcrTranslateApiMessage( + jsonMap.get("text"), jsonMap.get("filename"), jsonMap.get("lang")); + } + + public byte[] toPubsubData() { + return gson.toJson(this).getBytes(StandardCharsets.UTF_8); + } +} +// [END functions_ocr_translate_pojo] diff --git a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateText.java b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateText.java new file mode 100644 index 00000000000..1fb46299522 --- /dev/null +++ b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateText.java @@ -0,0 +1,128 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package functions; + +// [START functions_ocr_translate] + +import com.google.cloud.functions.CloudEventsFunction; +import com.google.cloud.pubsub.v1.Publisher; +import com.google.cloud.translate.v3.LocationName; +import com.google.cloud.translate.v3.TranslateTextRequest; +import com.google.cloud.translate.v3.TranslateTextResponse; +import com.google.cloud.translate.v3.TranslationServiceClient; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.protobuf.ByteString; +import com.google.pubsub.v1.ProjectTopicName; +import com.google.pubsub.v1.PubsubMessage; +import functions.eventpojos.MessagePublishedData; +import io.cloudevents.CloudEvent; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.time.OffsetDateTime; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.lang.reflect.Type; +import java.util.logging.Logger; + +public class OcrTranslateText implements CloudEventsFunction { + private static final Logger logger = Logger.getLogger(OcrTranslateText.class.getName()); + + // TODO set these environment variables + private static final String PROJECT_ID = getenv("GCP_PROJECT"); + private static final String RESULTS_TOPIC_NAME = getenv("RESULT_TOPIC"); + private static final String LOCATION_NAME = LocationName.of(PROJECT_ID, "global").toString(); + private Publisher publisher; + + public OcrTranslateText() throws IOException { + publisher = Publisher.newBuilder( + ProjectTopicName.of(PROJECT_ID, RESULTS_TOPIC_NAME)).build(); + } + // Create custom deserializer to handle timestamps in event data + class DateDeserializer implements JsonDeserializer { + @Override + public OffsetDateTime deserialize( + JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + return OffsetDateTime.parse(json.getAsString()); + } + } + Gson gson = + new GsonBuilder().registerTypeAdapter(OffsetDateTime.class, new DateDeserializer()).create(); + + @Override + public void accept(CloudEvent event) throws InterruptedException, IOException { + MessagePublishedData data = gson.fromJson( + new String(event.getData().toBytes(), StandardCharsets.UTF_8), MessagePublishedData.class); + OcrTranslateApiMessage ocrMessage = OcrTranslateApiMessage + .fromPubsubData(data.getMessage().getData().getBytes(StandardCharsets.UTF_8)); + + String targetLang = ocrMessage.getLang(); + logger.info("Translating text into " + targetLang); + + // Translate text to target language + String text = ocrMessage.getText(); + TranslateTextRequest request = TranslateTextRequest.newBuilder().setParent(LOCATION_NAME) + .setMimeType("text/plain").setTargetLanguageCode(targetLang).addContents(text).build(); + + TranslateTextResponse response; + try (TranslationServiceClient client = TranslationServiceClient.create()) { + response = client.translateText(request); + } catch (IOException e) { + // Log error (since IOException cannot be thrown by a function) + logger.log(Level.SEVERE, "Error translating text: " + e.getMessage(), e); + return; + } + if (response.getTranslationsCount() == 0) { + return; + } + + String translatedText = response.getTranslations(0).getTranslatedText(); + logger.info("Translated text: " + translatedText); + + // Send translated text to (subsequent) Pub/Sub topic + String filename = ocrMessage.getFilename(); + OcrTranslateApiMessage translateMessage = + new OcrTranslateApiMessage(translatedText, filename, targetLang); + try { + ByteString byteStr = ByteString.copyFrom(translateMessage.toPubsubData()); + PubsubMessage pubsubApiMessage = PubsubMessage.newBuilder().setData(byteStr).build(); + publisher.publish(pubsubApiMessage).get(); + logger.info("Text translated to " + targetLang); + } catch (InterruptedException | ExecutionException e) { + // Log error (since these exception types cannot be thrown by a function) + logger.log(Level.SEVERE, "Error publishing translation save request: " + e.getMessage(), e); + } + } + + // Avoid ungraceful deployment failures due to unset environment variables. + // If you get this warning you should redeploy with the variable set. + private static String getenv(String name) { + String value = System.getenv(name); + if (value == null) { + logger.warning("Environment variable " + name + " was not set"); + value = "MISSING"; + } + return value; + } +} +// [END functions_ocr_translate] diff --git a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/Message.java b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/Message.java new file mode 100644 index 00000000000..4610be9379b --- /dev/null +++ b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/Message.java @@ -0,0 +1,13 @@ +package functions.eventpojos; + +import java.time.OffsetDateTime; +import java.util.Map; + +@lombok.Data +public class Message { + private Map attributes; + private String data; + private String messageID; + private String orderingKey; + private OffsetDateTime publishTime; +} diff --git a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/MessagePublishedData.java b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/MessagePublishedData.java new file mode 100644 index 00000000000..a162311412b --- /dev/null +++ b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/MessagePublishedData.java @@ -0,0 +1,7 @@ +package functions.eventpojos; + +@lombok.Data +public class MessagePublishedData { + private Message message; + private String subscription; +} diff --git a/functions/v2/ocr/ocr-translate-text/src/test/java/functions/OcrTranslateTextTest.java b/functions/v2/ocr/ocr-translate-text/src/test/java/functions/OcrTranslateTextTest.java new file mode 100644 index 00000000000..0f4cd71f0dd --- /dev/null +++ b/functions/v2/ocr/ocr-translate-text/src/test/java/functions/OcrTranslateTextTest.java @@ -0,0 +1,111 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package functions; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.testing.TestLogHandler; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import functions.eventpojos.Message; +import functions.eventpojos.MessagePublishedData; +import io.cloudevents.CloudEvent; +import io.cloudevents.core.builder.CloudEventBuilder; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Base64; +import java.util.List; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class OcrTranslateTextTest { + private static final Logger logger = Logger.getLogger(OcrTranslateText.class.getName()); + + private static final TestLogHandler LOG_HANDLER = new TestLogHandler(); + + private static final Gson gson = new Gson(); + + private static OcrTranslateText sampleUnderTest; + + @BeforeClass + public static void setUpClass() throws IOException { + assertThat(System.getenv("RESULT_TOPIC")).isNotNull(); + sampleUnderTest = new OcrTranslateText(); + logger.addHandler(LOG_HANDLER); + } + + @After + public void afterTest() { + LOG_HANDLER.clear(); + } + + @Test(expected = IllegalArgumentException.class) + public void functionsOcrTranslate_shouldValidateParams() + throws IOException, URISyntaxException, InterruptedException { + MessagePublishedData data = new MessagePublishedData(); + Message message = new Message(); + message.setData(new String(Base64.getEncoder().encode("{}".getBytes()))); + data.setMessage(message); + + CloudEvent event = + CloudEventBuilder.v1() + .withId("000") + .withType("google.cloud.pubsub.topic.v1.messagePublished") + .withSource(new URI("curl-command")) + .withData("application/json", gson.toJson(data).getBytes()) + .build(); + sampleUnderTest.accept(event); + } + + @Test + public void functionsOcrTranslate_shouldTranslateText() + throws IOException, URISyntaxException, InterruptedException { + String text = "Wake up human!"; + String filename = "wakeupcat.jpg"; + String lang = "es"; + + JsonObject dataJson = new JsonObject(); + dataJson.addProperty("text", text); + dataJson.addProperty("filename", filename); + dataJson.addProperty("lang", lang); + + MessagePublishedData data = new MessagePublishedData(); + Message message = new Message(); + message.setData(new String(Base64.getEncoder().encode(gson.toJson(dataJson).getBytes()))); + data.setMessage(message); + CloudEvent event = + CloudEventBuilder.v1() + .withId("000") + .withType("google.cloud.pubsub.topic.v1.messagePublished") + .withSource(new URI("curl-command")) + .withData("application/json", gson.toJson(data).getBytes()) + .build(); + + sampleUnderTest.accept(event); + + List logs = LOG_HANDLER.getStoredLogRecords(); + assertThat(logs.get(1).getMessage()).contains("¡Despierta humano!"); + assertThat(logs.get(2).getMessage()).isEqualTo("Text translated to es"); + } +} From 756afb69bbf5d00cb8818c2db14d9ad637d57e4e Mon Sep 17 00:00:00 2001 From: averikitsch Date: Mon, 24 Oct 2022 16:14:21 -0700 Subject: [PATCH 2/5] lint --- .../main/java/functions/OcrProcessImage.java | 1 + .../eventpojos/StorageObjectData.java | 68 +++++++++---------- .../java/functions/OcrProcessImageTest.java | 30 +++++--- .../java/functions/eventpojos/Message.java | 12 ++-- .../eventpojos/MessagePublishedData.java | 4 +- .../java/functions/OcrSaveResultTest.java | 31 ++++++--- .../main/java/functions/OcrTranslateText.java | 43 +++++++----- .../java/functions/eventpojos/Message.java | 18 ++++- .../eventpojos/MessagePublishedData.java | 16 +++++ 9 files changed, 144 insertions(+), 79 deletions(-) diff --git a/functions/v2/ocr/ocr-process-image/src/main/java/functions/OcrProcessImage.java b/functions/v2/ocr/ocr-process-image/src/main/java/functions/OcrProcessImage.java index bed90b6985f..e0fd90e7c99 100644 --- a/functions/v2/ocr/ocr-process-image/src/main/java/functions/OcrProcessImage.java +++ b/functions/v2/ocr/ocr-process-image/src/main/java/functions/OcrProcessImage.java @@ -77,6 +77,7 @@ public OffsetDateTime deserialize( return OffsetDateTime.parse(json.getAsString()); } } + Gson gson = new GsonBuilder().registerTypeAdapter(OffsetDateTime.class, new DateDeserializer()).create(); // [END functions_ocr_setup] diff --git a/functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/StorageObjectData.java b/functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/StorageObjectData.java index e8957d3cbea..7bdef4549d7 100644 --- a/functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/StorageObjectData.java +++ b/functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/StorageObjectData.java @@ -23,39 +23,39 @@ // https://cloud.google.com/storage/docs/json_api/v1/objects @lombok.Data public class StorageObjectData { - private String bucket; - private String cacheControl; - private Long componentCount; - private String contentDisposition; - private String contentEncoding; - private String contentLanguage; - private String contentType; - private String crc32C; - private CustomerEncryption customerEncryption; - private String etag; - private Boolean eventBasedHold; - private Long generation; - private String id; - private String kind; - private String kmsKeyName; - private String md5Hash; - private String mediaLink; - private Map metadata; - private Long metageneration; - private String name; - private OffsetDateTime retentionExpirationTime; - private String selfLink; - private Long size; - private String storageClass; - private Boolean temporaryHold; - private OffsetDateTime timeCreated; - private OffsetDateTime timeDeleted; - private OffsetDateTime timeStorageClassUpdated; - private OffsetDateTime updated; + private String bucket; + private String cacheControl; + private Long componentCount; + private String contentDisposition; + private String contentEncoding; + private String contentLanguage; + private String contentType; + private String crc32C; + private CustomerEncryption customerEncryption; + private String etag; + private Boolean eventBasedHold; + private Long generation; + private String id; + private String kind; + private String kmsKeyName; + private String md5Hash; + private String mediaLink; + private Map metadata; + private Long metageneration; + private String name; + private OffsetDateTime retentionExpirationTime; + private String selfLink; + private Long size; + private String storageClass; + private Boolean temporaryHold; + private OffsetDateTime timeCreated; + private OffsetDateTime timeDeleted; + private OffsetDateTime timeStorageClassUpdated; + private OffsetDateTime updated; - @lombok.Data - public class CustomerEncryption { - private String encryptionAlgorithm; - private String keySha256; - } + @lombok.Data + public class CustomerEncryption { + private String encryptionAlgorithm; + private String keySha256; + } } diff --git a/functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java b/functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java index b2abda15d53..b18a5cc4bf5 100644 --- a/functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java +++ b/functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java @@ -19,7 +19,6 @@ import com.google.common.testing.TestLogHandler; import com.google.common.truth.Truth; import com.google.gson.Gson; -import functions.eventpojos.GcsEvent; import functions.eventpojos.StorageObjectData; import io.cloudevents.CloudEvent; import io.cloudevents.core.builder.CloudEventBuilder; @@ -36,8 +35,7 @@ public class OcrProcessImageTest { private static String FUNCTIONS_BUCKET = "nodejs-docs-samples-tests"; - private static final Logger logger = Logger.getLogger( - OcrProcessImage.class.getName()); + private static final Logger logger = Logger.getLogger(OcrProcessImage.class.getName()); private static final TestLogHandler LOG_HANDLER = new TestLogHandler(); @@ -58,8 +56,14 @@ public void afterTest() { public void functionsOcrProcess_shouldValidateParams() throws IOException, URISyntaxException { StorageObjectData data = new StorageObjectData(); Gson gson = new Gson(); - CloudEvent event = CloudEventBuilder.v1().withId("000").withType("google.cloud.storage.object.v1.finalized").withSource(new URI("curl-command")).withData("application/json", gson.toJson(data).getBytes()).build(); - + CloudEvent event = + CloudEventBuilder.v1() + .withId("000") + .withType("google.cloud.storage.object.v1.finalized") + .withSource(new URI("curl-command")) + .withData("application/json", gson.toJson(data).getBytes()) + .build(); + sampleUnderTest.accept(event); } @@ -69,14 +73,20 @@ public void functionsOcrProcess_shouldDetectText() throws IOException, URISyntax data.setBucket(FUNCTIONS_BUCKET); data.setName("wakeupcat.jpg"); Gson gson = new Gson(); - CloudEvent event = CloudEventBuilder.v1().withId("000").withType("google.cloud.storage.object.v1.finalized").withSource(new URI("curl-command")).withData("application/json", gson.toJson(data).getBytes()).build(); + CloudEvent event = + CloudEventBuilder.v1() + .withId("000") + .withType("google.cloud.storage.object.v1.finalized") + .withSource(new URI("curl-command")) + .withData("application/json", gson.toJson(data).getBytes()) + .build(); sampleUnderTest.accept(event); List logs = LOG_HANDLER.getStoredLogRecords(); - Truth.assertThat(logs.get(1).getMessage()).contains( - "Extracted text from image: Wake up human!"); - Truth.assertThat(logs.get(2).getMessage()).contains( - "Detected language en for file wakeupcat.jpg"); + Truth.assertThat(logs.get(1).getMessage()) + .contains("Extracted text from image: Wake up human!"); + Truth.assertThat(logs.get(2).getMessage()) + .contains("Detected language en for file wakeupcat.jpg"); } } diff --git a/functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/Message.java b/functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/Message.java index 9f11b604df7..1bc64926807 100644 --- a/functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/Message.java +++ b/functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/Message.java @@ -23,9 +23,9 @@ // https://cloud.google.com/pubsub/docs/reference/rest/v1/PubsubMessage @lombok.Data public class Message { - private Map attributes; - private String data; - private String messageID; - private String orderingKey; - private OffsetDateTime publishTime; -} \ No newline at end of file + private Map attributes; + private String data; + private String messageId; + private String orderingKey; + private OffsetDateTime publishTime; +} diff --git a/functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/MessagePublishedData.java b/functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/MessagePublishedData.java index cb02c1bec81..bc5030f29b7 100644 --- a/functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/MessagePublishedData.java +++ b/functions/v2/ocr/ocr-save-result/src/main/java/functions/eventpojos/MessagePublishedData.java @@ -19,6 +19,6 @@ // The event data when a message is published to a topic. @lombok.Data public class MessagePublishedData { - private Message message; - private String subscription; + private Message message; + private String subscription; } diff --git a/functions/v2/ocr/ocr-save-result/src/test/java/functions/OcrSaveResultTest.java b/functions/v2/ocr/ocr-save-result/src/test/java/functions/OcrSaveResultTest.java index 20a26def270..cefe52c7a64 100644 --- a/functions/v2/ocr/ocr-save-result/src/test/java/functions/OcrSaveResultTest.java +++ b/functions/v2/ocr/ocr-save-result/src/test/java/functions/OcrSaveResultTest.java @@ -25,8 +25,8 @@ import com.google.common.truth.Truth; import com.google.gson.Gson; import com.google.gson.JsonObject; -import functions.eventpojos.MessagePublishedData; import functions.eventpojos.Message; +import functions.eventpojos.MessagePublishedData; import io.cloudevents.CloudEvent; import io.cloudevents.core.builder.CloudEventBuilder; import java.io.IOException; @@ -77,14 +77,21 @@ public void functionsOcrSave_shouldValidateParams() throws IOException, URISynta Message message = new Message(); message.setData(new String(Base64.getEncoder().encode("{}".getBytes()))); data.setMessage(message); - - CloudEvent event = CloudEventBuilder.v1().withId("000").withType("google.cloud.pubsub.topic.v1.messagePublished").withSource(new URI("curl-command")).withData("application/json", gson.toJson(data).getBytes()).build(); + + CloudEvent event = + CloudEventBuilder.v1() + .withId("000") + .withType("google.cloud.pubsub.topic.v1.messagePublished") + .withSource(new URI("curl-command")) + .withData("application/json", gson.toJson(data).getBytes()) + .build(); new OcrSaveResult().accept(event); } @Test - public void functionsOcrSave_shouldPublishTranslatedText() throws IOException, URISyntaxException { + public void functionsOcrSave_shouldPublishTranslatedText() + throws IOException, URISyntaxException { String text = "Wake up human!"; String filename = String.format("test-%s.jpg", RANDOM_STRING); String lang = "es"; @@ -98,7 +105,13 @@ public void functionsOcrSave_shouldPublishTranslatedText() throws IOException, U Message message = new Message(); message.setData(new String(Base64.getEncoder().encode(gson.toJson(dataJson).getBytes()))); data.setMessage(message); - CloudEvent event = CloudEventBuilder.v1().withId("000").withType("google.cloud.pubsub.topic.v1.messagePublished").withSource(new URI("curl-command")).withData("application/json", gson.toJson(data).getBytes()).build(); + CloudEvent event = + CloudEventBuilder.v1() + .withId("000") + .withType("google.cloud.pubsub.topic.v1.messagePublished") + .withSource(new URI("curl-command")) + .withData("application/json", gson.toJson(data).getBytes()) + .build(); new OcrSaveResult().accept(event); @@ -106,10 +119,10 @@ public void functionsOcrSave_shouldPublishTranslatedText() throws IOException, U // Check log messages List logs = LOG_HANDLER.getStoredLogRecords(); - String expectedMessage = String.format( - "Saving result to %s in bucket %s", resultFilename, RESULT_BUCKET); - Truth.assertThat(LOG_HANDLER.getStoredLogRecords().get(1).getMessage()).isEqualTo( - expectedMessage); + String expectedMessage = + String.format("Saving result to %s in bucket %s", resultFilename, RESULT_BUCKET); + Truth.assertThat(LOG_HANDLER.getStoredLogRecords().get(1).getMessage()) + .isEqualTo(expectedMessage); // Check that file was written BlobInfo resultBlob = STORAGE.get(RESULT_BUCKET, resultFilename); diff --git a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateText.java b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateText.java index 1fb46299522..2c55cafed6f 100644 --- a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateText.java +++ b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateText.java @@ -1,17 +1,17 @@ /* * Copyright 2022 Google LLC * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package functions; @@ -36,12 +36,11 @@ import functions.eventpojos.MessagePublishedData; import io.cloudevents.CloudEvent; import java.io.IOException; +import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.time.OffsetDateTime; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import java.util.logging.Level; -import java.lang.reflect.Type; import java.util.logging.Logger; public class OcrTranslateText implements CloudEventsFunction { @@ -51,12 +50,13 @@ public class OcrTranslateText implements CloudEventsFunction { private static final String PROJECT_ID = getenv("GCP_PROJECT"); private static final String RESULTS_TOPIC_NAME = getenv("RESULT_TOPIC"); private static final String LOCATION_NAME = LocationName.of(PROJECT_ID, "global").toString(); + private Publisher publisher; public OcrTranslateText() throws IOException { - publisher = Publisher.newBuilder( - ProjectTopicName.of(PROJECT_ID, RESULTS_TOPIC_NAME)).build(); + publisher = Publisher.newBuilder(ProjectTopicName.of(PROJECT_ID, RESULTS_TOPIC_NAME)).build(); } + // Create custom deserializer to handle timestamps in event data class DateDeserializer implements JsonDeserializer { @Override @@ -66,23 +66,32 @@ public OffsetDateTime deserialize( return OffsetDateTime.parse(json.getAsString()); } } + Gson gson = new GsonBuilder().registerTypeAdapter(OffsetDateTime.class, new DateDeserializer()).create(); @Override public void accept(CloudEvent event) throws InterruptedException, IOException { - MessagePublishedData data = gson.fromJson( - new String(event.getData().toBytes(), StandardCharsets.UTF_8), MessagePublishedData.class); - OcrTranslateApiMessage ocrMessage = OcrTranslateApiMessage - .fromPubsubData(data.getMessage().getData().getBytes(StandardCharsets.UTF_8)); + MessagePublishedData data = + gson.fromJson( + new String(event.getData().toBytes(), StandardCharsets.UTF_8), + MessagePublishedData.class); + OcrTranslateApiMessage ocrMessage = + OcrTranslateApiMessage.fromPubsubData( + data.getMessage().getData().getBytes(StandardCharsets.UTF_8)); String targetLang = ocrMessage.getLang(); logger.info("Translating text into " + targetLang); // Translate text to target language String text = ocrMessage.getText(); - TranslateTextRequest request = TranslateTextRequest.newBuilder().setParent(LOCATION_NAME) - .setMimeType("text/plain").setTargetLanguageCode(targetLang).addContents(text).build(); + TranslateTextRequest request = + TranslateTextRequest.newBuilder() + .setParent(LOCATION_NAME) + .setMimeType("text/plain") + .setTargetLanguageCode(targetLang) + .addContents(text) + .build(); TranslateTextResponse response; try (TranslationServiceClient client = TranslationServiceClient.create()) { diff --git a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/Message.java b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/Message.java index 4610be9379b..05d527b6b6e 100644 --- a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/Message.java +++ b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/Message.java @@ -1,3 +1,19 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package functions.eventpojos; import java.time.OffsetDateTime; @@ -7,7 +23,7 @@ public class Message { private Map attributes; private String data; - private String messageID; + private String messageId; private String orderingKey; private OffsetDateTime publishTime; } diff --git a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/MessagePublishedData.java b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/MessagePublishedData.java index a162311412b..51bc04a53c4 100644 --- a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/MessagePublishedData.java +++ b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/MessagePublishedData.java @@ -1,3 +1,19 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package functions.eventpojos; @lombok.Data From f02be352f25c5c2cb4bd28b860b18671ab2aa388 Mon Sep 17 00:00:00 2001 From: averikitsch Date: Tue, 25 Oct 2022 11:06:08 -0700 Subject: [PATCH 3/5] fix for j17 --- functions/v2/ocr/ocr-process-image/pom.xml | 7 +++++- .../java/functions/OcrProcessImageTest.java | 25 +++++++++++++++++-- .../java/functions/OcrSaveResultTest.java | 21 +++++++++++++++- .../main/java/functions/OcrTranslateText.java | 2 +- .../java/functions/OcrTranslateTextTest.java | 21 +++++++++++++++- 5 files changed, 70 insertions(+), 6 deletions(-) diff --git a/functions/v2/ocr/ocr-process-image/pom.xml b/functions/v2/ocr/ocr-process-image/pom.xml index 6fa5b2d298a..3a3254751ed 100644 --- a/functions/v2/ocr/ocr-process-image/pom.xml +++ b/functions/v2/ocr/ocr-process-image/pom.xml @@ -77,7 +77,12 @@ com.google.cloud google-cloud-pubsub - + + com.google.code.gson + gson + 2.9.1 + + diff --git a/functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java b/functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java index b18a5cc4bf5..a29fc71b993 100644 --- a/functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java +++ b/functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java @@ -19,12 +19,20 @@ import com.google.common.testing.TestLogHandler; import com.google.common.truth.Truth; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; import functions.eventpojos.StorageObjectData; import io.cloudevents.CloudEvent; import io.cloudevents.core.builder.CloudEventBuilder; import java.io.IOException; +import java.lang.reflect.Type; import java.net.URI; import java.net.URISyntaxException; +import java.time.OffsetDateTime; import java.util.List; import java.util.logging.LogRecord; import java.util.logging.Logger; @@ -38,11 +46,26 @@ public class OcrProcessImageTest { private static final Logger logger = Logger.getLogger(OcrProcessImage.class.getName()); private static final TestLogHandler LOG_HANDLER = new TestLogHandler(); + // Create custom serializer to handle timestamps in event data + class DateSerializer implements JsonSerializer { + @Override + public JsonElement serialize( + OffsetDateTime time, Type typeOfSrc, JsonSerializationContext context) + throws JsonParseException { + return new JsonPrimitive(time.toString()); + } + } + + private final Gson gson = + new GsonBuilder().registerTypeAdapter(OffsetDateTime.class, new DateSerializer()).create(); private static OcrProcessImage sampleUnderTest; @BeforeClass public static void setUpClass() throws IOException { + Truth.assertThat(System.getenv("GCP_PROJECT")); + Truth.assertThat(System.getenv("TO_LANG")); + Truth.assertThat(System.getenv("TRANSLATE_TOPIC")); sampleUnderTest = new OcrProcessImage(); logger.addHandler(LOG_HANDLER); } @@ -55,7 +78,6 @@ public void afterTest() { @Test(expected = IllegalArgumentException.class) public void functionsOcrProcess_shouldValidateParams() throws IOException, URISyntaxException { StorageObjectData data = new StorageObjectData(); - Gson gson = new Gson(); CloudEvent event = CloudEventBuilder.v1() .withId("000") @@ -72,7 +94,6 @@ public void functionsOcrProcess_shouldDetectText() throws IOException, URISyntax StorageObjectData data = new StorageObjectData(); data.setBucket(FUNCTIONS_BUCKET); data.setName("wakeupcat.jpg"); - Gson gson = new Gson(); CloudEvent event = CloudEventBuilder.v1() .withId("000") diff --git a/functions/v2/ocr/ocr-save-result/src/test/java/functions/OcrSaveResultTest.java b/functions/v2/ocr/ocr-save-result/src/test/java/functions/OcrSaveResultTest.java index cefe52c7a64..7a25275c2c0 100644 --- a/functions/v2/ocr/ocr-save-result/src/test/java/functions/OcrSaveResultTest.java +++ b/functions/v2/ocr/ocr-save-result/src/test/java/functions/OcrSaveResultTest.java @@ -24,14 +24,22 @@ import com.google.common.testing.TestLogHandler; import com.google.common.truth.Truth; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; import functions.eventpojos.Message; import functions.eventpojos.MessagePublishedData; import io.cloudevents.CloudEvent; import io.cloudevents.core.builder.CloudEventBuilder; import java.io.IOException; +import java.lang.reflect.Type; import java.net.URI; import java.net.URISyntaxException; +import java.time.OffsetDateTime; import java.util.Base64; import java.util.List; import java.util.UUID; @@ -49,7 +57,18 @@ public class OcrSaveResultTest { private static final TestLogHandler LOG_HANDLER = new TestLogHandler(); - private static final Gson gson = new Gson(); + // Create custom serializer to handle timestamps in event data + class DateSerializer implements JsonSerializer { + @Override + public JsonElement serialize( + OffsetDateTime time, Type typeOfSrc, JsonSerializationContext context) + throws JsonParseException { + return new JsonPrimitive(time.toString()); + } + } + + private final Gson gson = + new GsonBuilder().registerTypeAdapter(OffsetDateTime.class, new DateSerializer()).create(); private static final Storage STORAGE = StorageOptions.getDefaultInstance().getService(); private static final String RANDOM_STRING = UUID.randomUUID().toString(); diff --git a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateText.java b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateText.java index 2c55cafed6f..dafec796657 100644 --- a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateText.java +++ b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/OcrTranslateText.java @@ -56,7 +56,7 @@ public class OcrTranslateText implements CloudEventsFunction { public OcrTranslateText() throws IOException { publisher = Publisher.newBuilder(ProjectTopicName.of(PROJECT_ID, RESULTS_TOPIC_NAME)).build(); } - + // Create custom deserializer to handle timestamps in event data class DateDeserializer implements JsonDeserializer { @Override diff --git a/functions/v2/ocr/ocr-translate-text/src/test/java/functions/OcrTranslateTextTest.java b/functions/v2/ocr/ocr-translate-text/src/test/java/functions/OcrTranslateTextTest.java index 0f4cd71f0dd..63c8eae334f 100644 --- a/functions/v2/ocr/ocr-translate-text/src/test/java/functions/OcrTranslateTextTest.java +++ b/functions/v2/ocr/ocr-translate-text/src/test/java/functions/OcrTranslateTextTest.java @@ -20,14 +20,22 @@ import com.google.common.testing.TestLogHandler; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; import functions.eventpojos.Message; import functions.eventpojos.MessagePublishedData; import io.cloudevents.CloudEvent; import io.cloudevents.core.builder.CloudEventBuilder; import java.io.IOException; +import java.lang.reflect.Type; import java.net.URI; import java.net.URISyntaxException; +import java.time.OffsetDateTime; import java.util.Base64; import java.util.List; import java.util.logging.LogRecord; @@ -44,7 +52,18 @@ public class OcrTranslateTextTest { private static final TestLogHandler LOG_HANDLER = new TestLogHandler(); - private static final Gson gson = new Gson(); + // Create custom serializer to handle timestamps in event data + class DateSerializer implements JsonSerializer { + @Override + public JsonElement serialize( + OffsetDateTime time, Type typeOfSrc, JsonSerializationContext context) + throws JsonParseException { + return new JsonPrimitive(time.toString()); + } + } + + private final Gson gson = + new GsonBuilder().registerTypeAdapter(OffsetDateTime.class, new DateSerializer()).create(); private static OcrTranslateText sampleUnderTest; From e9fdae26c615b099b881447b2a94b32d1cf14be7 Mon Sep 17 00:00:00 2001 From: averikitsch Date: Tue, 25 Oct 2022 11:13:56 -0700 Subject: [PATCH 4/5] lint --- .../src/test/java/functions/OcrProcessImageTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java b/functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java index a29fc71b993..1d88b96987b 100644 --- a/functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java +++ b/functions/v2/ocr/ocr-process-image/src/test/java/functions/OcrProcessImageTest.java @@ -46,6 +46,7 @@ public class OcrProcessImageTest { private static final Logger logger = Logger.getLogger(OcrProcessImage.class.getName()); private static final TestLogHandler LOG_HANDLER = new TestLogHandler(); + // Create custom serializer to handle timestamps in event data class DateSerializer implements JsonSerializer { @Override From 4b3120babc89ddd1c4a88eeba8b5040edea7d064 Mon Sep 17 00:00:00 2001 From: averikitsch Date: Wed, 26 Oct 2022 11:10:11 -0700 Subject: [PATCH 5/5] add comments --- .../eventpojos/CustomerEncryption.java | 25 +++++++++++++++++++ .../eventpojos/StorageObjectData.java | 6 ----- .../java/functions/eventpojos/Message.java | 2 ++ .../eventpojos/MessagePublishedData.java | 1 + 4 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/CustomerEncryption.java diff --git a/functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/CustomerEncryption.java b/functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/CustomerEncryption.java new file mode 100644 index 00000000000..3d8bdf8ba23 --- /dev/null +++ b/functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/CustomerEncryption.java @@ -0,0 +1,25 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package functions.eventpojos; + +// Metadata of customer-supplied encryption key for a Cloud Storage object +// https://cloud.google.com/storage/docs/json_api/v1/objects +@lombok.Data +public class CustomerEncryption { + private String encryptionAlgorithm; + private String keySha256; +} diff --git a/functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/StorageObjectData.java b/functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/StorageObjectData.java index 7bdef4549d7..24c3971a3a6 100644 --- a/functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/StorageObjectData.java +++ b/functions/v2/ocr/ocr-process-image/src/main/java/functions/eventpojos/StorageObjectData.java @@ -52,10 +52,4 @@ public class StorageObjectData { private OffsetDateTime timeDeleted; private OffsetDateTime timeStorageClassUpdated; private OffsetDateTime updated; - - @lombok.Data - public class CustomerEncryption { - private String encryptionAlgorithm; - private String keySha256; - } } diff --git a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/Message.java b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/Message.java index 05d527b6b6e..1bc64926807 100644 --- a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/Message.java +++ b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/Message.java @@ -19,6 +19,8 @@ import java.time.OffsetDateTime; import java.util.Map; +// Represents a PubSub message +// https://cloud.google.com/pubsub/docs/reference/rest/v1/PubsubMessage @lombok.Data public class Message { private Map attributes; diff --git a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/MessagePublishedData.java b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/MessagePublishedData.java index 51bc04a53c4..bc5030f29b7 100644 --- a/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/MessagePublishedData.java +++ b/functions/v2/ocr/ocr-translate-text/src/main/java/functions/eventpojos/MessagePublishedData.java @@ -16,6 +16,7 @@ package functions.eventpojos; +// The event data when a message is published to a topic. @lombok.Data public class MessagePublishedData { private Message message;