From 94d23b3f3962d466ccde6ecb4e7dee0407f9707b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Poniedzia=C5=82ek?= Date: Wed, 6 Mar 2024 18:36:51 -0800 Subject: [PATCH] Add `AcceptedLicense` model Common `AcceptedLicense` class which could be used by all apps to check if user has accepted SLUL --- .../snowplow/runtime/AcceptedLicense.scala | 31 +++++++++ .../snowplow/runtime/LicenseSpec.scala | 65 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 modules/runtime-common/src/main/scala/com/snowplowanalytics/snowplow/runtime/AcceptedLicense.scala create mode 100644 modules/runtime-common/src/test/scala/com/snowplowanalytics/snowplow/runtime/LicenseSpec.scala diff --git a/modules/runtime-common/src/main/scala/com/snowplowanalytics/snowplow/runtime/AcceptedLicense.scala b/modules/runtime-common/src/main/scala/com/snowplowanalytics/snowplow/runtime/AcceptedLicense.scala new file mode 100644 index 0000000..cb50993 --- /dev/null +++ b/modules/runtime-common/src/main/scala/com/snowplowanalytics/snowplow/runtime/AcceptedLicense.scala @@ -0,0 +1,31 @@ +/* + * 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 io.circe.Decoder + +final case class AcceptedLicense() + +object AcceptedLicense { + final case class DocumentationLink(value: String) + + def decoder(documentationLink: DocumentationLink): Decoder[AcceptedLicense] = { + val truthy = Set("true", "yes", "on", "1") + Decoder + .forProduct1("accept")((s: String) => truthy(s.toLowerCase())) + .or(Decoder.forProduct1("accept")((b: Boolean) => b)) + .emap { + case false => + Left( + s"Please accept the terms of the Snowplow Limited Use License Agreement to proceed. See ${documentationLink.value} for more information on the license and how to configure this." + ) + case _ => + Right(AcceptedLicense()) + } + } +} 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..d10179c --- /dev/null +++ b/modules/runtime-common/src/test/scala/com/snowplowanalytics/snowplow/runtime/LicenseSpec.scala @@ -0,0 +1,65 @@ +/* + * 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.testing.specs2.CatsEffect +import io.circe.literal.JsonStringContext +import io.circe.{Decoder, DecodingFailure} +import org.specs2.Specification + +class LicenseSpec extends Specification with CatsEffect { + + implicit val decoder: Decoder[AcceptedLicense] = + AcceptedLicense.decoder(AcceptedLicense.DocumentationLink("https://test.license.doc")) + + private val expectedErrorMessage = + "DecodingFailure at : Please accept the terms of the Snowplow Limited Use License Agreement to proceed. See https://test.license.doc for more information on the license and how to configure this." + + def is = s2""" + Decoding license should be successful for: + boolean true $e1 + string "true" $e2 + string "yes" $e3 + string "on" $e4 + string "1" $e5 + Decoding license should not be successful for: + boolean false $e6 + string "false" $e7 + any 'non-truthy' string like "something" $e8 + """ + + def e1 = + json"""{"accept": true}""".as[AcceptedLicense] must beRight(AcceptedLicense()) + + def e2 = + json"""{"accept": "true"}""".as[AcceptedLicense] must beRight(AcceptedLicense()) + + def e3 = + json"""{"accept": "yes"}""".as[AcceptedLicense] must beRight(AcceptedLicense()) + + def e4 = + json"""{"accept": "on"}""".as[AcceptedLicense] must beRight(AcceptedLicense()) + + def e5 = + json"""{"accept": "1"}""".as[AcceptedLicense] must beRight(AcceptedLicense()) + + def e6 = + json"""{"accept": false}""".as[AcceptedLicense] must beLeft.like { case er: DecodingFailure => + er.getMessage must beEqualTo(expectedErrorMessage) + } + + def e7 = + json"""{"accept": "false"}""".as[AcceptedLicense] must beLeft.like { case er: DecodingFailure => + er.getMessage must beEqualTo(expectedErrorMessage) + } + + def e8 = + json"""{"accept": "something"}""".as[AcceptedLicense] must beLeft.like { case er: DecodingFailure => + er.getMessage must beEqualTo(expectedErrorMessage) + } +}