From 6891bc12d31ba12a214865d30d0e5f4839b1b982 Mon Sep 17 00:00:00 2001 From: tanakaryo Date: Tue, 30 Jan 2024 02:49:06 +0900 Subject: [PATCH] #42 add new resources. --- .../java/test1/cloudfntest1/pom.xml | 58 ++++++++- .../src/main/java/cloudfn/App.java | 35 +++++- .../src/main/java/cloudfn/App2.java | 64 ++++++++++ .../main/java/cloudfn/GCSEventFunction.java | 37 ++++++ .../java/cloudfn/JsonEventConverterFn.java | 112 ++++++++++++++++++ .../java/cloudfn/elements/StudentProfile.java | 16 +++ .../src/main/java/cloudfn/gcsEventFunction.sh | 13 ++ .../main/java/cloudfn/jsonEventConverterFn.sh | 18 +++ .../resources/asset/JsonEventConverterFn.jpg | Bin 0 -> 14977 bytes .../drawio/JsonEventConverterFn.drawio | 73 ++++++++++++ .../java/test1/cloudfntest1/test.csv | 0 11 files changed, 420 insertions(+), 6 deletions(-) create mode 100644 services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/App2.java create mode 100644 services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/GCSEventFunction.java create mode 100644 services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/JsonEventConverterFn.java create mode 100644 services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/elements/StudentProfile.java create mode 100644 services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/gcsEventFunction.sh create mode 100644 services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/jsonEventConverterFn.sh create mode 100644 services/cloud-functions/java/test1/cloudfntest1/src/resources/asset/JsonEventConverterFn.jpg create mode 100644 services/cloud-functions/java/test1/cloudfntest1/src/resources/drawio/JsonEventConverterFn.drawio create mode 100644 services/cloud-functions/java/test1/cloudfntest1/test.csv diff --git a/services/cloud-functions/java/test1/cloudfntest1/pom.xml b/services/cloud-functions/java/test1/cloudfntest1/pom.xml index 68b3160..2eb6dc8 100644 --- a/services/cloud-functions/java/test1/cloudfntest1/pom.xml +++ b/services/cloud-functions/java/test1/cloudfntest1/pom.xml @@ -1,17 +1,29 @@ - + 4.0.0 cloudfn cloudfntest1 jar 1.0-SNAPSHOT cloudfntest1 - + 11 11 + + + + com.google.cloud + libraries-bom + 26.20.0 + pom + import + + + + junit @@ -26,6 +38,46 @@ 1.1.0 provided + + com.google.cloud + google-cloud-storage + 2.32.1 + + + com.google.cloud + google-cloudevent-types + 0.14.0 + + + com.fasterxml.jackson.core + jackson-core + 2.16.1 + + + + com.fasterxml.jackson.core + jackson-databind + 2.16.1 + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-csv + 2.16.1 + + + + org.projectlombok + lombok + 1.18.30 + provided + + + + org.apache.commons + commons-lang3 + 3.14.0 + diff --git a/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/App.java b/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/App.java index 7b37156..1044bc8 100644 --- a/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/App.java +++ b/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/App.java @@ -1,13 +1,42 @@ package cloudfn; +import java.io.File; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.csv.CsvGenerator; +import com.fasterxml.jackson.dataformat.csv.CsvMapper; +import com.fasterxml.jackson.dataformat.csv.CsvSchema; +import com.fasterxml.jackson.dataformat.csv.CsvSchema.Builder; + /** * Hello world! * */ public class App { - public static void main( String[] args ) - { - System.out.println( "Hello World!" ); + public static void main( String[] args ) throws Exception { + + String jsn = "[{\"name\": \"mosh\", \"job\" : \"Officer\"},{\"name\": \"pp\", \"job\" : \"oo\"}]"; + JsonNode jsonTree = new ObjectMapper().readTree(jsn); + Builder csvBuilder = CsvSchema.builder(); + JsonNode firstObject = jsonTree.elements().next(); + firstObject.fieldNames().forEachRemaining(fieldName -> {csvBuilder.addColumn(fieldName);}); + CsvSchema csvSchema = csvBuilder.build().withQuoteChar('"').withHeader(); + + String csv = ""; + CsvMapper csvMapper = new CsvMapper(); + csvMapper.configure(CsvGenerator.Feature.ALWAYS_QUOTE_STRINGS, true); + csvMapper.writerFor(JsonNode.class) + .with(csvSchema) + .writeValue(new File("test.csv"), jsonTree); + + String jsn2 = "{\"name\": \"mosh\", \"job\" : \" Officer\"},{\\\"name\\\": \\\"henrig\\\", \\\"job\\\" : \\\" pop\\\"}"; + ObjectMapper mapper = new ObjectMapper(); + JsonNode root = mapper.readTree(jsn2); + + System.out.println(root.get(0).toString()); + System.out.println(root.get(1).toString()); + + } } diff --git a/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/App2.java b/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/App2.java new file mode 100644 index 0000000..e5dac12 --- /dev/null +++ b/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/App2.java @@ -0,0 +1,64 @@ +package cloudfn; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.StringReader; +import org.apache.commons.lang3.StringUtils; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.csv.CsvGenerator; +import com.fasterxml.jackson.dataformat.csv.CsvMapper; +import com.fasterxml.jackson.dataformat.csv.CsvSchema; +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 cloudfn.elements.StudentProfile; + +public class App2 { + + private static final String HEADER = "\"name\",\"age\",\"sex\",\"address\",\"school\""; + + public static void main(String[] args) throws IOException { + Storage storage = StorageOptions.newBuilder().setProjectId("dataflowtest002").build().getService(); + + byte[] content = storage.readAllBytes("testcfneventjp20240129", "test.json"); + BufferedReader reader = new BufferedReader(new StringReader(new String(content))); + + StringBuilder sb = new StringBuilder(); + + sb.append(HEADER); + ObjectMapper oMapper = new ObjectMapper(); + StudentProfile student = new StudentProfile(); + CsvMapper mapper = new CsvMapper(); + mapper.configure(CsvGenerator.Feature.ALWAYS_QUOTE_STRINGS, true); + CsvSchema schema = mapper.schemaFor(StudentProfile.class) + .sortedBy("name", "age","sex","address","school") + .withQuoteChar('"') .withoutHeader(); + + String line; + while ((line = reader.readLine()) != null) { + // Object Mapper convert Json to Object. + try { + student = oMapper.readValue(line, StudentProfile.class); + } catch (Exception e) { + System.err.println("error"); + } + + // CSV Mapper conver Object to CSV. + try { + sb.append(mapper.writer(schema).writeValueAsString(student)); + } catch (Exception e) { + System.err.println("error"); + } + } + + String uploadFileName = StringUtils.replace("test.json", ".json", ".csv"); + BlobId blobId = BlobId.of("testcfneventjp20240129", uploadFileName); + BlobInfo blobInfo = BlobInfo.newBuilder(blobId).build(); + storage.createFrom(blobInfo, new ByteArrayInputStream(sb.toString().getBytes())); + } + +} diff --git a/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/GCSEventFunction.java b/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/GCSEventFunction.java new file mode 100644 index 0000000..8a41203 --- /dev/null +++ b/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/GCSEventFunction.java @@ -0,0 +1,37 @@ +package cloudfn; + +import java.nio.charset.StandardCharsets; +import java.util.logging.Logger; + +import com.google.events.cloud.storage.v1.StorageObjectData; +import com.google.protobuf.util.JsonFormat; +import com.google.cloud.functions.CloudEventsFunction; + +import io.cloudevents.CloudEvent; + +public class GCSEventFunction implements CloudEventsFunction { + + private static final Logger logger = Logger.getLogger(GCSEventFunction.class.getName()); + + @Override + public void accept(CloudEvent event) throws Exception { + logger.info("Event" + event.getId()); + logger.info("Event Type:" + event.getType()); + + if (event.getData() == null) { + logger.warning("No data found in cloud event payload."); + } + + String cloudEventData = new String(event.getData().toBytes(), StandardCharsets.UTF_8); + StorageObjectData.Builder builder = StorageObjectData.newBuilder(); + JsonFormat.parser().merge(cloudEventData, builder); + StorageObjectData data = builder.build(); + + logger.info("Bucket: " + data.getBucket()); + logger.info("File: " + data.getName()); + logger.info("Metageneration: " + data.getMetageneration()); + logger.info("Created: " + data.getTimeCreated()); + logger.info("Updated: " + data.getUpdated()); + } + +} diff --git a/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/JsonEventConverterFn.java b/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/JsonEventConverterFn.java new file mode 100644 index 0000000..55f0a43 --- /dev/null +++ b/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/JsonEventConverterFn.java @@ -0,0 +1,112 @@ +package cloudfn; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.util.logging.Logger; + +import org.apache.commons.lang3.StringUtils; + +import com.google.events.cloud.storage.v1.StorageObjectData; +import com.google.protobuf.util.JsonFormat; + +import cloudfn.elements.StudentProfile; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.csv.CsvGenerator; +import com.fasterxml.jackson.dataformat.csv.CsvMapper; +import com.fasterxml.jackson.dataformat.csv.CsvSchema; +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 io.cloudevents.CloudEvent; +import lombok.Data; + +public class JsonEventConverterFn implements CloudEventsFunction { + + private static final Logger logger = Logger.getLogger(GCSEventFunction.class.getName()); + + private static final String HEADER_RECORD = "\"name\",\"age\",\"sex\",\"address\",\"school\""; + + private static final String EXT_JSON = ".json"; + private static final String EXT_CSV = ".csv"; + + private static final char QUOTE_CHAR = '"'; + + @Override + public void accept(CloudEvent event) throws Exception { + logger.info("Event" + event.getId()); + logger.info("Event Type:" + event.getType()); + + if (event.getData() == null) { + logger.warning("No data found in cloud event payload."); + return; + } + + String cloudEventData = new String(event.getData().toBytes(), StandardCharsets.UTF_8); + StorageObjectData.Builder builder = StorageObjectData.newBuilder(); + JsonFormat.parser().merge(cloudEventData, builder); + StorageObjectData data = builder.build(); + + logger.info("Bucket: " + data.getBucket()); + logger.info("File: " + data.getName()); + logger.info("Metageneration: " + data.getMetageneration()); + logger.info("Created: " + data.getTimeCreated()); + logger.info("Updated: " + data.getUpdated()); + + // ファイル名を作成 + String uploadFileName = StringUtils.replace(data.getName(), EXT_JSON, EXT_CSV); + + // Storageオブジェクト作成 + Storage storage = StorageOptions.newBuilder() + .setProjectId(System.getenv("PROJECTID")) + .build().getService(); + + // 対象ファイルダウンロード + byte[] fileContent = storage.readAllBytes(data.getBucket(), data.getName()); + // ファイルはLF改行されているため、BufferedReaderで読み取り + BufferedReader reader = new BufferedReader(new StringReader(new String(fileContent))); + + // CSVファイル構築用StringBuilder(ヘッダレコード追加) + StringBuilder outputFileBuilder = new StringBuilder(); + outputFileBuilder.append(HEADER_RECORD); + + // ObjectMapper作成 + ObjectMapper objectMapper = new ObjectMapper(); + // CsvMapper作成(囲い文字設定有効化) + CsvMapper csvMapper = new CsvMapper(); + csvMapper.configure(CsvGenerator.Feature.ALWAYS_QUOTE_STRINGS, true); + // CsvSchema作成 + CsvSchema csvSchema = csvMapper.schemaFor(StudentProfile.class) + .sortedBy("name", "age","sex","address","school") + .withQuoteChar(QUOTE_CHAR) + .withoutHeader(); + + // JSON→CSV変換処理 + StudentProfile lineObject = new StudentProfile(); + String line; + while ((line = reader.readLine()) != null) { + try { + // 行データをオブジェクトに変換 + lineObject = objectMapper.readValue(line, StudentProfile.class); + // オブジェクトをCSV行に変換してStringBuilderにCSV行を追加 + outputFileBuilder.append(csvMapper.writer(csvSchema).writeValueAsString(lineObject)); + } catch (Exception e) { + logger.warning("Error happen when convert json to csv."); + throw e; + } + } + + // ファイルを指定のバケットにアップロード + BlobId blobId = BlobId.of(System.getenv("UPLOADBKT"), uploadFileName); + BlobInfo blobInfo = BlobInfo.newBuilder(blobId).build(); + storage.createFrom(blobInfo, new ByteArrayInputStream(outputFileBuilder.toString().getBytes())); + + logger.info("Success convert json to csv."); + } + +} diff --git a/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/elements/StudentProfile.java b/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/elements/StudentProfile.java new file mode 100644 index 0000000..939c703 --- /dev/null +++ b/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/elements/StudentProfile.java @@ -0,0 +1,16 @@ +package cloudfn.elements; + +import lombok.Data; + +@Data +public class StudentProfile { + + public StudentProfile() { + } + + private String name; + private String age; + private String sex; + private String address; + private String school; +} diff --git a/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/gcsEventFunction.sh b/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/gcsEventFunction.sh new file mode 100644 index 0000000..45fcf0e --- /dev/null +++ b/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/gcsEventFunction.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +gsutil mb -c standard -l asia-northeast1 gs://testcfneventjp20240129 + +gcloud functions deploy java-finalize-function \ +--gen2 \ +--runtime=java17 \ +--region=asia-northeast1 \ +--source=. \ +--entry-point=cloudfn.GCSEventFunction \ +--memory=512MB \ +--trigger-event-filters="type=google.cloud.storage.object.v1.finalized" \ +--trigger-event-filters="bucket=testcfneventjp20240129" \ No newline at end of file diff --git a/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/jsonEventConverterFn.sh b/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/jsonEventConverterFn.sh new file mode 100644 index 0000000..d34228a --- /dev/null +++ b/services/cloud-functions/java/test1/cloudfntest1/src/main/java/cloudfn/jsonEventConverterFn.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +gsutil mb -c standard -l asia-northeast1 gs://testcfneventjp20240129 + +gsutil mb -c standard -l asia-northeast1 gs://testuploadcfneventjp20240130 + +gcloud functions deploy json-conv-csv-fn \ +--gen2 \ +--runtime=java17 \ +--region=asia-northeast1 \ +--service-account=437530287615-compute@developer.gserviceaccount.com \ +--source=. \ +--entry-point=cloudfn.JsonEventConverterFn \ +--memory=512MB \ +--trigger-event-filters="type=google.cloud.storage.object.v1.finalized" \ +--trigger-event-filters="bucket=testcfneventjp20240129" \ +--set-env-vars="UPLOADBKT=testuploadcfneventjp20240130" \ +--set-env-vars="PROJECTID=dataflowtest002" \ No newline at end of file diff --git a/services/cloud-functions/java/test1/cloudfntest1/src/resources/asset/JsonEventConverterFn.jpg b/services/cloud-functions/java/test1/cloudfntest1/src/resources/asset/JsonEventConverterFn.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b269235b84923a003e61c58320f7e59f1d38641b GIT binary patch literal 14977 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 d!e$w*KnDd95 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/cloud-functions/java/test1/cloudfntest1/test.csv b/services/cloud-functions/java/test1/cloudfntest1/test.csv new file mode 100644 index 0000000..e69de29