Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
refactor: move value objects to separate project (DEV-615) (#2069)
* add new project and deps

* move value objects to separate ptoject

* move & refactor error messages

* copy temporarily + adjust v2 validation related stuff

* fix all errors but StringLiteralV2

* fix StringLiteralV2 class related issues

* fix admin tests

* move tests to new project

* rewrite tests to ZIO

* fixt tests

* refactor IRI type property names

* cleanup

* review changes

* more review changes

* even more review changes
  • Loading branch information
mpro7 committed Jun 2, 2022
1 parent 63b8844 commit b55eb12
Show file tree
Hide file tree
Showing 60 changed files with 2,354 additions and 1,650 deletions.
9 changes: 9 additions & 0 deletions build.sbt
Expand Up @@ -201,6 +201,7 @@ lazy val webapi: Project = Project(id = "webapi", base = file("webapi"))
),
buildInfoPackage := "org.knora.webapi.http.version"
)
.dependsOn(valueObjects)

lazy val webapiJavaRunOptions = Seq(
// "-showversion",
Expand Down Expand Up @@ -282,3 +283,11 @@ lazy val schemaRepoSearchService = project
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
.dependsOn(schemaRepo)

lazy val valueObjects = project
.in(file("dsp-value-objects"))
.settings(
name := "valueObjects",
libraryDependencies ++= Dependencies.valueObjectsLibraryDependencies,
testFrameworks := Seq(new TestFramework("zio.test.sbt.ZTestFramework"))
)
99 changes: 99 additions & 0 deletions dsp-value-objects/src/main/scala/dsp/valueobjects/Group.scala
@@ -0,0 +1,99 @@
/*
* Copyright © 2021 - 2022 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors.
* SPDX-License-Identifier: Apache-2.0
*/

package dsp.valueobjects

import zio.prelude.Validation

object Group {

/**
* GroupName value object.
*/
sealed abstract case class GroupName private (value: String)
object GroupName { self =>
def make(value: String): Validation[Throwable, GroupName] =
if (value.isEmpty) {
Validation.fail(V2.BadRequestException(GroupErrorMessages.GroupNameMissing))
} else {
val validatedValue = Validation(
V2IriValidation.toSparqlEncodedString(
value,
throw V2.BadRequestException(GroupErrorMessages.GroupNameInvalid)
)
)

validatedValue.map(new GroupName(_) {})
}

def make(value: Option[String]): Validation[Throwable, Option[GroupName]] =
value match {
case Some(v) => self.make(v).map(Some(_))
case None => Validation.succeed(None)
}
}

/**
* GroupDescriptions value object.
*/
sealed abstract case class GroupDescriptions private (value: Seq[V2.StringLiteralV2])
object GroupDescriptions { self =>
def make(value: Seq[V2.StringLiteralV2]): Validation[Throwable, GroupDescriptions] =
if (value.isEmpty) {
Validation.fail(V2.BadRequestException(GroupErrorMessages.GroupDescriptionsMissing))
} else {
val validatedDescriptions = Validation(value.map { description =>
val validatedDescription =
V2IriValidation.toSparqlEncodedString(
description.value,
throw V2.BadRequestException(GroupErrorMessages.GroupDescriptionsInvalid)
)
V2.StringLiteralV2(value = validatedDescription, language = description.language)
})
validatedDescriptions.map(new GroupDescriptions(_) {})
}

def make(value: Option[Seq[V2.StringLiteralV2]]): Validation[Throwable, Option[GroupDescriptions]] =
value match {
case Some(v) => self.make(v).map(Some(_))
case None => Validation.succeed(None)
}
}

/**
* GroupStatus value object.
*/
sealed abstract case class GroupStatus private (value: Boolean)
object GroupStatus { self =>
def make(value: Boolean): Validation[Throwable, GroupStatus] =
Validation.succeed(new GroupStatus(value) {})
def make(value: Option[Boolean]): Validation[Throwable, Option[GroupStatus]] =
value match {
case Some(v) => self.make(v).map(Some(_))
case None => Validation.succeed(None)
}
}

/**
* GroupSelfJoin value object.
*/
sealed abstract case class GroupSelfJoin private (value: Boolean)
object GroupSelfJoin { self =>
def make(value: Boolean): Validation[Throwable, GroupSelfJoin] =
Validation.succeed(new GroupSelfJoin(value) {})
def make(value: Option[Boolean]): Validation[Throwable, Option[GroupSelfJoin]] =
value match {
case Some(v) => self.make(v).map(Some(_))
case None => Validation.succeed(None)
}
}
}

object GroupErrorMessages {
val GroupNameMissing = "Group name cannot be empty."
val GroupNameInvalid = "Group name is invalid."
val GroupDescriptionsMissing = "Group description cannot be empty."
val GroupDescriptionsInvalid = "Group description is invalid."
}
157 changes: 157 additions & 0 deletions dsp-value-objects/src/main/scala/dsp/valueobjects/Iri.scala
@@ -0,0 +1,157 @@
/*
* Copyright © 2021 - 2022 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors.
* SPDX-License-Identifier: Apache-2.0
*/

package dsp.valueobjects

import zio.prelude.Validation

sealed trait Iri
object Iri {

/**
* GroupIri value object.
*/
sealed abstract case class GroupIri private (value: String) extends Iri
object GroupIri { self =>
def make(value: String): Validation[Throwable, GroupIri] =
if (value.isEmpty) {
Validation.fail(V2.BadRequestException(IriErrorMessages.GroupIriMissing))
} else {
val isUuid: Boolean = V2UuidValidation.hasUuidLength(value.split("/").last)

if (!V2IriValidation.isKnoraGroupIriStr(value)) {
Validation.fail(V2.BadRequestException(IriErrorMessages.GroupIriInvalid))
} else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) {
Validation.fail(V2.BadRequestException(IriErrorMessages.UuidVersionInvalid))
} else {
val validatedValue = Validation(
V2IriValidation.validateAndEscapeIri(value, throw V2.BadRequestException(IriErrorMessages.GroupIriInvalid))
)

validatedValue.map(new GroupIri(_) {})
}
}

def make(value: Option[String]): Validation[Throwable, Option[GroupIri]] =
value match {
case Some(v) => self.make(v).map(Some(_))
case None => Validation.succeed(None)
}
}

/**
* ListIri value object.
*/
sealed abstract case class ListIri private (value: String) extends Iri
object ListIri { self =>
def make(value: String): Validation[Throwable, ListIri] =
if (value.isEmpty) {
Validation.fail(V2.BadRequestException(IriErrorMessages.ListIriMissing))
} else {
val isUuid: Boolean = V2UuidValidation.hasUuidLength(value.split("/").last)

if (!V2IriValidation.isKnoraListIriStr(value)) {
Validation.fail(V2.BadRequestException(IriErrorMessages.ListIriInvalid))
} else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) {
Validation.fail(V2.BadRequestException(IriErrorMessages.UuidVersionInvalid))
} else {
val validatedValue = Validation(
V2IriValidation.validateAndEscapeIri(
value,
throw V2.BadRequestException(IriErrorMessages.ListIriInvalid)
)
)

validatedValue.map(new ListIri(_) {})
}
}

def make(value: Option[String]): Validation[Throwable, Option[ListIri]] =
value match {
case Some(v) => self.make(v).map(Some(_))
case None => Validation.succeed(None)
}
}

