From 32b9e4990bae1b17a23d08aea4f27438e4e2e9e2 Mon Sep 17 00:00:00 2001 From: Ivan Subotic <400790+subotic@users.noreply.github.com> Date: Wed, 13 Oct 2021 11:50:50 +0200 Subject: [PATCH] refactor(projects): cleaner value objects usage in addProject route (DEV-119) (#1920) --- third_party/dependencies.bzl | 9 +- .../org/knora/webapi/messages/BUILD.bazel | 2 + .../ProjectCreatePayloadADM.scala | 28 ++++ .../projectsmessages/ProjectPayloadsADM.scala | 57 -------- .../valueObjects/ValueObjectsADM.scala | 67 ++++++---- .../org/knora/webapi/routing/BUILD.bazel | 2 + .../org/knora/webapi/routing/KnoraRoute.scala | 13 ++ .../webapi/routing/admin/GroupsRouteADM.scala | 6 +- .../routing/admin/ProjectsRouteADM.scala | 54 ++++---- .../webapi/routing/admin/UsersRouteADM.scala | 6 +- .../org/knora/webapi/IntegrationSpec.scala | 43 +++--- .../valueObjects/ValueObjectsADMSpec.scala | 2 +- .../admin/GroupsResponderADMSpec.scala | 24 ++-- .../admin/ProjectsResponderADMSpec.scala | 126 +++++++++--------- .../admin/UsersResponderADMSpec.scala | 16 +-- 15 files changed, 224 insertions(+), 231 deletions(-) create mode 100644 webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectCreatePayloadADM.scala delete mode 100644 webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectPayloadsADM.scala diff --git a/third_party/dependencies.bzl b/third_party/dependencies.bzl index eeb82d3117..6d059578bc 100644 --- a/third_party/dependencies.bzl +++ b/third_party/dependencies.bzl @@ -28,10 +28,11 @@ def dependencies(): "com.typesafe:config:1.3.3", # ZIO - "dev.zio:zio_2.13:1.0.9", + "dev.zio:zio_2.13:2.0.0-M3", "dev.zio:zio-json_2.13:0.1.5", - "dev.zio:zio-test_2.13:1.0.9", - "dev.zio:zio-test-junit_2.13:1.0.9", + "dev.zio:zio-test_2.13:2.0.0-M3", + "dev.zio:zio-test-junit_2.13:2.0.0-M3", + "dev.zio:zio-prelude_2.13:1.0.0-RC6", # CORS support "ch.megard:akka-http-cors_2.13:1.0.0", @@ -186,6 +187,8 @@ BASE_TEST_DEPENDENCIES = [ "@maven//:com_typesafe_akka_akka_http_testkit_2_13", "@maven//:com_typesafe_akka_akka_stream_2_13", "@maven//:com_typesafe_config", + "@maven//:dev_zio_zio_2_13", + "@maven//:dev_zio_zio_prelude_2_13", "@maven//:org_scalatest_scalatest_2_13", "@maven//:org_scalatest_scalatest_core_2_13", "@maven//:org_scalatest_scalatest_wordspec_2_13", diff --git a/webapi/src/main/scala/org/knora/webapi/messages/BUILD.bazel b/webapi/src/main/scala/org/knora/webapi/messages/BUILD.bazel index 5efc64ca71..2c522e3fe9 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/BUILD.bazel +++ b/webapi/src/main/scala/org/knora/webapi/messages/BUILD.bazel @@ -29,6 +29,8 @@ scala_library( "@maven//:com_typesafe_scala_logging_scala_logging_2_13", "@maven//:commons_io_commons_io", "@maven//:commons_validator_commons_validator", + "@maven//:dev_zio_zio_2_13", + "@maven//:dev_zio_zio_prelude_2_13", "@maven//:io_spray_spray_json_2_13", "@maven//:javax_json_javax_json_api", "@maven//:net_sf_saxon_Saxon_HE", diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectCreatePayloadADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectCreatePayloadADM.scala new file mode 100644 index 0000000000..ad797c6b31 --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectCreatePayloadADM.scala @@ -0,0 +1,28 @@ +package org.knora.webapi.messages.admin.responder.projectsmessages + +import org.knora.webapi.IRI +import org.knora.webapi.messages.admin.responder.valueObjects.{ + Shortname, + Longname, + Shortcode, + Description, + Keywords, + Logo, + Status, + Selfjoin +} + +/** + * Project payload + */ +final case class ProjectCreatePayloadADM( + id: Option[IRI] = None, + shortname: Shortname, + shortcode: Shortcode, + longname: Option[Longname], + description: Description, + keywords: Keywords, + logo: Option[Logo], + status: Status, + selfjoin: Selfjoin +) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectPayloadsADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectPayloadsADM.scala deleted file mode 100644 index bd98347dab..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectPayloadsADM.scala +++ /dev/null @@ -1,57 +0,0 @@ -package org.knora.webapi.messages.admin.responder.projectsmessages - -import org.knora.webapi.IRI -import org.knora.webapi.messages.admin.responder.valueObjects.{ - Shortname, - Longname, - Shortcode, - Description, - Keywords, - Logo, - Status, - Selfjoin -} - -// TODO: https://github.com/dasch-swiss/dsp-api/pull/1909#discussion_r718330669 - -/** - * Project payload - */ -sealed abstract case class ProjectCreatePayloadADM private ( - id: Option[IRI], - shortname: Shortname, - shortcode: Shortcode, - longname: Option[Longname], - description: Description, - keywords: Keywords, - logo: Option[Logo], - status: Status, - selfjoin: Selfjoin -) - -object ProjectCreatePayloadADM { - - /** The create constructor */ - def create( - id: Option[IRI] = None, - shortname: Shortname, - shortcode: Shortcode, - longname: Option[Longname] = None, - description: Description, - keywords: Keywords, - logo: Option[Logo] = None, - status: Status, - selfjoin: Selfjoin - ): ProjectCreatePayloadADM = - new ProjectCreatePayloadADM( - id = id, - shortname = shortname, - shortcode = shortcode, - longname = longname, - description = description, - keywords = keywords, - logo = logo, - status = status, - selfjoin = selfjoin - ) {} -} diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/ValueObjectsADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/ValueObjectsADM.scala index 93c3d1c663..99554cf585 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/ValueObjectsADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/ValueObjectsADM.scala @@ -4,6 +4,7 @@ import org.knora.webapi.LanguageCodes import org.knora.webapi.exceptions.{AssertionException, BadRequestException} import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 +import zio.prelude.Validation import scala.util.matching.Regex @@ -136,12 +137,14 @@ sealed abstract case class Shortcode private (value: String) object Shortcode { val stringFormatter = StringFormatter.getGeneralInstance - def create(value: String): Either[Throwable, Shortcode] = + def make(value: String): Validation[Throwable, Shortcode] = if (value.isEmpty) { - Left(BadRequestException("Missing shortcode")) + Validation.fail(BadRequestException("Missing shortcode")) } else { - val shortcode: String = stringFormatter.validateProjectShortcode(value, throw AssertionException("not valid")) - Right(new Shortcode(shortcode) {}) + val validatedValue: Validation[Throwable, String] = Validation( + stringFormatter.validateProjectShortcode(value, throw AssertionException("not valid")) + ) + validatedValue.map(new Shortcode(_) {}) } } @@ -152,12 +155,14 @@ sealed abstract case class Shortname private (value: String) object Shortname { val stringFormatter = StringFormatter.getGeneralInstance - def create(value: String): Either[Throwable, Shortname] = + def make(value: String): Validation[Throwable, Shortname] = if (value.isEmpty) { - Left(BadRequestException("Missing shortname")) + Validation.fail(BadRequestException("Missing shortname")) } else { - val shortname = stringFormatter.validateAndEscapeProjectShortname(value, throw AssertionException("not valid")) - Right(new Shortname(shortname) {}) + val validatedValue = Validation( + stringFormatter.validateAndEscapeProjectShortname(value, throw AssertionException("not valid")) + ) + validatedValue.map(new Shortname(_) {}) } } @@ -165,12 +170,17 @@ object Shortname { * Project Longname value object. */ sealed abstract case class Longname private (value: String) -object Longname { - def create(value: String): Either[Throwable, Longname] = +object Longname { self => + def make(value: String): Validation[Throwable, Longname] = if (value.isEmpty) { - Left(BadRequestException("Missing long name")) + Validation.fail(BadRequestException("Missing long name")) } else { - Right(new Longname(value) {}) + Validation.succeed(new Longname(value) {}) + } + def make(value: Option[String]): Validation[Throwable, Option[Longname]] = + value match { + case None => Validation.succeed(None) + case Some(v) => self.make(v).map(Some(_)) } } @@ -179,11 +189,11 @@ object Longname { */ sealed abstract case class Keywords private (value: Seq[String]) object Keywords { - def create(value: Seq[String]): Either[Throwable, Keywords] = + def make(value: Seq[String]): Validation[Throwable, Keywords] = if (value.isEmpty) { - Left(BadRequestException("Missing keywords")) + Validation.fail(BadRequestException("Missing keywords")) } else { - Right(new Keywords(value) {}) + Validation.succeed(new Keywords(value) {}) } } @@ -191,12 +201,17 @@ object Keywords { * Project Logo value object. */ sealed abstract case class Logo private (value: String) -object Logo { - def create(value: String): Either[Throwable, Logo] = +object Logo { self => + def make(value: String): Validation[Throwable, Logo] = if (value.isEmpty) { - Left(BadRequestException("Missing logo")) + Validation.fail(BadRequestException("Missing logo")) } else { - Right(new Logo(value) {}) + Validation.succeed(new Logo(value) {}) + } + def make(value: Option[String]): Validation[Throwable, Option[Logo]] = + value match { + case None => Validation.succeed(None) + case Some(v) => self.make(v).map(Some(_)) } } @@ -222,8 +237,8 @@ object Name { */ sealed abstract case class Selfjoin private (value: Boolean) object Selfjoin { - def create(value: Boolean): Either[Throwable, Selfjoin] = - Right(new Selfjoin(value) {}) + def make(value: Boolean): Validation[Throwable, Selfjoin] = + Validation.succeed(new Selfjoin(value) {}) } /** @@ -231,8 +246,8 @@ object Selfjoin { */ sealed abstract case class Status private (value: Boolean) object Status { - def create(value: Boolean): Either[Throwable, Status] = - Right(new Status(value) {}) + def make(value: Boolean): Validation[Throwable, Status] = + Validation.succeed(new Status(value) {}) } /** @@ -240,10 +255,10 @@ object Status { */ sealed abstract case class Description private (value: Seq[StringLiteralV2]) object Description { - def create(value: Seq[StringLiteralV2]): Either[Throwable, Description] = + def make(value: Seq[StringLiteralV2]): Validation[Throwable, Description] = if (value.isEmpty) { - Left(BadRequestException("Missing description")) + Validation.fail(BadRequestException("Missing description")) } else { - Right(new Description(value) {}) + Validation.succeed(new Description(value) {}) } } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/BUILD.bazel b/webapi/src/main/scala/org/knora/webapi/routing/BUILD.bazel index 0b46389554..96f85d798c 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/BUILD.bazel +++ b/webapi/src/main/scala/org/knora/webapi/routing/BUILD.bazel @@ -32,6 +32,8 @@ scala_library( "@maven//:com_typesafe_play_twirl_api_2_13", "@maven//:com_typesafe_scala_logging_scala_logging_2_13", "@maven//:commons_validator_commons_validator", + "@maven//:dev_zio_zio_2_13", + "@maven//:dev_zio_zio_prelude_2_13", "@maven//:io_spray_spray_json_2_13", "@maven//:io_swagger_swagger_annotations", "@maven//:io_swagger_swagger_jaxrs", diff --git a/webapi/src/main/scala/org/knora/webapi/routing/KnoraRoute.scala b/webapi/src/main/scala/org/knora/webapi/routing/KnoraRoute.scala index cf033cd542..40ba9fd855 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/KnoraRoute.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/KnoraRoute.scala @@ -41,6 +41,7 @@ import org.knora.webapi.messages.admin.responder.projectsmessages.{ } import org.knora.webapi.messages.admin.responder.usersmessages.UserADM import org.knora.webapi.settings.{KnoraDispatchers, KnoraSettings, KnoraSettingsImpl} +import zio.prelude.Validation import scala.concurrent.{ExecutionContext, Future} @@ -159,4 +160,16 @@ abstract class KnoraRoute(routeData: KnoraRouteData) extends KnoraRouteFactory(r )).mapTo[ProjectGetResponseADM] } yield projectInfoResponse.project } + + /** + * Helper method converting an [[Either]] to a [[Future]]. + */ + def toFuture[A](either: Either[Throwable, A]): Future[A] = either.fold(Future.failed, Future.successful) + + /** + * Helper method converting an [[zio.prelude.Validation]] to a [[Future]]. + * FIXME: only the first error is returned, which defeats the purpose, but don't know better at the moment. + */ + def toFuture[A](validation: Validation[Throwable, A]): Future[A] = + validation.fold(errors => Future.failed(errors.head), Future.successful) } diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala index 038a117211..75f7648942 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/GroupsRouteADM.scala @@ -96,11 +96,11 @@ class GroupsRouteADM(routeData: KnoraRouteData) id = stringFormatter .validateAndEscapeOptionalIri(apiRequest.id, throw BadRequestException(s"Invalid group IRI")), name = Name.create(apiRequest.name).fold(e => throw e, v => v), - descriptions = Description.create(apiRequest.descriptions).fold(e => throw e, v => v), + descriptions = Description.make(apiRequest.descriptions).fold(e => throw e.head, v => v), project = stringFormatter .validateAndEscapeProjectIri(apiRequest.project, throw BadRequestException(s"Invalid project IRI")), - status = Status.create(apiRequest.status).fold(e => throw e, v => v), - selfjoin = Selfjoin.create(apiRequest.selfjoin).fold(e => throw e, v => v) + status = Status.make(apiRequest.status).fold(e => throw e.head, v => v), + selfjoin = Selfjoin.make(apiRequest.selfjoin).fold(e => throw e.head, v => v) ) val requestMessage = for { diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala index 3f1c77390f..9dc4ced9de 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/ProjectsRouteADM.scala @@ -19,8 +19,6 @@ package org.knora.webapi.routing.admin -import java.nio.file.Files -import java.util.UUID import akka.Done import akka.http.scaladsl.model.headers.{ContentDispositionTypes, `Content-Disposition`} import akka.http.scaladsl.model.{ContentTypes, HttpEntity} @@ -31,7 +29,6 @@ import akka.stream.IOResult import akka.stream.scaladsl.{FileIO, Source} import akka.util.ByteString import io.swagger.annotations._ -import javax.ws.rs.Path import org.knora.webapi.IRI import org.knora.webapi.annotation.ApiMayChange import org.knora.webapi.exceptions.BadRequestException @@ -48,6 +45,11 @@ import org.knora.webapi.messages.admin.responder.valueObjects.{ Status } import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilADM} +import zio.prelude.Validation + +import java.nio.file.Files +import java.util.UUID +import javax.ws.rs.Path import scala.concurrent.Future import scala.util.Try @@ -149,34 +151,28 @@ class ProjectsRouteADM(routeData: KnoraRouteData) private def addProject(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath) { post { entity(as[CreateProjectApiRequestADM]) { apiRequest => requestContext => - val maybeLongname: Option[Longname] = apiRequest.longname match { - case Some(value) => Some(Longname.create(value).fold(error => throw error, value => value)) - case None => None - } - - val maybeLogo: Option[Logo] = apiRequest.logo match { - case Some(value) => Some(Logo.create(value).fold(error => throw error, value => value)) - case None => None - } - - val projectCreatePayload: ProjectCreatePayloadADM = - ProjectCreatePayloadADM.create( - id = stringFormatter - .validateAndEscapeOptionalProjectIri(apiRequest.id, throw BadRequestException(s"Invalid project IRI")), - shortname = Shortname.create(apiRequest.shortname).fold(error => throw error, value => value), - shortcode = Shortcode.create(apiRequest.shortcode).fold(error => throw error, value => value), - longname = maybeLongname, - description = Description.create(apiRequest.description).fold(error => throw error, value => value), - keywords = Keywords.create(apiRequest.keywords).fold(error => throw error, value => value), - logo = maybeLogo, - status = Status.create(apiRequest.status).fold(error => throw error, value => value), - selfjoin = Selfjoin.create(apiRequest.selfjoin).fold(error => throw error, value => value) + // zio prelude: validation + val id = Validation( + stringFormatter + .validateAndEscapeOptionalProjectIri(apiRequest.id, throw BadRequestException(s"Invalid project IRI")) + ) + val shortname = Shortname.make(apiRequest.shortname) + val shortcode = Shortcode.make(apiRequest.shortcode) + val longname = Longname.make(apiRequest.longname) + val description = Description.make(apiRequest.description) + val keywords = Keywords.make(apiRequest.keywords) + val logo = Logo.make(apiRequest.logo) + val status = Status.make(apiRequest.status) + val selfjoin = Selfjoin.make(apiRequest.selfjoin) + + val projectCreatePayload: Validation[Throwable, ProjectCreatePayloadADM] = + Validation.validateWith(id, shortname, shortcode, longname, description, keywords, logo, status, selfjoin)( + ProjectCreatePayloadADM ) + val requestMessage: Future[ProjectCreateRequestADM] = for { - requestingUser <- getUserADM( - requestContext = requestContext, - featureFactoryConfig = featureFactoryConfig - ) + projectCreatePayload <- toFuture(projectCreatePayload) + requestingUser <- getUserADM(requestContext, featureFactoryConfig) } yield ProjectCreateRequestADM( createRequest = projectCreatePayload, featureFactoryConfig = featureFactoryConfig, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala index cbaba332bb..736aaf536a 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/UsersRouteADM.scala @@ -145,7 +145,7 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit givenName = GivenName.create(apiRequest.givenName).fold(error => throw error, value => value), familyName = FamilyName.create(apiRequest.familyName).fold(error => throw error, value => value), password = Password.create(apiRequest.password).fold(error => throw error, value => value), - status = Status.create(apiRequest.status).fold(error => throw error, value => value), + status = Status.make(apiRequest.status).fold(error => throw error.head, value => value), lang = LanguageCode.create(apiRequest.lang).fold(error => throw error, value => value), systemAdmin = SystemAdmin.create(apiRequest.systemAdmin).fold(error => throw error, value => value) ) @@ -414,7 +414,7 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } val newStatus = apiRequest.status match { - case Some(status) => Status.create(status).fold(error => throw error, value => value) + case Some(status) => Status.make(status).fold(error => throw error.head, value => value) case None => throw BadRequestException("The status is missing.") } @@ -464,7 +464,7 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit } /* update existing user's status to false */ - val status = Status.create(false).fold(error => throw error, value => value) + val status = Status.make(false).fold(error => throw error.head, value => value) val requestMessage: Future[UserChangeStatusRequestADM] = for { requestingUser <- getUserADM( diff --git a/webapi/src/test/scala/org/knora/webapi/IntegrationSpec.scala b/webapi/src/test/scala/org/knora/webapi/IntegrationSpec.scala index 10366c6fae..98be27b920 100644 --- a/webapi/src/test/scala/org/knora/webapi/IntegrationSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/IntegrationSpec.scala @@ -21,32 +21,23 @@ package org.knora.webapi import akka.actor.{ActorRef, ActorSystem} import akka.dispatch.MessageDispatcher +import akka.pattern.ask import akka.util.Timeout import com.typesafe.config.{Config, ConfigFactory} import com.typesafe.scalalogging.LazyLogging import org.knora.webapi.messages.StringFormatter -import org.knora.webapi.messages.store.triplestoremessages.{ - CheckTriplestoreRequest, - CheckTriplestoreResponse, - RdfDataObject, - ResetRepositoryContent, - TriplestoreStatus -} +import org.knora.webapi.messages.store.triplestoremessages._ import org.knora.webapi.settings.{KnoraDispatchers, KnoraSettings, KnoraSettingsImpl} import org.scalatest.BeforeAndAfterAll import org.scalatest.matchers.should.Matchers -import org.scalatest.wordspec.{AnyWordSpecLike, AsyncWordSpecLike} +import org.scalatest.wordspec.AsyncWordSpecLike +import zio.Console.printLine +import zio.Schedule.{Decision, WithState} +import zio.{Schedule, _} import scala.concurrent.{Await, ExecutionContext, Future} import scala.language.postfixOps import scala.util.{Failure, Success, Try} -import akka.pattern.ask -import org.knora.webapi.messages.store.triplestoremessages.TriplestoreStatus.TriplestoreStatus -import zio.Schedule.Decision -import zio.clock.Clock -import zio.console.{Console, putStrLn} -import zio.duration._ -import zio.{BootstrapRuntime, IO, Runtime, Schedule, Task, ZIO} object IntegrationSpec { @@ -107,7 +98,7 @@ abstract class IntegrationSpec(_config: Config) value <- if (checkResult.triplestoreStatus == TriplestoreStatus.ServiceAvailable) { - ZIO.effectTotal(logger.info("... triplestore is ready.")) + ZIO.succeed(logger.info("... triplestore is ready.")) } else { ZIO.fail( new Exception( @@ -117,8 +108,12 @@ abstract class IntegrationSpec(_config: Config) } } yield value - implicit val rt: Runtime[Clock with Console] = Runtime.default - rt.unsafeRun(checkTriplestore.retry(ScheduleUtil.schedule)) + implicit val rt: Runtime[Has[Clock] with Has[Console]] = Runtime.default + rt.unsafeRun( + checkTriplestore + .retry(ScheduleUtil.schedule) + .foldZIO(ex => printLine("Exception Failed"), v => printLine(s"Succeeded with $v")) + ) } protected def loadTestData( @@ -139,22 +134,22 @@ object ScheduleUtil { /** * Retry every second for 60 times, i.e., 60 seconds in total. */ - def schedule[A]: Schedule[Console, Any, (Long, Long)] = Schedule.spaced(1.second) && Schedule + def schedule[A]: WithState[(Long, Long), Has[Console], Any, (Long, Long)] = Schedule.spaced(1.second) && Schedule .recurs(60) .onDecision({ - case Decision.Done(_) => putStrLn(s"done trying").orDie - case Decision.Continue(attempt, _, _) => putStrLn(s"attempt #$attempt").orDie + case (_, _, Decision.Done) => printLine(s"done trying").orDie + case (_, attempt, Decision.Continue(_)) => printLine(s"attempt #$attempt").orDie }) } //// ZIO helpers //// object LegacyRuntime { + val runtime: Runtime[Has[Clock] with Has[Console]] = Runtime.default + /** * Transforms a [[Task]] into a [[Future]]. */ def fromTask[Res](body: => Task[Res]): Future[Res] = - MyRuntime.unsafeRunToFuture(body) + runtime.unsafeRunToFuture(body) } - -object MyRuntime extends BootstrapRuntime diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/ValueObjectsADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/ValueObjectsADMSpec.scala index 77b082cc02..1e2f2be0b1 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/ValueObjectsADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/ValueObjectsADMSpec.scala @@ -55,7 +55,7 @@ class ValueObjectsADMSpec extends UnitSpec(ValueObjectsADMSpec.config) { givenName = GivenName.create(createUserApiRequestADM.givenName).fold(error => throw error, value => value), familyName = FamilyName.create(createUserApiRequestADM.familyName).fold(error => throw error, value => value), password = Password.create(createUserApiRequestADM.password).fold(error => throw error, value => value), - status = Status.create(createUserApiRequestADM.status).fold(error => throw error, value => value), + status = Status.make(createUserApiRequestADM.status).fold(error => throw error.head, value => value), lang = LanguageCode.create(createUserApiRequestADM.lang).fold(error => throw error, value => value), systemAdmin = SystemAdmin.create(createUserApiRequestADM.systemAdmin).fold(error => throw error, value => value) ) diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/GroupsResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/GroupsResponderADMSpec.scala index 89e4a6500e..301e384b52 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/GroupsResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/GroupsResponderADMSpec.scala @@ -106,15 +106,15 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit id = None, name = Name.create("NewGroup").fold(e => throw e, v => v), descriptions = Description - .create( + .make( Seq( StringLiteralV2(value = """NewGroupDescription with "quotes" and """, language = Some("en")) ) ) - .fold(e => throw e, v => v), + .fold(e => throw e.head, v => v), project = SharedTestDataADM.IMAGES_PROJECT_IRI, - status = Status.create(true).fold(e => throw e, v => v), - selfjoin = Selfjoin.create(false).fold(e => throw e, v => v) + status = Status.make(true).fold(e => throw e.head, v => v), + selfjoin = Selfjoin.make(false).fold(e => throw e.head, v => v) ), featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.imagesUser01, @@ -142,11 +142,11 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit id = Some(imagesReviewerGroup.id), name = Name.create("NewGroup").fold(e => throw e, v => v), descriptions = Description - .create(Seq(StringLiteralV2(value = "NewGroupDescription", language = Some("en")))) - .fold(e => throw e, v => v), + .make(Seq(StringLiteralV2(value = "NewGroupDescription", language = Some("en")))) + .fold(e => throw e.head, v => v), project = SharedTestDataADM.IMAGES_PROJECT_IRI, - status = Status.create(true).fold(e => throw e, v => v), - selfjoin = Selfjoin.create(false).fold(e => throw e, v => v) + status = Status.make(true).fold(e => throw e.head, v => v), + selfjoin = Selfjoin.make(false).fold(e => throw e.head, v => v) ), featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.imagesUser01, @@ -164,11 +164,11 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit id = Some(""), name = Name.create("OtherNewGroup").fold(e => throw e, v => v), descriptions = Description - .create(Seq(StringLiteralV2(value = "OtherNewGroupDescription", language = Some("en")))) - .fold(e => throw e, v => v), + .make(Seq(StringLiteralV2(value = "OtherNewGroupDescription", language = Some("en")))) + .fold(e => throw e.head, v => v), project = "", - status = Status.create(true).fold(e => throw e, v => v), - selfjoin = Selfjoin.create(false).fold(e => throw e, v => v) + status = Status.make(true).fold(e => throw e.head, v => v), + selfjoin = Selfjoin.make(false).fold(e => throw e.head, v => v) ), featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.imagesUser01, diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala index fee570ed18..13aea3f9a9 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala @@ -226,19 +226,18 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) "CREATE a project and return the project info if the supplied shortname is unique" in { val shortCode = "111c" responderManager ! ProjectCreateRequestADM( - createRequest = ProjectCreatePayloadADM - .create( - shortname = Shortname.create("newproject").fold(error => throw error, value => value), - shortcode = Shortcode.create(shortCode).fold(error => throw error, value => value), // lower case - longname = Some(Longname.create("project longname").fold(error => throw error, value => value)), - description = Description - .create(Seq(StringLiteralV2(value = "project description", language = Some("en")))) - .fold(error => throw error, value => value), - keywords = Keywords.create(Seq("keywords")).fold(error => throw error, value => value), - logo = Some(Logo.create("/fu/bar/baz.jpg").fold(error => throw error, value => value)), - status = Status.create(true).fold(error => throw error, value => value), - selfjoin = Selfjoin.create(false).fold(error => throw error, value => value) - ), + createRequest = ProjectCreatePayloadADM( + shortname = Shortname.make("newproject").fold(error => throw error.head, value => value), + shortcode = Shortcode.make(shortCode).fold(error => throw error.head, value => value), // lower case + longname = Longname.make(Some("project longname")).fold(error => throw error.head, value => value), + description = Description + .make(Seq(StringLiteralV2(value = "project description", language = Some("en")))) + .fold(error => throw error.head, value => value), + keywords = Keywords.make(Seq("keywords")).fold(error => throw error.head, value => value), + logo = Logo.make(Some("/fu/bar/baz.jpg")).fold(error => throw error.head, value => value), + status = Status.make(true).fold(error => throw error.head, value => value), + selfjoin = Selfjoin.make(false).fold(error => throw error.head, value => value) + ), featureFactoryConfig = defaultFeatureFactoryConfig, SharedTestDataADM.rootUser, UUID.randomUUID() @@ -325,19 +324,18 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) "CREATE a project and return the project info if the supplied shortname and shortcode is unique" in { responderManager ! ProjectCreateRequestADM( - createRequest = ProjectCreatePayloadADM - .create( - shortname = Shortname.create("newproject2").fold(error => throw error, value => value), - shortcode = Shortcode.create("1112").fold(error => throw error, value => value), // lower case - longname = Some(Longname.create("project longname").fold(error => throw error, value => value)), - description = Description - .create(Seq(StringLiteralV2(value = "project description", language = Some("en")))) - .fold(error => throw error, value => value), - keywords = Keywords.create(Seq("keywords")).fold(error => throw error, value => value), - logo = Some(Logo.create("/fu/bar/baz.jpg").fold(error => throw error, value => value)), - status = Status.create(true).fold(error => throw error, value => value), - selfjoin = Selfjoin.create(false).fold(error => throw error, value => value) - ), + createRequest = ProjectCreatePayloadADM( + shortname = Shortname.make("newproject2").fold(error => throw error.head, value => value), + shortcode = Shortcode.make("1112").fold(error => throw error.head, value => value), // lower case + longname = Some(Longname.make("project longname").fold(error => throw error.head, value => value)), + description = Description + .make(Seq(StringLiteralV2(value = "project description", language = Some("en")))) + .fold(error => throw error.head, value => value), + keywords = Keywords.make(Seq("keywords")).fold(error => throw error.head, value => value), + logo = Logo.make(Some("/fu/bar/baz.jpg")).fold(error => throw error.head, value => value), + status = Status.make(true).fold(error => throw error.head, value => value), + selfjoin = Selfjoin.make(false).fold(error => throw error.head, value => value) + ), featureFactoryConfig = defaultFeatureFactoryConfig, SharedTestDataADM.rootUser, UUID.randomUUID() @@ -359,19 +357,19 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) val descriptionWithSpecialCharacter = "project \\\"description\\\"" val keywordWithSpecialCharacter = "new \\\"keyword\\\"" responderManager ! ProjectCreateRequestADM( - createRequest = ProjectCreatePayloadADM - .create( - shortname = Shortname.create("project_with_character").fold(error => throw error, value => value), - shortcode = Shortcode.create("1312").fold(error => throw error, value => value), // lower case - longname = Some(Longname.create(longnameWithSpecialCharacter).fold(error => throw error, value => value)), - description = Description - .create(Seq(StringLiteralV2(value = descriptionWithSpecialCharacter, language = Some("en")))) - .fold(error => throw error, value => value), - keywords = Keywords.create(Seq(keywordWithSpecialCharacter)).fold(error => throw error, value => value), - logo = Some(Logo.create("/fu/bar/baz.jpg").fold(error => throw error, value => value)), - status = Status.create(true).fold(error => throw error, value => value), - selfjoin = Selfjoin.create(false).fold(error => throw error, value => value) - ), + createRequest = ProjectCreatePayloadADM( + shortname = Shortname.make("project_with_character").fold(error => throw error.head, value => value), + shortcode = Shortcode.make("1312").fold(error => throw error.head, value => value), // lower case + longname = + Longname.make(Some(longnameWithSpecialCharacter)).fold(error => throw error.head, value => value), + description = Description + .make(Seq(StringLiteralV2(value = descriptionWithSpecialCharacter, language = Some("en")))) + .fold(error => throw error.head, value => value), + keywords = Keywords.make(Seq(keywordWithSpecialCharacter)).fold(error => throw error.head, value => value), + logo = Logo.make(Some("/fu/bar/baz.jpg")).fold(error => throw error.head, value => value), + status = Status.make(true).fold(error => throw error.head, value => value), + selfjoin = Selfjoin.make(false).fold(error => throw error.head, value => value) + ), featureFactoryConfig = defaultFeatureFactoryConfig, SharedTestDataADM.rootUser, UUID.randomUUID() @@ -393,19 +391,18 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) "return a 'DuplicateValueException' during creation if the supplied project shortname is not unique" in { responderManager ! ProjectCreateRequestADM( - createRequest = ProjectCreatePayloadADM - .create( - shortname = Shortname.create("newproject").fold(error => throw error, value => value), - shortcode = Shortcode.create("111C").fold(error => throw error, value => value), // lower case - longname = Some(Longname.create("project longname").fold(error => throw error, value => value)), - description = Description - .create(Seq(StringLiteralV2(value = "project description", language = Some("en")))) - .fold(error => throw error, value => value), - keywords = Keywords.create(Seq("keywords")).fold(error => throw error, value => value), - logo = Some(Logo.create("/fu/bar/baz.jpg").fold(error => throw error, value => value)), - status = Status.create(true).fold(error => throw error, value => value), - selfjoin = Selfjoin.create(false).fold(error => throw error, value => value) - ), + createRequest = ProjectCreatePayloadADM( + shortname = Shortname.make("newproject").fold(error => throw error.head, value => value), + shortcode = Shortcode.make("111C").fold(error => throw error.head, value => value), // lower case + longname = Longname.make(Some("project longname")).fold(error => throw error.head, value => value), + description = Description + .make(Seq(StringLiteralV2(value = "project description", language = Some("en")))) + .fold(error => throw error.head, value => value), + keywords = Keywords.make(Seq("keywords")).fold(error => throw error.head, value => value), + logo = Logo.make(Some("/fu/bar/baz.jpg")).fold(error => throw error.head, value => value), + status = Status.make(true).fold(error => throw error.head, value => value), + selfjoin = Selfjoin.make(false).fold(error => throw error.head, value => value) + ), featureFactoryConfig = defaultFeatureFactoryConfig, SharedTestDataADM.rootUser, UUID.randomUUID() @@ -415,19 +412,18 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) "return a 'DuplicateValueException' during creation if the supplied project shortname is unique but the shortcode is not" in { responderManager ! ProjectCreateRequestADM( - createRequest = ProjectCreatePayloadADM - .create( - shortname = Shortname.create("newproject3").fold(error => throw error, value => value), - shortcode = Shortcode.create("111C").fold(error => throw error, value => value), // lower case - longname = Some(Longname.create("project longname").fold(error => throw error, value => value)), - description = Description - .create(Seq(StringLiteralV2(value = "project description", language = Some("en")))) - .fold(error => throw error, value => value), - keywords = Keywords.create(Seq("keywords")).fold(error => throw error, value => value), - logo = Some(Logo.create("/fu/bar/baz.jpg").fold(error => throw error, value => value)), - status = Status.create(true).fold(error => throw error, value => value), - selfjoin = Selfjoin.create(false).fold(error => throw error, value => value) - ), + createRequest = ProjectCreatePayloadADM( + shortname = Shortname.make("newproject3").fold(error => throw error.head, value => value), + shortcode = Shortcode.make("111C").fold(error => throw error.head, value => value), // lower case + longname = Longname.make(Some("project longname")).fold(error => throw error.head, value => value), + description = Description + .make(Seq(StringLiteralV2(value = "project description", language = Some("en")))) + .fold(error => throw error.head, value => value), + keywords = Keywords.make(Seq("keywords")).fold(error => throw error.head, value => value), + logo = Logo.make(Some("/fu/bar/baz.jpg")).fold(error => throw error.head, value => value), + status = Status.make(true).fold(error => throw error.head, value => value), + selfjoin = Selfjoin.make(false).fold(error => throw error.head, value => value) + ), featureFactoryConfig = defaultFeatureFactoryConfig, SharedTestDataADM.rootUser, UUID.randomUUID() diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/UsersResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/UsersResponderADMSpec.scala index 5095f75ec7..250009b59a 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/UsersResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/UsersResponderADMSpec.scala @@ -257,7 +257,7 @@ class UsersResponderADMSpec extends CoreSpec(UsersResponderADMSpec.config) with givenName = GivenName.create("Donald").fold(error => throw error, value => value), familyName = FamilyName.create("Duck").fold(error => throw error, value => value), password = Password.create("test").fold(error => throw error, value => value), - status = Status.create(true).fold(error => throw error, value => value), + status = Status.make(true).fold(error => throw error.head, value => value), lang = LanguageCode.create("en").fold(error => throw error, value => value), systemAdmin = SystemAdmin.create(false).fold(error => throw error, value => value) ), @@ -284,7 +284,7 @@ class UsersResponderADMSpec extends CoreSpec(UsersResponderADMSpec.config) with givenName = GivenName.create("Donald").fold(error => throw error, value => value), familyName = FamilyName.create("Duck").fold(error => throw error, value => value), password = Password.create("test").fold(error => throw error, value => value), - status = Status.create(true).fold(error => throw error, value => value), + status = Status.make(true).fold(error => throw error.head, value => value), lang = LanguageCode.create("en").fold(error => throw error, value => value), systemAdmin = SystemAdmin.create(false).fold(error => throw error, value => value) ), @@ -305,7 +305,7 @@ class UsersResponderADMSpec extends CoreSpec(UsersResponderADMSpec.config) with givenName = GivenName.create("Donald").fold(error => throw error, value => value), familyName = FamilyName.create("Duck").fold(error => throw error, value => value), password = Password.create("test").fold(error => throw error, value => value), - status = Status.create(true).fold(error => throw error, value => value), + status = Status.make(true).fold(error => throw error.head, value => value), lang = LanguageCode.create("en").fold(error => throw error, value => value), systemAdmin = SystemAdmin.create(false).fold(error => throw error, value => value) ), @@ -469,7 +469,7 @@ class UsersResponderADMSpec extends CoreSpec(UsersResponderADMSpec.config) with "UPDATE the user's status, (deleting) making him inactive " in { responderManager ! UserChangeStatusRequestADM( userIri = SharedTestDataADM.normalUser.id, - status = Status.create(false).fold(error => throw error, value => value), + status = Status.make(false).fold(error => throw error.head, value => value), featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.superUser, UUID.randomUUID() @@ -480,7 +480,7 @@ class UsersResponderADMSpec extends CoreSpec(UsersResponderADMSpec.config) with responderManager ! UserChangeStatusRequestADM( userIri = SharedTestDataADM.normalUser.id, - status = Status.create(true).fold(error => throw error, value => value), + status = Status.make(true).fold(error => throw error.head, value => value), featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.superUser, UUID.randomUUID() @@ -557,7 +557,7 @@ class UsersResponderADMSpec extends CoreSpec(UsersResponderADMSpec.config) with /* Status is updated by other normal user */ responderManager ! UserChangeStatusRequestADM( userIri = SharedTestDataADM.superUser.id, - status = Status.create(false).fold(error => throw error, value => value), + status = Status.make(false).fold(error => throw error.head, value => value), featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.normalUser, UUID.randomUUID @@ -585,7 +585,7 @@ class UsersResponderADMSpec extends CoreSpec(UsersResponderADMSpec.config) with responderManager ! UserChangeStatusRequestADM( userIri = KnoraSystemInstances.Users.SystemUser.id, - status = Status.create(false).fold(error => throw error, value => value), + status = Status.make(false).fold(error => throw error.head, value => value), featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.superUser, UUID.randomUUID() @@ -598,7 +598,7 @@ class UsersResponderADMSpec extends CoreSpec(UsersResponderADMSpec.config) with responderManager ! UserChangeStatusRequestADM( userIri = KnoraSystemInstances.Users.AnonymousUser.id, - status = Status.create(false).fold(error => throw error, value => value), + status = Status.make(false).fold(error => throw error.head, value => value), featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = SharedTestDataADM.superUser, UUID.randomUUID()