diff --git a/modules/runtime-common/src/main/scala/com/snowplowanalytics/snowplow/runtime/License.scala b/modules/runtime-common/src/main/scala/com/snowplowanalytics/snowplow/runtime/License.scala new file mode 100644 index 0000000..56bbe3b --- /dev/null +++ b/modules/runtime-common/src/main/scala/com/snowplowanalytics/snowplow/runtime/License.scala @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023-present Snowplow Analytics Ltd. All rights reserved. + * + * This program is licensed to you under the Snowplow Community License Version 1.0, + * and you may not use this file except in compliance with the Snowplow Community License Version 1.0. + * You may obtain a copy of the Snowplow Community License Version 1.0 at https://docs.snowplow.io/community-license-1.0 + */ +package com.snowplowanalytics.snowplow.runtime + +import cats.effect.Sync +import io.circe.Decoder + +final case class License(accept: Boolean) + +object License { + + def check[F[_]: Sync](value: License): F[Unit] = + if (value.accept) + Sync[F].unit + else + Sync[F].raiseError( + new IllegalStateException( + "Please accept the terms of the Snowplow Limited Use License Agreement to proceed. See https://docs.snowplow.io/docs/pipeline-components-and-applications/stream-collector/configure/#license for more information on the license and how to configure this." + ) + ) + + implicit val licenseDecoder: Decoder[License] = { + val truthy = Set("true", "yes", "on", "1") + Decoder + .forProduct1("accept")((s: String) => License(truthy(s.toLowerCase()))) + .or(Decoder.forProduct1("accept")((b: Boolean) => License(b))) + } +} diff --git a/modules/runtime-common/src/test/scala/com/snowplowanalytics/snowplow/runtime/LicenseSpec.scala b/modules/runtime-common/src/test/scala/com/snowplowanalytics/snowplow/runtime/LicenseSpec.scala new file mode 100644 index 0000000..a947da8 --- /dev/null +++ b/modules/runtime-common/src/test/scala/com/snowplowanalytics/snowplow/runtime/LicenseSpec.scala @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023-present Snowplow Analytics Ltd. All rights reserved. + * + * This program is licensed to you under the Snowplow Community License Version 1.0, + * and you may not use this file except in compliance with the Snowplow Community License Version 1.0. + * You may obtain a copy of the Snowplow Community License Version 1.0 at https://docs.snowplow.io/community-license-1.0 + */ +package com.snowplowanalytics.snowplow.runtime + +import cats.effect.IO +import cats.effect.testing.specs2.CatsEffect +import io.circe.Decoder +import io.circe.generic.semiauto.deriveDecoder +import io.circe.literal.JsonStringContext +import org.specs2.Specification + +class LicenseSpec extends Specification with CatsEffect { + + def is = s2""" + Decoding license 'accept' value should be true for: + boolean true $e1 + string "true" $e2 + string "yes" $e3 + string "on" $e4 + string "1" $e5 + Decoding license 'accept' value should be false for: + boolean false $e6 + string "false" $e7 + any 'non-truthy' string like "something" $e8 + Checking license should do nothing if accept flag is true $e9 + Checking license should raise error if accept flag is false $e10 + """ + + def e1 = + json"""{"accept": true}""".as[License] must beRight(License(accept = true)) + + def e2 = + json"""{"accept": "true"}""".as[License] must beRight(License(accept = true)) + + def e3 = + json"""{"accept": "yes"}""".as[License] must beRight(License(accept = true)) + + def e4 = + json"""{"accept": "on"}""".as[License] must beRight(License(accept = true)) + + def e5 = + json"""{"accept": "1"}""".as[License] must beRight(License(accept = true)) + + def e6 = + json"""{"accept": false}""".as[License] must beRight(License(accept = false)) + + def e7 = + json"""{"accept": "false"}""".as[License] must beRight(License(accept = false)) + + def e8 = + json"""{"accept": "something"}""".as[License] must beRight(License(accept = false)) + + def e9 = + License.check[IO](License(accept = true)).map(result => result must beEqualTo(())) + + def e10 = + License + .check[IO](License(accept = false)) + .attempt + .map { result => + result must beLeft.like { case ex: Throwable => + ex.getMessage must beEqualTo( + "Please accept the terms of the Snowplow Limited Use License Agreement to proceed. See https://docs.snowplow.io/docs/pipeline-components-and-applications/stream-collector/configure/#license for more information on the license and how to configure this." + ) + } + } +} + +object LicenseSpec { + final case class TestConfig(license: License) + implicit val testConfigDecoder: Decoder[TestConfig] = deriveDecoder[TestConfig] +}