From fd22f6b6714ab8f18933aa2e5e30ac8a26727266 Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Fri, 12 Jun 2026 10:38:37 +0000 Subject: [PATCH 01/16] clean history --- .mergify.yml | 8 + build.sbt | 30 ++++ .../events/MessagePublishedData.scala | 71 ++++++++ .../events/StorageObjectData.scala | 164 ++++++++++++++++++ .../events/MessagePublishedDataSuite.scala | 37 ++++ .../events/StorageObjectDataSuite.scala | 53 ++++++ 6 files changed, 363 insertions(+) create mode 100644 google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala create mode 100644 google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala create mode 100644 google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala create mode 100644 google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala diff --git a/.mergify.yml b/.mergify.yml index aa44cd0a..7b13b5b8 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -28,6 +28,14 @@ pull_request_rules: add: - examples remove: [] +- name: Label google-cloud PRs + conditions: + - files~=^google-cloud/ + actions: + label: + add: + - google-cloud + remove: [] - name: Label google-cloud-http4s PRs conditions: - files~=^google-cloud-http4s/ diff --git a/build.sbt b/build.sbt index c22ab4ae..c2ad34a0 100644 --- a/build.sbt +++ b/build.sbt @@ -76,6 +76,7 @@ lazy val root = lambdaHttp4s, lambdaCloudFormationCustomResource, googleCloudHttp4s, + googleCloud, examples, unidocs ) @@ -271,3 +272,32 @@ lazy val googleCloudHttp4s = crossProject(JSPlatform, JVMPlatform) "co.fs2" %%% "fs2-io" % fs2Version ) ) + +lazy val googleCloud = crossProject(JSPlatform, JVMPlatform) + .in(file("google-cloud")) + .settings( + name := "feral-google-cloud", + libraryDependencies ++= Seq( + "org.typelevel" %%% "cats-effect" % catsEffectVersion, + "org.scodec" %%% "scodec-bits" % "1.2.0", + "io.circe" %%% "circe-core" % circeVersion, + "io.circe" %%% "circe-generic" % circeVersion, + "org.scalameta" %%% "munit-scalacheck" % munitVersion % Test, + "org.typelevel" %%% "munit-cats-effect" % munitCEVersion % Test, + "io.circe" %%% "circe-literal" % circeVersion % Test, + ), + tlVersionIntroduced := List("2.13", "3").map(_ -> "0.3.1").toMap + ) + .settings(commonSettings) + .jsSettings( + libraryDependencies ++= Seq( + "io.github.cquiroz" %%% "scala-java-time" % "2.5.0" + ) + ) + .jvmSettings( + Test / fork := true, + libraryDependencies ++= Seq( + "com.google.cloud.functions" % "functions-framework-api" % "1.1.4" % Provided, + "co.fs2" %%% "fs2-io" % fs2Version + ) + ) \ No newline at end of file diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala new file mode 100644 index 00000000..0060b641 --- /dev/null +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala @@ -0,0 +1,71 @@ +package feral.`google-cloud` +package events + +import io.circe._ + +sealed abstract class MessagePublishedData { + def message: PubsubMessage + def subscription: String + def deliveryAttempt: Option[Int] +} + +object MessagePublishedData { + + def apply( + message: PubsubMessage, + subscription: String, + deliveryAttempt: Option[Int] = None + ): MessagePublishedData = { + new Impl(message, subscription, deliveryAttempt) + } + + implicit val decoder: Decoder[MessagePublishedData] = + Decoder.forProduct3("message", "subscription", "deliveryAttempt")( + MessagePublishedData.apply + ) + + private final case class Impl( + message: PubsubMessage, + subscription: String, + deliveryAttempt: Option[Int] + ) extends MessagePublishedData { + override def productPrefix = "MessagePublishedData" + } +} + +sealed abstract class PubsubMessage { + def data: String + def attributes: Map[String, String] + def messageId: String + def publishTime: String + def orderingKey: Option[String] +} + +object PubsubMessage { + + def apply( + data: String, + attributes: Map[String, String], + messageId: String, + publishTime: String, + orderingKey: Option[String] + ): PubsubMessage = { + new Impl(data, attributes, messageId, publishTime, orderingKey) + } + + implicit val decoder: Decoder[PubsubMessage] = Decoder.forProduct5( + "data", + "attributes", + "messageId", + "publishTime", + "orderingKey" + )(PubsubMessage.apply) + + private final case class Impl( + data: String, + attributes: Map[String, String], + messageId: String, + publishTime: String, + orderingKey: Option[String] + ) extends PubsubMessage { override def productPrefix = "PubsubMessage" } +} diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala new file mode 100644 index 00000000..df0b3658 --- /dev/null +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala @@ -0,0 +1,164 @@ +package feral.`google-cloud`.events + +import io.circe._ +import io.circe.generic.semiauto.deriveDecoder + +sealed abstract class StorageObjectData { + def contentEncoding: String + def contentDisposition: String + def cacheControl: String + def contentLanguage: String + def metageneration: Int + def timeDeleted: String + def contentType: String + def size: Int + def timeCreated: String + def crc32c: String + def componentCount: Int + def md5Hash: String + def etag: String + def updated: String + def storageClass: String + def kmsKeyName: String + def timeStorageClassUpdated: String + def temporaryHold: Boolean + def retentionExpirationTime: String + def metadata: Map[String, String] + def eventBasedHold: Boolean + def name: String + def id: String + def bucket: String + def generation: Int + def customerEncryption: CustomerEncryption + def mediaLink: String + def selfLink: String + def kind: String +} + +object StorageObjectData { + + def apply( + contentEncoding: String, + contentDisposition: String, + cacheControl: String, + contentLanguage: String, + metageneration: Int, + timeDeleted: String, + contentType: String, + size: Int, + timeCreated: String, + crc32c: String, + componentCount: Int, + md5Hash: String, + etag: String, + updated: String, + storageClass: String, + kmsKeyName: String, + timeStorageClassUpdated: String, + temporaryHold: Boolean, + retentionExpirationTime: String, + metadata: Map[String, String], + eventBasedHold: Boolean, + name: String, + id: String, + bucket: String, + generation: Int, + customerEncryption: CustomerEncryption, + mediaLink: String, + selfLink: String, + kind: String + ): StorageObjectData = { + new Impl( + contentEncoding, + contentDisposition, + cacheControl, + contentLanguage, + metageneration, + timeDeleted, + contentType, + size, + timeCreated, + crc32c, + componentCount, + md5Hash, + etag, + updated, + storageClass, + kmsKeyName, + timeStorageClassUpdated, + temporaryHold, + retentionExpirationTime, + metadata, + eventBasedHold, + name, + id, + bucket, + generation, + customerEncryption, + mediaLink, + selfLink, + kind + ) + } + + implicit val decoder: Decoder[StorageObjectData] = deriveDecoder[Impl].map(identity) + + private final case class Impl( + contentEncoding: String, + contentDisposition: String, + cacheControl: String, + contentLanguage: String, + metageneration: Int, + timeDeleted: String, + contentType: String, + size: Int, + timeCreated: String, + crc32c: String, + componentCount: Int, + md5Hash: String, + etag: String, + updated: String, + storageClass: String, + kmsKeyName: String, + timeStorageClassUpdated: String, + temporaryHold: Boolean, + retentionExpirationTime: String, + metadata: Map[String, String], + eventBasedHold: Boolean, + name: String, + id: String, + bucket: String, + generation: Int, + customerEncryption: CustomerEncryption, + mediaLink: String, + selfLink: String, + kind: String + ) extends StorageObjectData { + override def productPrefix = "StorageObjectData" + } +} + +sealed abstract class CustomerEncryption { + def encryptionAlgorithm: String + def keySha256: String +} + +object CustomerEncryption { + + def apply( + encryptionAlgorithm: String, + keySha256: String + ): CustomerEncryption = { + new Impl(encryptionAlgorithm, keySha256) + } + + implicit val decoder: Decoder[CustomerEncryption] = + Decoder.forProduct2("encryptionAlgorithm", "keySha256")( + CustomerEncryption.apply + ) + + private final case class Impl(encryptionAlgorithm: String, keySha256: String) + extends CustomerEncryption { + override def productPrefix = "CustomerEncryption" + } +} diff --git a/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala new file mode 100644 index 00000000..f2719150 --- /dev/null +++ b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala @@ -0,0 +1,37 @@ +package feral.`google-cloud`.events + +import munit.FunSuite +import io.circe.literal._ + + +class MessagePublishedDataSuite extends FunSuite { + + import MessagePublishedDataSuite._ + + test("decoder"){ + val data = event.as[MessagePublishedData].toTry.get + assertEquals(data.subscription, "projects/my-project/subscriptions/my-subscription") + assertEquals(data.message.data, "dGVzdCBtZXNzYWdlIDM=") + assertEquals(data.message.attributes, Map("attr1" -> "attr1-value")) + assertEquals(data.message.messageId, "message-id") + assertEquals(data.message.publishTime, "2021-02-05T04:06:14.109Z") + } + +} + +object MessagePublishedDataSuite { + + def event = json""" + { + "subscription": "projects/my-project/subscriptions/my-subscription", + "message": { + "attributes": { + "attr1":"attr1-value" + }, + "data": "dGVzdCBtZXNzYWdlIDM=", + "messageId": "message-id", + "publishTime":"2021-02-05T04:06:14.109Z" + } + } + """ +} \ No newline at end of file diff --git a/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala b/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala new file mode 100644 index 00000000..2a4a23ba --- /dev/null +++ b/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala @@ -0,0 +1,53 @@ +package feral.`google-cloud`.events + +import munit.FunSuite +import io.circe.literal._ + +class StorageObjectDataSuite extends FunSuite { + + import StorageObjectDataSuite._ + + test("decoder"){ + event.as[StorageObjectData].toTry.get + } + +} + +object StorageObjectDataSuite { + def event = json""" + { + "bucket": "some-bucket", + "cacheControl": "", + "componentCount": 0, + "contentDisposition": "", + "contentEncoding": "", + "contentLanguage": "", + "contentType": "text/plain", + "crc32c": "rTVTeQ==", + "customerEncryption": { + "encryptionAlgorithm": "AES256", + "keySha256": "abc123" + }, + "etag": "CNHZkbuF/ugCEAE=", + "eventBasedHold": false, + "generation": 12345, + "id": "some-bucket/folder/Test.cs/1587627537231057", + "kind": "storage#object", + "kmsKeyName": "", + "md5Hash": "kF8MuJ5+CTJxvyhHS1xzRg==", + "mediaLink": "https://www.googleapis.com/download/storage/v1/b/some-bucket/o/folder%2FTest.cs?generation=1587627537231057\u0026alt=media", + "metadata": {}, + "metageneration": 1, + "name": "folder/Test.cs", + "retentionExpirationTime": "", + "selfLink": "https://www.googleapis.com/storage/v1/b/some-bucket/o/folder/Test.cs", + "size": 352, + "storageClass": "MULTI_REGIONAL", + "temporaryHold": false, + "timeCreated": "2020-04-23T07:38:57.230Z", + "timeDeleted": "", + "timeStorageClassUpdated": "2020-04-23T07:38:57.230Z", + "updated": "2020-04-23T07:38:57.230Z" + } + """ +} From fde92c64e9309ec340dbb543b90a84ecdeb09ec7 Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Fri, 12 Jun 2026 10:46:38 +0000 Subject: [PATCH 02/16] Regenerate Github Actions --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36174d9c..48911dc4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -117,11 +117,11 @@ jobs: - name: Make target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: mkdir -p lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target project/target + run: mkdir -p lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target google-cloud/js/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target google-cloud/jvm/target project/target - name: Compress target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: tar cf targets.tar lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target project/target + run: tar cf targets.tar lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target google-cloud/js/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target google-cloud/jvm/target project/target - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') From abbc70434b8866ebbd8e6e8c121bafd9c4609feb Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Fri, 12 Jun 2026 10:56:26 +0000 Subject: [PATCH 03/16] fix fmt errors --- .../events/StorageObjectData.scala | 2 +- .../events/MessagePublishedDataSuite.scala | 25 +++++++++---------- .../events/StorageObjectDataSuite.scala | 12 ++++----- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala index df0b3658..3e48e026 100644 --- a/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala @@ -100,7 +100,7 @@ object StorageObjectData { kind ) } - + implicit val decoder: Decoder[StorageObjectData] = deriveDecoder[Impl].map(identity) private final case class Impl( diff --git a/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala index f2719150..8c7cefd5 100644 --- a/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala +++ b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala @@ -1,27 +1,26 @@ package feral.`google-cloud`.events -import munit.FunSuite import io.circe.literal._ - +import munit.FunSuite class MessagePublishedDataSuite extends FunSuite { - import MessagePublishedDataSuite._ + import MessagePublishedDataSuite._ - test("decoder"){ - val data = event.as[MessagePublishedData].toTry.get - assertEquals(data.subscription, "projects/my-project/subscriptions/my-subscription") - assertEquals(data.message.data, "dGVzdCBtZXNzYWdlIDM=") - assertEquals(data.message.attributes, Map("attr1" -> "attr1-value")) - assertEquals(data.message.messageId, "message-id") - assertEquals(data.message.publishTime, "2021-02-05T04:06:14.109Z") - } + test("decoder") { + val data = event.as[MessagePublishedData].toTry.get + assertEquals(data.subscription, "projects/my-project/subscriptions/my-subscription") + assertEquals(data.message.data, "dGVzdCBtZXNzYWdlIDM=") + assertEquals(data.message.attributes, Map("attr1" -> "attr1-value")) + assertEquals(data.message.messageId, "message-id") + assertEquals(data.message.publishTime, "2021-02-05T04:06:14.109Z") + } } object MessagePublishedDataSuite { - def event = json""" + def event = json""" { "subscription": "projects/my-project/subscriptions/my-subscription", "message": { @@ -34,4 +33,4 @@ object MessagePublishedDataSuite { } } """ -} \ No newline at end of file +} diff --git a/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala b/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala index 2a4a23ba..f2bda2b9 100644 --- a/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala +++ b/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala @@ -1,20 +1,20 @@ package feral.`google-cloud`.events -import munit.FunSuite import io.circe.literal._ +import munit.FunSuite class StorageObjectDataSuite extends FunSuite { - import StorageObjectDataSuite._ + import StorageObjectDataSuite._ - test("decoder"){ - event.as[StorageObjectData].toTry.get - } + test("decoder") { + event.as[StorageObjectData].toTry.get + } } object StorageObjectDataSuite { - def event = json""" + def event = json""" { "bucket": "some-bucket", "cacheControl": "", From 9ac2c2f91ffa6c689dca473c505270818081818e Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Fri, 12 Jun 2026 11:08:11 +0000 Subject: [PATCH 04/16] resolved header errors --- .../events/MessagePublishedData.scala | 16 ++++++++++++++++ .../google-cloud/events/StorageObjectData.scala | 16 ++++++++++++++++ .../events/MessagePublishedDataSuite.scala | 16 ++++++++++++++++ .../events/StorageObjectDataSuite.scala | 16 ++++++++++++++++ 4 files changed, 64 insertions(+) diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala index 0060b641..e84f7a1f 100644 --- a/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2021 Typelevel + * + * 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 feral.`google-cloud` package events diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala index 3e48e026..1950a68a 100644 --- a/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2021 Typelevel + * + * 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 feral.`google-cloud`.events import io.circe._ diff --git a/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala index 8c7cefd5..96e87f9d 100644 --- a/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala +++ b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2021 Typelevel + * + * 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 feral.`google-cloud`.events import io.circe.literal._ diff --git a/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala b/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala index f2bda2b9..d88270eb 100644 --- a/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala +++ b/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2021 Typelevel + * + * 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 feral.`google-cloud`.events import io.circe.literal._ From 345f8c9f4bb1600c9209e284411e32257abb093d Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Fri, 12 Jun 2026 11:19:44 +0000 Subject: [PATCH 05/16] fixed sbt fmt check --- build.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index c2ad34a0..2a16ea5c 100644 --- a/build.sbt +++ b/build.sbt @@ -284,7 +284,7 @@ lazy val googleCloud = crossProject(JSPlatform, JVMPlatform) "io.circe" %%% "circe-generic" % circeVersion, "org.scalameta" %%% "munit-scalacheck" % munitVersion % Test, "org.typelevel" %%% "munit-cats-effect" % munitCEVersion % Test, - "io.circe" %%% "circe-literal" % circeVersion % Test, + "io.circe" %%% "circe-literal" % circeVersion % Test ), tlVersionIntroduced := List("2.13", "3").map(_ -> "0.3.1").toMap ) @@ -300,4 +300,4 @@ lazy val googleCloud = crossProject(JSPlatform, JVMPlatform) "com.google.cloud.functions" % "functions-framework-api" % "1.1.4" % Provided, "co.fs2" %%% "fs2-io" % fs2Version ) - ) \ No newline at end of file + ) From c414ea3d8db67297ad875b4d0d4b81c3761a5def Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Fri, 12 Jun 2026 11:28:44 +0000 Subject: [PATCH 06/16] resolved CI error --- .../scala/feral/google-cloud/events/MessagePublishedData.scala | 2 +- .../scala/feral/google-cloud/events/StorageObjectData.scala | 2 +- .../feral/google-cloud/events/MessagePublishedDataSuite.scala | 2 +- .../feral/google-cloud/events/StorageObjectDataSuite.scala | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala index e84f7a1f..11b3f063 100644 --- a/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package feral.`google-cloud` +package feral.googlecloud package events import io.circe._ diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala index 1950a68a..1be3b885 100644 --- a/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package feral.`google-cloud`.events +package feral.googlecloud.events import io.circe._ import io.circe.generic.semiauto.deriveDecoder diff --git a/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala index 96e87f9d..9d102ea0 100644 --- a/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala +++ b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package feral.`google-cloud`.events +package feral.googlecloud.events import io.circe.literal._ import munit.FunSuite diff --git a/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala b/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala index d88270eb..ef33c175 100644 --- a/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala +++ b/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package feral.`google-cloud`.events +package feral.googlecloud.events import io.circe.literal._ import munit.FunSuite From 9c511d0cf3fe756607c0320844d65be9e7259570 Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Fri, 12 Jun 2026 11:59:49 +0000 Subject: [PATCH 07/16] fix mima issues --- build.sbt | 1 + 1 file changed, 1 insertion(+) diff --git a/build.sbt b/build.sbt index 2a16ea5c..c76c20ee 100644 --- a/build.sbt +++ b/build.sbt @@ -286,6 +286,7 @@ lazy val googleCloud = crossProject(JSPlatform, JVMPlatform) "org.typelevel" %%% "munit-cats-effect" % munitCEVersion % Test, "io.circe" %%% "circe-literal" % circeVersion % Test ), + mimaPreviousArtifacts := Set.empty, tlVersionIntroduced := List("2.13", "3").map(_ -> "0.3.1").toMap ) .settings(commonSettings) From 8ba0a2191e664d61a3d1d59eef87fff44aee5ee8 Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Fri, 12 Jun 2026 15:29:56 +0000 Subject: [PATCH 08/16] use tlVersionIntroduced Map --- build.sbt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index c76c20ee..55ad8aec 100644 --- a/build.sbt +++ b/build.sbt @@ -286,8 +286,7 @@ lazy val googleCloud = crossProject(JSPlatform, JVMPlatform) "org.typelevel" %%% "munit-cats-effect" % munitCEVersion % Test, "io.circe" %%% "circe-literal" % circeVersion % Test ), - mimaPreviousArtifacts := Set.empty, - tlVersionIntroduced := List("2.13", "3").map(_ -> "0.3.1").toMap + tlVersionIntroduced := List("2.13", "3").map(_ -> "0.3.2").toMap ) .settings(commonSettings) .jsSettings( From 27b4552a47f504d78762b275b93528a82271e989 Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Tue, 30 Jun 2026 19:45:47 +0000 Subject: [PATCH 09/16] change publishTime from String to Instant; added codecs --- .../events/MessagePublishedData.scala | 10 ++++-- .../feral/google-cloud/events/codecs.scala | 31 +++++++++++++++++++ .../events/MessagePublishedDataSuite.scala | 4 ++- 3 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala index 11b3f063..c7ea826f 100644 --- a/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala @@ -19,6 +19,8 @@ package events import io.circe._ +import java.time.Instant + sealed abstract class MessagePublishedData { def message: PubsubMessage def subscription: String @@ -53,7 +55,7 @@ sealed abstract class PubsubMessage { def data: String def attributes: Map[String, String] def messageId: String - def publishTime: String + def publishTime: Instant def orderingKey: Option[String] } @@ -63,12 +65,14 @@ object PubsubMessage { data: String, attributes: Map[String, String], messageId: String, - publishTime: String, + publishTime: Instant, orderingKey: Option[String] ): PubsubMessage = { new Impl(data, attributes, messageId, publishTime, orderingKey) } + import codecs.decodeInstant + implicit val decoder: Decoder[PubsubMessage] = Decoder.forProduct5( "data", "attributes", @@ -81,7 +85,7 @@ object PubsubMessage { data: String, attributes: Map[String, String], messageId: String, - publishTime: String, + publishTime: Instant, orderingKey: Option[String] ) extends PubsubMessage { override def productPrefix = "PubsubMessage" } } diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala new file mode 100644 index 00000000..3fe49f4c --- /dev/null +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala @@ -0,0 +1,31 @@ +/* + * Copyright 2021 Typelevel + * + * 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 feral.googlecloud.events + +import io.circe.Decoder + +import java.time.Instant +import scala.util.Try + +private object codecs { + implicit def decodeInstantFromMillis: Decoder[Instant] = + Decoder.decodeLong.emapTry { millis => Try(Instant.ofEpochMilli(millis)) } + + implicit def decodeInstant: Decoder[Instant] = + Decoder.decodeString.emapTry { str => Try(Instant.parse(str)) } + +} diff --git a/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala index 9d102ea0..9641c489 100644 --- a/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala +++ b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala @@ -19,6 +19,8 @@ package feral.googlecloud.events import io.circe.literal._ import munit.FunSuite +import java.time.Instant + class MessagePublishedDataSuite extends FunSuite { import MessagePublishedDataSuite._ @@ -29,7 +31,7 @@ class MessagePublishedDataSuite extends FunSuite { assertEquals(data.message.data, "dGVzdCBtZXNzYWdlIDM=") assertEquals(data.message.attributes, Map("attr1" -> "attr1-value")) assertEquals(data.message.messageId, "message-id") - assertEquals(data.message.publishTime, "2021-02-05T04:06:14.109Z") + assertEquals(data.message.publishTime, Instant.parse("2021-02-05T04:06:14.109Z")) } } From d6fce5e7b852dfa1977662052f2c7b3239c2f14d Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Fri, 3 Jul 2026 16:59:49 +0000 Subject: [PATCH 10/16] update test pattern for consistency --- .../events/MessagePublishedDataSuite.scala | 40 +++--- .../events/StorageObjectDataSuite.scala | 114 +++++++++++------- 2 files changed, 96 insertions(+), 58 deletions(-) diff --git a/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala index 9641c489..a078be90 100644 --- a/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala +++ b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala @@ -23,8 +23,6 @@ import java.time.Instant class MessagePublishedDataSuite extends FunSuite { - import MessagePublishedDataSuite._ - test("decoder") { val data = event.as[MessagePublishedData].toTry.get assertEquals(data.subscription, "projects/my-project/subscriptions/my-subscription") @@ -34,21 +32,29 @@ class MessagePublishedDataSuite extends FunSuite { assertEquals(data.message.publishTime, Instant.parse("2021-02-05T04:06:14.109Z")) } -} - -object MessagePublishedDataSuite { - def event = json""" - { - "subscription": "projects/my-project/subscriptions/my-subscription", - "message": { - "attributes": { - "attr1":"attr1-value" - }, - "data": "dGVzdCBtZXNzYWdlIDM=", - "messageId": "message-id", - "publishTime":"2021-02-05T04:06:14.109Z" - } + { + "subscription": "projects/my-project/subscriptions/my-subscription", + "message": { + "attributes": { + "attr1":"attr1-value" + }, + "data": "dGVzdCBtZXNzYWdlIDM=", + "messageId": "message-id", + "publishTime":"2021-02-05T04:06:14.109Z" } - """ + } + """ + + def result = MessagePublishedData( + subscription = "projects/my-project/subscriptions/my-subscription", + message = PubsubMessage( + data = "dGVzdCBtZXNzYWdlIDM=", + attributes = Map("attr1" -> "attr1-value"), + messageId = "message-id", + publishTime = Instant.parse("2021-02-05T04:06:14.109Z"), + orderingKey = None + ) + ) + } diff --git a/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala b/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala index ef33c175..af980714 100644 --- a/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala +++ b/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala @@ -21,49 +21,81 @@ import munit.FunSuite class StorageObjectDataSuite extends FunSuite { - import StorageObjectDataSuite._ - test("decoder") { - event.as[StorageObjectData].toTry.get + assertEquals(event.as[StorageObjectData].toTry.get, result) } -} - -object StorageObjectDataSuite { def event = json""" - { - "bucket": "some-bucket", - "cacheControl": "", - "componentCount": 0, - "contentDisposition": "", - "contentEncoding": "", - "contentLanguage": "", - "contentType": "text/plain", - "crc32c": "rTVTeQ==", - "customerEncryption": { - "encryptionAlgorithm": "AES256", - "keySha256": "abc123" - }, - "etag": "CNHZkbuF/ugCEAE=", - "eventBasedHold": false, - "generation": 12345, - "id": "some-bucket/folder/Test.cs/1587627537231057", - "kind": "storage#object", - "kmsKeyName": "", - "md5Hash": "kF8MuJ5+CTJxvyhHS1xzRg==", - "mediaLink": "https://www.googleapis.com/download/storage/v1/b/some-bucket/o/folder%2FTest.cs?generation=1587627537231057\u0026alt=media", - "metadata": {}, - "metageneration": 1, - "name": "folder/Test.cs", - "retentionExpirationTime": "", - "selfLink": "https://www.googleapis.com/storage/v1/b/some-bucket/o/folder/Test.cs", - "size": 352, - "storageClass": "MULTI_REGIONAL", - "temporaryHold": false, - "timeCreated": "2020-04-23T07:38:57.230Z", - "timeDeleted": "", - "timeStorageClassUpdated": "2020-04-23T07:38:57.230Z", - "updated": "2020-04-23T07:38:57.230Z" - } - """ + { + "bucket": "some-bucket", + "cacheControl": "", + "componentCount": 0, + "contentDisposition": "", + "contentEncoding": "", + "contentLanguage": "", + "contentType": "text/plain", + "crc32c": "rTVTeQ==", + "customerEncryption": { + "encryptionAlgorithm": "AES256", + "keySha256": "abc123" + }, + "etag": "CNHZkbuF/ugCEAE=", + "eventBasedHold": false, + "generation": 12345, + "id": "some-bucket/folder/Test.cs/1587627537231057", + "kind": "storage#object", + "kmsKeyName": "", + "md5Hash": "kF8MuJ5+CTJxvyhHS1xzRg==", + "mediaLink": "https://www.googleapis.com/download/storage/v1/b/some-bucket/o/folder%2FTest.cs?generation=1587627537231057\u0026alt=media", + "metadata": {}, + "metageneration": 1, + "name": "folder/Test.cs", + "retentionExpirationTime": "", + "selfLink": "https://www.googleapis.com/storage/v1/b/some-bucket/o/folder/Test.cs", + "size": 352, + "storageClass": "MULTI_REGIONAL", + "temporaryHold": false, + "timeCreated": "2020-04-23T07:38:57.230Z", + "timeDeleted": "", + "timeStorageClassUpdated": "2020-04-23T07:38:57.230Z", + "updated": "2020-04-23T07:38:57.230Z" + } + """ + + def result = StorageObjectData( + contentEncoding = "", + contentDisposition = "", + cacheControl = "", + contentLanguage = "", + metageneration = 1, + timeDeleted = "", + contentType = "text/plain", + size = 352, + timeCreated = "2020-04-23T07:38:57.230Z", + crc32c = "rTVTeQ==", + componentCount = 0, + md5Hash = "kF8MuJ5+CTJxvyhHS1xzRg==", + etag = "CNHZkbuF/ugCEAE=", + updated = "2020-04-23T07:38:57.230Z", + storageClass = "MULTI_REGIONAL", + kmsKeyName = "", + timeStorageClassUpdated = "2020-04-23T07:38:57.230Z", + temporaryHold = false, + retentionExpirationTime = "", + metadata = Map.empty, + eventBasedHold = false, + name = "folder/Test.cs", + id = "some-bucket/folder/Test.cs/1587627537231057", + bucket = "some-bucket", + generation = 12345, + customerEncryption = CustomerEncryption( + encryptionAlgorithm = "AES256", + keySha256 = "abc123" + ), + mediaLink = + "https://www.googleapis.com/download/storage/v1/b/some-bucket/o/folder%2FTest.cs?generation=1587627537231057\u0026alt=media", + selfLink = "https://www.googleapis.com/storage/v1/b/some-bucket/o/folder/Test.cs", + kind = "storage#object" + ) + } From 03002c0c0103b0c5dcb37035bbbd3cee5bc8ccfe Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Fri, 3 Jul 2026 18:13:06 +0000 Subject: [PATCH 11/16] forgot to update messagepublished_data_suite --- .../events/MessagePublishedDataSuite.scala | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala index a078be90..41687555 100644 --- a/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala +++ b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala @@ -24,25 +24,20 @@ import java.time.Instant class MessagePublishedDataSuite extends FunSuite { test("decoder") { - val data = event.as[MessagePublishedData].toTry.get - assertEquals(data.subscription, "projects/my-project/subscriptions/my-subscription") - assertEquals(data.message.data, "dGVzdCBtZXNzYWdlIDM=") - assertEquals(data.message.attributes, Map("attr1" -> "attr1-value")) - assertEquals(data.message.messageId, "message-id") - assertEquals(data.message.publishTime, Instant.parse("2021-02-05T04:06:14.109Z")) + assertEquals(event.as[MessagePublishedData].toTry.get, result) } def event = json""" { - "subscription": "projects/my-project/subscriptions/my-subscription", - "message": { - "attributes": { - "attr1":"attr1-value" - }, - "data": "dGVzdCBtZXNzYWdlIDM=", - "messageId": "message-id", - "publishTime":"2021-02-05T04:06:14.109Z" - } + "subscription": "projects/my-project/subscriptions/my-subscription", + "message": { + "attributes": { + "attr1":"attr1-value" + }, + "data": "dGVzdCBtZXNzYWdlIDM=", + "messageId": "message-id", + "publishTime":"2021-02-05T04:06:14.109Z" + } } """ From 3d935085b698f818cbc21c7a5ec24103f7d2f8d6 Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Mon, 6 Jul 2026 11:54:34 +0000 Subject: [PATCH 12/16] message.data type changed to ByteVector --- .../google-cloud/events/MessagePublishedData.scala | 10 ++++++---- .../main/scala/feral/google-cloud/events/codecs.scala | 6 ++++++ .../events/MessagePublishedDataSuite.scala | 3 ++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala index c7ea826f..62be47b1 100644 --- a/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala @@ -21,6 +21,8 @@ import io.circe._ import java.time.Instant +import scodec.bits.ByteVector + sealed abstract class MessagePublishedData { def message: PubsubMessage def subscription: String @@ -52,7 +54,7 @@ object MessagePublishedData { } sealed abstract class PubsubMessage { - def data: String + def data: ByteVector def attributes: Map[String, String] def messageId: String def publishTime: Instant @@ -62,7 +64,7 @@ sealed abstract class PubsubMessage { object PubsubMessage { def apply( - data: String, + data: ByteVector, attributes: Map[String, String], messageId: String, publishTime: Instant, @@ -71,7 +73,7 @@ object PubsubMessage { new Impl(data, attributes, messageId, publishTime, orderingKey) } - import codecs.decodeInstant + import codecs.{decodeByteVector, decodeInstant} implicit val decoder: Decoder[PubsubMessage] = Decoder.forProduct5( "data", @@ -82,7 +84,7 @@ object PubsubMessage { )(PubsubMessage.apply) private final case class Impl( - data: String, + data: ByteVector, attributes: Map[String, String], messageId: String, publishTime: Instant, diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala index 3fe49f4c..ecca6535 100644 --- a/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala @@ -20,6 +20,7 @@ import io.circe.Decoder import java.time.Instant import scala.util.Try +import scodec.bits.ByteVector private object codecs { implicit def decodeInstantFromMillis: Decoder[Instant] = @@ -28,4 +29,9 @@ private object codecs { implicit def decodeInstant: Decoder[Instant] = Decoder.decodeString.emapTry { str => Try(Instant.parse(str)) } + implicit def decodeByteVector: Decoder[ByteVector] = + Decoder.decodeString.emap{ str => + ByteVector.fromBase64(str).toRight(s"Invalid base64 string") + } + } diff --git a/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala index 41687555..22b121f8 100644 --- a/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala +++ b/google-cloud/shared/src/test/scala/feral/google-cloud/events/MessagePublishedDataSuite.scala @@ -18,6 +18,7 @@ package feral.googlecloud.events import io.circe.literal._ import munit.FunSuite +import scodec.bits.ByteVector import java.time.Instant @@ -44,7 +45,7 @@ class MessagePublishedDataSuite extends FunSuite { def result = MessagePublishedData( subscription = "projects/my-project/subscriptions/my-subscription", message = PubsubMessage( - data = "dGVzdCBtZXNzYWdlIDM=", + data = ByteVector.fromBase64("dGVzdCBtZXNzYWdlIDM=").get, attributes = Map("attr1" -> "attr1-value"), messageId = "message-id", publishTime = Instant.parse("2021-02-05T04:06:14.109Z"), From 6820e59cd6c0680868ad2d70c8ef916bf515d105 Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Mon, 6 Jul 2026 12:09:02 +0000 Subject: [PATCH 13/16] update data to use bytevector --- .../feral/google-cloud/events/MessagePublishedData.scala | 3 +-- .../src/main/scala/feral/google-cloud/events/codecs.scala | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala index 62be47b1..45e9c120 100644 --- a/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/MessagePublishedData.scala @@ -18,11 +18,10 @@ package feral.googlecloud package events import io.circe._ +import scodec.bits.ByteVector import java.time.Instant -import scodec.bits.ByteVector - sealed abstract class MessagePublishedData { def message: PubsubMessage def subscription: String diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala index ecca6535..4580dc89 100644 --- a/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala @@ -17,10 +17,10 @@ package feral.googlecloud.events import io.circe.Decoder +import scodec.bits.ByteVector import java.time.Instant import scala.util.Try -import scodec.bits.ByteVector private object codecs { implicit def decodeInstantFromMillis: Decoder[Instant] = @@ -29,9 +29,9 @@ private object codecs { implicit def decodeInstant: Decoder[Instant] = Decoder.decodeString.emapTry { str => Try(Instant.parse(str)) } - implicit def decodeByteVector: Decoder[ByteVector] = - Decoder.decodeString.emap{ str => - ByteVector.fromBase64(str).toRight(s"Invalid base64 string") + implicit def decodeByteVector: Decoder[ByteVector] = + Decoder.decodeString.emap { str => + ByteVector.fromBase64(str).toRight(s"Invalid base64 string") } } From f0bb1e5db82b5c7b518548d14fe5d676c68c440e Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Mon, 6 Jul 2026 18:55:04 +0000 Subject: [PATCH 14/16] changed date-time format to Instant in storageobject --- .../events/StorageObjectData.scala | 34 +++++++++++-------- .../feral/google-cloud/events/codecs.scala | 19 +++++++++++ .../events/StorageObjectDataSuite.scala | 12 ++++--- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala index 1be3b885..d706926b 100644 --- a/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/StorageObjectData.scala @@ -19,26 +19,30 @@ package feral.googlecloud.events import io.circe._ import io.circe.generic.semiauto.deriveDecoder +import java.time.Instant + +import codecs.decodeDateTimetoInstant + sealed abstract class StorageObjectData { def contentEncoding: String def contentDisposition: String def cacheControl: String def contentLanguage: String def metageneration: Int - def timeDeleted: String + def timeDeleted: Option[Instant] def contentType: String def size: Int - def timeCreated: String + def timeCreated: Option[Instant] def crc32c: String def componentCount: Int def md5Hash: String def etag: String - def updated: String + def updated: Option[Instant] def storageClass: String def kmsKeyName: String - def timeStorageClassUpdated: String + def timeStorageClassUpdated: Option[Instant] def temporaryHold: Boolean - def retentionExpirationTime: String + def retentionExpirationTime: Option[Instant] def metadata: Map[String, String] def eventBasedHold: Boolean def name: String @@ -59,20 +63,20 @@ object StorageObjectData { cacheControl: String, contentLanguage: String, metageneration: Int, - timeDeleted: String, + timeDeleted: Option[Instant], contentType: String, size: Int, - timeCreated: String, + timeCreated: Option[Instant], crc32c: String, componentCount: Int, md5Hash: String, etag: String, - updated: String, + updated: Option[Instant], storageClass: String, kmsKeyName: String, - timeStorageClassUpdated: String, + timeStorageClassUpdated: Option[Instant], temporaryHold: Boolean, - retentionExpirationTime: String, + retentionExpirationTime: Option[Instant], metadata: Map[String, String], eventBasedHold: Boolean, name: String, @@ -125,20 +129,20 @@ object StorageObjectData { cacheControl: String, contentLanguage: String, metageneration: Int, - timeDeleted: String, + timeDeleted: Option[Instant], contentType: String, size: Int, - timeCreated: String, + timeCreated: Option[Instant], crc32c: String, componentCount: Int, md5Hash: String, etag: String, - updated: String, + updated: Option[Instant], storageClass: String, kmsKeyName: String, - timeStorageClassUpdated: String, + timeStorageClassUpdated: Option[Instant], temporaryHold: Boolean, - retentionExpirationTime: String, + retentionExpirationTime: Option[Instant], metadata: Map[String, String], eventBasedHold: Boolean, name: String, diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala index 4580dc89..2fbf7acc 100644 --- a/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala @@ -17,15 +17,34 @@ package feral.googlecloud.events import io.circe.Decoder +import io.circe.DecodingFailure import scodec.bits.ByteVector import java.time.Instant +import java.time.format.DateTimeFormatter +import java.util.Locale import scala.util.Try private object codecs { implicit def decodeInstantFromMillis: Decoder[Instant] = Decoder.decodeLong.emapTry { millis => Try(Instant.ofEpochMilli(millis)) } + implicit def decodeDateTimetoInstant: Decoder[Option[Instant]] = + Decoder.instance[Option[Instant]] { c => + c.as[String] match { + case Right("") => Right(None) + case Right(str) => { + Try { + val parsed_date_time = + java.time.ZonedDateTime.parse(str, DateTimeFormatter.ISO_OFFSET_DATE_TIME) + parsed_date_time.toInstant() + }.toEither.left.map(e => DecodingFailure(e.getMessage(), c.history)).map(Some(_)) + } + case Left(_) => Right(None) + } + + } + implicit def decodeInstant: Decoder[Instant] = Decoder.decodeString.emapTry { str => Try(Instant.parse(str)) } diff --git a/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala b/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala index af980714..4724581d 100644 --- a/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala +++ b/google-cloud/shared/src/test/scala/feral/google-cloud/events/StorageObjectDataSuite.scala @@ -19,6 +19,8 @@ package feral.googlecloud.events import io.circe.literal._ import munit.FunSuite +import java.time.Instant + class StorageObjectDataSuite extends FunSuite { test("decoder") { @@ -68,20 +70,20 @@ class StorageObjectDataSuite extends FunSuite { cacheControl = "", contentLanguage = "", metageneration = 1, - timeDeleted = "", + timeDeleted = None, contentType = "text/plain", size = 352, - timeCreated = "2020-04-23T07:38:57.230Z", + timeCreated = Some(Instant.parse("2020-04-23T07:38:57.230Z")), crc32c = "rTVTeQ==", componentCount = 0, md5Hash = "kF8MuJ5+CTJxvyhHS1xzRg==", etag = "CNHZkbuF/ugCEAE=", - updated = "2020-04-23T07:38:57.230Z", + updated = Some(Instant.parse("2020-04-23T07:38:57.230Z")), storageClass = "MULTI_REGIONAL", kmsKeyName = "", - timeStorageClassUpdated = "2020-04-23T07:38:57.230Z", + timeStorageClassUpdated = Some(Instant.parse("2020-04-23T07:38:57.230Z")), temporaryHold = false, - retentionExpirationTime = "", + retentionExpirationTime = None, metadata = Map.empty, eventBasedHold = false, name = "folder/Test.cs", From bab8793ed55cf4f2203ce0ec19145ddb83d09d5c Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Mon, 6 Jul 2026 19:03:20 +0000 Subject: [PATCH 15/16] fix CI --- .../scala/feral/google-cloud/events/codecs.scala | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala index 2fbf7acc..d76718f3 100644 --- a/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala @@ -17,26 +17,25 @@ package feral.googlecloud.events import io.circe.Decoder -import io.circe.DecodingFailure import scodec.bits.ByteVector import java.time.Instant import java.time.format.DateTimeFormatter -import java.util.Locale import scala.util.Try +import io.circe.DecodingFailure private object codecs { implicit def decodeInstantFromMillis: Decoder[Instant] = Decoder.decodeLong.emapTry { millis => Try(Instant.ofEpochMilli(millis)) } - implicit def decodeDateTimetoInstant: Decoder[Option[Instant]] = - Decoder.instance[Option[Instant]] { c => + implicit def decodeDateTimetoInstant: Decoder[Option[Instant]] = + Decoder.instance[Option[Instant]]{ c => + c.as[String] match { case Right("") => Right(None) - case Right(str) => { + case Right(str) => { Try { - val parsed_date_time = - java.time.ZonedDateTime.parse(str, DateTimeFormatter.ISO_OFFSET_DATE_TIME) + val parsed_date_time = java.time.ZonedDateTime.parse(str, DateTimeFormatter.ISO_OFFSET_DATE_TIME) parsed_date_time.toInstant() }.toEither.left.map(e => DecodingFailure(e.getMessage(), c.history)).map(Some(_)) } From c4ba683167396c4d194ad813847da60f0d66f77c Mon Sep 17 00:00:00 2001 From: Yummy-Yums Date: Mon, 6 Jul 2026 19:08:50 +0000 Subject: [PATCH 16/16] fix CI --- .../scala/feral/google-cloud/events/codecs.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala b/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala index d76718f3..c550b0da 100644 --- a/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala +++ b/google-cloud/shared/src/main/scala/feral/google-cloud/events/codecs.scala @@ -17,25 +17,25 @@ package feral.googlecloud.events import io.circe.Decoder +import io.circe.DecodingFailure import scodec.bits.ByteVector import java.time.Instant import java.time.format.DateTimeFormatter import scala.util.Try -import io.circe.DecodingFailure private object codecs { implicit def decodeInstantFromMillis: Decoder[Instant] = Decoder.decodeLong.emapTry { millis => Try(Instant.ofEpochMilli(millis)) } - implicit def decodeDateTimetoInstant: Decoder[Option[Instant]] = - Decoder.instance[Option[Instant]]{ c => - + implicit def decodeDateTimetoInstant: Decoder[Option[Instant]] = + Decoder.instance[Option[Instant]] { c => c.as[String] match { case Right("") => Right(None) - case Right(str) => { + case Right(str) => { Try { - val parsed_date_time = java.time.ZonedDateTime.parse(str, DateTimeFormatter.ISO_OFFSET_DATE_TIME) + val parsed_date_time = + java.time.ZonedDateTime.parse(str, DateTimeFormatter.ISO_OFFSET_DATE_TIME) parsed_date_time.toInstant() }.toEither.left.map(e => DecodingFailure(e.getMessage(), c.history)).map(Some(_)) }