/**
* ProjectIri value object.
*/
sealed abstract case class ProjectIri private (value: String) extends Iri
object ProjectIri { self =>
def make(value: String): Validation[Throwable, ProjectIri] =
if (value.isEmpty) {
Validation.fail(V2.BadRequestException(IriErrorMessages.ProjectIriMissing))
} else {
val isUuid: Boolean = V2UuidValidation.hasUuidLength(value.split("/").last)

if (!V2IriValidation.isKnoraProjectIriStr(value)) {
Validation.fail(V2.BadRequestException(IriErrorMessages.ProjectIriInvalid))
} else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) {
Validation.fail(V2.BadRequestException(IriErrorMessages.UuidVersionInvalid))
} else {
val validatedValue = Validation(
V2IriValidation.validateAndEscapeProjectIri(
value,
throw V2.BadRequestException(IriErrorMessages.ProjectIriInvalid)
)
)

validatedValue.map(new ProjectIri(_) {})
}
}

def make(value: Option[String]): Validation[Throwable, Option[ProjectIri]] =
value match {
case Some(v) => self.make(v).map(Some(_))
case None => Validation.succeed(None)
}
}

/**
* UserIri value object.
*/
sealed abstract case class UserIri private (value: String) extends Iri
object UserIri { self =>
def make(value: String): Validation[Throwable, UserIri] =
if (value.isEmpty) {
Validation.fail(V2.BadRequestException(IriErrorMessages.UserIriMissing))
} else {
val isUuid: Boolean = V2UuidValidation.hasUuidLength(value.split("/").last)

if (!V2IriValidation.isKnoraUserIriStr(value)) {
Validation.fail(V2.BadRequestException(IriErrorMessages.UserIriInvalid))
} else if (isUuid && !V2UuidValidation.isUuidVersion4Or5(value)) {
Validation.fail(V2.BadRequestException(IriErrorMessages.UuidVersionInvalid))
} else {
val validatedValue = Validation(
V2IriValidation.validateAndEscapeUserIri(
value,
throw V2.BadRequestException(IriErrorMessages.UserIriInvalid)
)
)

validatedValue.map(new UserIri(_) {})
}
}

def make(value: Option[String]): Validation[Throwable, Option[UserIri]] =
value match {
case Some(v) => self.make(v).map(Some(_))
case None => Validation.succeed(None)
}
}
}

object IriErrorMessages {
val GroupIriMissing = "Group IRI cannot be empty."
val GroupIriInvalid = "Group IRI is invalid."
val ListIriMissing = "List IRI cannot be empty."
val ListIriInvalid = "List IRI is invalid"
val ProjectIriMissing = "Project IRI cannot be empty."
val ProjectIriInvalid = "Project IRI is invalid."
val UserIriMissing = "User IRI cannot be empty."
val UserIriInvalid = "User IRI is invalid."
val UuidVersionInvalid = "Invalid UUID used to create IRI. Only versions 4 and 5 are supported."
}

0 comments on commit b55eb12

Please sign in to comment.