diff --git a/build.sbt b/build.sbt index e7b99caba7..1d9db6bc83 100644 --- a/build.sbt +++ b/build.sbt @@ -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", @@ -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")) + ) diff --git a/dsp-value-objects/src/main/scala/dsp/valueobjects/Group.scala b/dsp-value-objects/src/main/scala/dsp/valueobjects/Group.scala new file mode 100644 index 0000000000..d0672b6705 --- /dev/null +++ b/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." +} diff --git a/dsp-value-objects/src/main/scala/dsp/valueobjects/Iri.scala b/dsp-value-objects/src/main/scala/dsp/valueobjects/Iri.scala new file mode 100644 index 0000000000..88c9d0fe0c --- /dev/null +++ b/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." +} diff --git a/dsp-value-objects/src/main/scala/dsp/valueobjects/List.scala b/dsp-value-objects/src/main/scala/dsp/valueobjects/List.scala new file mode 100644 index 0000000000..6a666be84f --- /dev/null +++ b/dsp-value-objects/src/main/scala/dsp/valueobjects/List.scala @@ -0,0 +1,123 @@ +/* + * 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 List { + + /** + * List ListName value object. + */ + sealed abstract case class ListName private (value: String) + object ListName { self => + def make(value: String): Validation[Throwable, ListName] = + if (value.isEmpty) { + Validation.fail(V2.BadRequestException(ListErrorMessages.ListNameMissing)) + } else { + val validatedValue = Validation( + V2IriValidation.toSparqlEncodedString(value, throw V2.BadRequestException(ListErrorMessages.ListNameInvalid)) + ) + + validatedValue.map(new ListName(_) {}) + } + + def make(value: Option[String]): Validation[Throwable, Option[ListName]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } + + /** + * List Position value object. + */ + sealed abstract case class Position private (value: Int) + object Position { self => + def make(value: Int): Validation[Throwable, Position] = + if (value < -1) { + Validation.fail(V2.BadRequestException(ListErrorMessages.InvalidPosition)) + } else { + Validation.succeed(new Position(value) {}) + } + + def make(value: Option[Int]): Validation[Throwable, Option[Position]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } + + /** + * List Labels value object. + */ + sealed abstract case class Labels private (value: Seq[V2.StringLiteralV2]) + object Labels { self => + def make(value: Seq[V2.StringLiteralV2]): Validation[Throwable, Labels] = + if (value.isEmpty) { + Validation.fail(V2.BadRequestException(ListErrorMessages.LabelsMissing)) + } else { + val validatedLabels = Validation(value.map { label => + val validatedLabel = + V2IriValidation.toSparqlEncodedString( + label.value, + throw V2.BadRequestException(ListErrorMessages.LabelsInvalid) + ) + V2.StringLiteralV2(value = validatedLabel, language = label.language) + }) + + validatedLabels.map(new Labels(_) {}) + } + + def make(value: Option[Seq[V2.StringLiteralV2]]): Validation[Throwable, Option[Labels]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } + + /** + * List Comments value object. + */ + sealed abstract case class Comments private (value: Seq[V2.StringLiteralV2]) + object Comments { self => + def make(value: Seq[V2.StringLiteralV2]): Validation[Throwable, Comments] = + if (value.isEmpty) { + Validation.fail(V2.BadRequestException(ListErrorMessages.CommentsMissing)) + } else { + val validatedComments = Validation(value.map { comment => + val validatedComment = + V2IriValidation.toSparqlEncodedString( + comment.value, + throw V2.BadRequestException(ListErrorMessages.CommentsInvalid) + ) + V2.StringLiteralV2(value = validatedComment, language = comment.language) + }) + + validatedComments.map(new Comments(_) {}) + } + + def make(value: Option[Seq[V2.StringLiteralV2]]): Validation[Throwable, Option[Comments]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } +} + +object ListErrorMessages { + val ListNameMissing = "List name cannot be empty." + val ListNameInvalid = "List name is invalid." + val LabelsMissing = "At least one label needs to be supplied." + val LabelsInvalid = "Invalid label." + val CommentsMissing = "At least one comment needs to be supplied." + val CommentsInvalid = "Invalid comment." + val ListCreatePermission = "A list can only be created by the project or system administrator." + val ListNodeCreatePermission = "A list node can only be created by the project or system administrator." + val ListChangePermission = "A list can only be changed by the project or system administrator." + val UpdateRequestEmptyLabel = "List labels cannot be empty." + val InvalidPosition = "Invalid position value is given. Position should be either a positive value, 0 or -1." +} diff --git a/dsp-value-objects/src/main/scala/dsp/valueobjects/Project.scala b/dsp-value-objects/src/main/scala/dsp/valueobjects/Project.scala new file mode 100644 index 0000000000..7cb062e608 --- /dev/null +++ b/dsp-value-objects/src/main/scala/dsp/valueobjects/Project.scala @@ -0,0 +1,183 @@ +/* + * 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 Project { + + // TODO-mpro: longname, description, keywords, logo are missing enhanced validation + + /** + * Project Shortcode value object. + */ + sealed abstract case class Shortcode private (value: String) + object Shortcode { self => + def make(value: String): Validation[Throwable, Shortcode] = + if (value.isEmpty) { + Validation.fail(V2.BadRequestException(ProjectErrorMessages.ShortcodeMissing)) + } else { + val validatedValue: Validation[Throwable, String] = Validation( + V2ProjectIriValidation.validateProjectShortcode( + value, + throw V2.BadRequestException(ProjectErrorMessages.ShortcodeInvalid) + ) + ) + validatedValue.map(new Shortcode(_) {}) + } + + def make(value: Option[String]): Validation[Throwable, Option[Shortcode]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } + + /** + * Project Shortname value object. + */ + sealed abstract case class Shortname private (value: String) + object Shortname { self => + def make(value: String): Validation[Throwable, Shortname] = + if (value.isEmpty) { + Validation.fail(V2.BadRequestException(ProjectErrorMessages.ShortnameMissing)) + } else { + val validatedValue = Validation( + V2ProjectIriValidation.validateAndEscapeProjectShortname( + value, + throw V2.BadRequestException(ProjectErrorMessages.ShortnameInvalid) + ) + ) + validatedValue.map(new Shortname(_) {}) + } + + def make(value: Option[String]): Validation[Throwable, Option[Shortname]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } + + /** + * Project Longname value object. + */ + sealed abstract case class Longname private (value: String) + object Longname { self => + def make(value: String): Validation[Throwable, Longname] = + if (value.isEmpty) { + Validation.fail(V2.BadRequestException(ProjectErrorMessages.LongnameMissing)) + } else { + 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(_)) + } + } + + /** + * ProjectDescription value object. + */ + sealed abstract case class ProjectDescription private (value: Seq[V2.StringLiteralV2]) // make it plural + object ProjectDescription { self => + def make(value: Seq[V2.StringLiteralV2]): Validation[Throwable, ProjectDescription] = + if (value.isEmpty) { + Validation.fail(V2.BadRequestException(ProjectErrorMessages.ProjectDescriptionsMissing)) + } else { + Validation.succeed(new ProjectDescription(value) {}) + } + + def make(value: Option[Seq[V2.StringLiteralV2]]): Validation[Throwable, Option[ProjectDescription]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } + + /** + * Project Keywords value object. + */ + sealed abstract case class Keywords private (value: Seq[String]) + object Keywords { self => + def make(value: Seq[String]): Validation[Throwable, Keywords] = + if (value.isEmpty) { + Validation.fail(V2.BadRequestException(ProjectErrorMessages.KeywordsMissing)) + } else { + Validation.succeed(new Keywords(value) {}) + } + + def make(value: Option[Seq[String]]): Validation[Throwable, Option[Keywords]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } + + /** + * Project Logo value object. + */ + sealed abstract case class Logo private (value: String) + object Logo { self => + def make(value: String): Validation[Throwable, Logo] = + if (value.isEmpty) { + Validation.fail(V2.BadRequestException(ProjectErrorMessages.LogoMissing)) + } else { + Validation.succeed(new Logo(value) {}) + } + def make(value: Option[String]): Validation[Throwable, Option[Logo]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } + + /** + * ProjectSelfjoin value object. + */ + sealed abstract case class ProjectSelfJoin private (value: Boolean) + object ProjectSelfJoin { self => + def make(value: Boolean): Validation[Throwable, ProjectSelfJoin] = + Validation.succeed(new ProjectSelfJoin(value) {}) + + def make(value: Option[Boolean]): Validation[Throwable, Option[ProjectSelfJoin]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } + + /** + * ProjectStatus value object. + */ + sealed abstract case class ProjectStatus private (value: Boolean) + object ProjectStatus { self => + def make(value: Boolean): Validation[Throwable, ProjectStatus] = + Validation.succeed(new ProjectStatus(value) {}) + + def make(value: Option[Boolean]): Validation[Throwable, Option[ProjectStatus]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } +} + +object ProjectErrorMessages { + val ShortcodeMissing = "Shortcode cannot be empty." + val ShortcodeInvalid = "Shortcode is invalid." + val ShortnameMissing = "Shortname cannot be empty." + val ShortnameInvalid = "Shortname is invalid." + val LongnameMissing = "Longname cannot be empty." + val LongnameInvalid = "Longname is invalid." + val ProjectDescriptionsMissing = "Description cannot be empty." + val ProjectDescriptionsInvalid = "Description is invalid." + val KeywordsMissing = "Keywords cannot be empty." + val KeywordsInvalid = "Keywords are invalid." + val LogoMissing = "Logo cannot be empty." + val LogoInvalid = "Logo is invalid." +} diff --git a/dsp-value-objects/src/main/scala/dsp/valueobjects/User.scala b/dsp-value-objects/src/main/scala/dsp/valueobjects/User.scala new file mode 100644 index 0000000000..344a59eb9c --- /dev/null +++ b/dsp-value-objects/src/main/scala/dsp/valueobjects/User.scala @@ -0,0 +1,188 @@ +/* + * 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 +import scala.util.matching.Regex + +object User { + + // TODO-mpro: password, givenname, familyname are missing enhanced validation + + /** + * Username value object. + */ + sealed abstract case class Username private (value: String) + object Username { self => + + /** + * A regex that matches a valid username + * - 4 - 50 characters long + * - Only contains alphanumeric characters, underscore and dot. + * - Underscore and dot can't be at the end or start of a username + * - Underscore or dot can't be used multiple times in a row + */ + private val UsernameRegex: Regex = + """^(?=.{4,50}$)(?![_.])(?!.*[_.]{2})[a-zA-Z0-9._]+(? Validation.succeed(new Username(value) {}) + case None => Validation.fail(V2.BadRequestException(UserErrorMessages.UsernameInvalid)) + } + } + + def make(value: Option[String]): Validation[Throwable, Option[Username]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } + + /** + * Email value object. + */ + sealed abstract case class Email private (value: String) + object Email { self => + private val EmailRegex: Regex = """^.+@.+$""".r + + def make(value: String): Validation[Throwable, Email] = + if (value.isEmpty) { + Validation.fail(V2.BadRequestException(UserErrorMessages.EmailMissing)) + } else { + EmailRegex.findFirstIn(value) match { + case Some(value) => Validation.succeed(new Email(value) {}) + case None => Validation.fail(V2.BadRequestException(UserErrorMessages.EmailInvalid)) + } + } + + def make(value: Option[String]): Validation[Throwable, Option[Email]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } + + /** + * GivenName value object. + */ + sealed abstract case class GivenName private (value: String) + object GivenName { self => + def make(value: String): Validation[Throwable, GivenName] = + if (value.isEmpty) { + Validation.fail(V2.BadRequestException(UserErrorMessages.GivenNameMissing)) + } else { + Validation.succeed(new GivenName(value) {}) + } + + def make(value: Option[String]): Validation[Throwable, Option[GivenName]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } + + /** + * FamilyName value object. + */ + sealed abstract case class FamilyName private (value: String) + object FamilyName { self => + def make(value: String): Validation[Throwable, FamilyName] = + if (value.isEmpty) { + Validation.fail(V2.BadRequestException(UserErrorMessages.FamilyNameMissing)) + } else { + Validation.succeed(new FamilyName(value) {}) + } + + def make(value: Option[String]): Validation[Throwable, Option[FamilyName]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } + + /** + * Password value object. + */ + sealed abstract case class Password private (value: String) + object Password { self => + private val PasswordRegex: Regex = """^[\s\S]*$""".r + + def make(value: String): Validation[Throwable, Password] = + if (value.isEmpty) { + Validation.fail(V2.BadRequestException(UserErrorMessages.PasswordMissing)) + } else { + PasswordRegex.findFirstIn(value) match { + case Some(value) => Validation.succeed(new Password(value) {}) + case None => Validation.fail(V2.BadRequestException(UserErrorMessages.PasswordInvalid)) + } + } + + def make(value: Option[String]): Validation[Throwable, Option[Password]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } + + /** + * UserStatus value object. + */ + sealed abstract case class UserStatus private (value: Boolean) + object UserStatus { + def make(value: Boolean): Validation[Throwable, UserStatus] = + Validation.succeed(new UserStatus(value) {}) + } + + /** + * LanguageCode value object. + */ + sealed abstract case class LanguageCode private (value: String) + object LanguageCode { self => + def make(value: String): Validation[Throwable, LanguageCode] = + if (value.isEmpty) { + Validation.fail(V2.BadRequestException(UserErrorMessages.LanguageCodeMissing)) + } else if (!V2.SupportedLanguageCodes.contains(value)) { + Validation.fail(V2.BadRequestException(UserErrorMessages.LanguageCodeInvalid)) + } else { + Validation.succeed(new LanguageCode(value) {}) + } + + def make(value: Option[String]): Validation[Throwable, Option[LanguageCode]] = + value match { + case Some(v) => self.make(v).map(Some(_)) + case None => Validation.succeed(None) + } + } + + /** + * SystemAdmin value object. + */ + sealed abstract case class SystemAdmin private (value: Boolean) + object SystemAdmin { + def make(value: Boolean): Validation[Throwable, SystemAdmin] = + Validation.succeed(new SystemAdmin(value) {}) + } +} + +object UserErrorMessages { + val UsernameMissing = "Username cannot be empty." + val UsernameInvalid = "Username is invalid." + val EmailMissing = "Email cannot be empty." + val EmailInvalid = "Email is invalid." + val PasswordMissing = "Password cannot be empty." + val PasswordInvalid = "Password is invalid." + val GivenNameMissing = "GivenName cannot be empty." + val GivenNameInvalid = "GivenName is invalid." + val FamilyNameMissing = "FamilyName cannot be empty." + val FamilyNameInvalid = "FamilyName is invalid." + val LanguageCodeMissing = "LanguageCode cannot be empty." + val LanguageCodeInvalid = "LanguageCode is invalid." +} diff --git a/dsp-value-objects/src/main/scala/dsp/valueobjects/V2.scala b/dsp-value-objects/src/main/scala/dsp/valueobjects/V2.scala new file mode 100644 index 0000000000..c14d4c7be2 --- /dev/null +++ b/dsp-value-objects/src/main/scala/dsp/valueobjects/V2.scala @@ -0,0 +1,367 @@ +/* + * 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 java.util.UUID +import java.util.Base64 +import java.nio.ByteBuffer +import scala.util.matching.Regex +import org.apache.commons.validator.routines.UrlValidator +import org.apache.commons.lang3.StringUtils +import com.google.gwt.safehtml.shared.UriUtils.encodeAllowEscapes + +// TODO-mpro: don't forget to remove all occurances and additional "helper" +// implementations in webapi project which needed to be added temporary in order +// to avoid circular dependencies after moving value objects to separate project. +object V2 { + val DE: String = "de" + val EN: String = "en" + val FR: String = "fr" + val IT: String = "it" + val RM: String = "rm" + + val SupportedLanguageCodes: Set[String] = Set( + DE, + EN, + FR, + IT, + RM + ) + + /** + * Represents a string with language iso. Allows sorting inside collections by value. + * + * @param value the string value. + * @param language the language iso. + */ + case class StringLiteralV2(value: String, language: Option[String]) + + /** + * An exception indicating that the request parameters did not make sense. + * + * @param message a description of the error. + */ + case class BadRequestException(message: String, cause: Throwable = null) extends Exception(message, cause) +} + +object V2IriValidation { + type IRI = String + + // Characters that are escaped in strings that will be used in SPARQL. + private val SparqlEscapeInput = Array( + "\\", + "\"", + "'", + "\t", + "\n" + ) + + // Escaped characters as they are used in SPARQL. + private val SparqlEscapeOutput = Array( + "\\\\", + "\\\"", + "\\'", + "\\t", + "\\n" + ) + + /** + * Makes a string safe to be entered in the triplestore by escaping special chars. + * + * If the param `revert` is set to `true`, the string is unescaped. + * + * @param s a string. + * @param errorFun a function that throws an exception. It will be called if the string is empty or contains + * a carriage return (`\r`). + * @return the same string, escaped or unescaped as requested. + */ + def toSparqlEncodedString(s: String, errorFun: => Nothing): String = { + if (s.isEmpty || s.contains("\r")) errorFun + + // http://www.morelab.deusto.es/code_injection/ + + StringUtils.replaceEach( + s, + SparqlEscapeInput, + SparqlEscapeOutput + ) + } + + /** + * The domain name used to construct IRIs. + */ + val IriDomain: String = "rdfh.ch" + + // Valid URL schemes. + private val schemes = Array("http", "https") + + // A validator for URLs. + private val urlValidator = + new UrlValidator( + schemes, + UrlValidator.ALLOW_LOCAL_URLS + ) // local urls are URL-encoded IRIs as part of the whole URL + + /** + * Returns `true` if a string is an IRI. + * + * @param s the string to be checked. + * @return `true` if the string is an IRI. + */ + def isIri(s: String): Boolean = + urlValidator.isValid(s) + + /** + * Returns `true` if an IRI string looks like a Knora list IRI. + * + * @param iri the IRI to be checked. + */ + def isKnoraListIriStr(iri: IRI): Boolean = + isIri(iri) && iri.startsWith("http://" + IriDomain + "/lists/") + + /** + * Returns `true` if an IRI string looks like a Knora user IRI. + * + * @param iri the IRI to be checked. + */ + def isKnoraUserIriStr(iri: IRI): Boolean = + isIri(iri) && iri.startsWith("http://" + IriDomain + "/users/") + + /** + * Returns `true` if an IRI string looks like a Knora group IRI. + * + * @param iri the IRI to be checked. + */ + def isKnoraGroupIriStr(iri: IRI): Boolean = + isIri(iri) && iri.startsWith("http://" + IriDomain + "/groups/") + + /** + * Returns `true` if an IRI string looks like a Knora project IRI + * + * @param iri the IRI to be checked. + */ + def isKnoraProjectIriStr(iri: IRI): Boolean = + isIri(iri) && (iri.startsWith("http://" + IriDomain + "/projects/") || isKnoraBuiltInProjectIriStr(iri)) + + /** + * Returns `true` if an IRI string looks like a Knora built-in IRI: + * - http://www.knora.org/ontology/knora-admin#SystemProject + * - http://www.knora.org/ontology/knora-admin#SharedOntologiesProject + * + * @param iri the IRI to be checked. + */ + def isKnoraBuiltInProjectIriStr(iri: IRI): Boolean = { + + val builtInProjects = Seq( + SystemProject, + DefaultSharedOntologiesProject + ) + + isIri(iri) && builtInProjects.contains(iri) + } + + val KnoraAdminOntologyLabel: String = "knora-admin" + val KnoraAdminOntologyIri: IRI = KnoraInternal.InternalOntologyStart + "/" + KnoraAdminOntologyLabel + val KnoraAdminPrefixExpansion: IRI = KnoraAdminOntologyIri + "#" + val SystemProject: IRI = KnoraAdminPrefixExpansion + "SystemProject" + val DefaultSharedOntologiesProject: IRI = KnoraAdminPrefixExpansion + "DefaultSharedOntologiesProject" + + object KnoraInternal { + // The start and end of an internal Knora ontology IRI. + val InternalOntologyStart = "http://www.knora.org/ontology" + } + + /** + * Checks that a string represents a valid IRI. Also encodes the IRI, preserving existing %-escapes. + * + * @param s the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * IRI. + * @return the same string. + */ + def validateAndEscapeIri(s: String, errorFun: => Nothing): IRI = { + val urlEncodedStr = encodeAllowEscapes(s) + + if (urlValidator.isValid(urlEncodedStr)) { + urlEncodedStr + } else { + errorFun + } + } + + /** + * Check that the supplied IRI represents a valid project IRI. + * + * @param iri the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * project IRI. + * @return the same string but escaped. + */ + def validateAndEscapeProjectIri(iri: IRI, errorFun: => Nothing): IRI = + if (isKnoraProjectIriStr(iri)) { + toSparqlEncodedString(iri, errorFun) + } else { + errorFun + } + + /** + * Check that the supplied IRI represents a valid user IRI. + * + * @param iri the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * user IRI. + * @return the same string but escaped. + */ + def validateAndEscapeUserIri(iri: IRI, errorFun: => Nothing): String = + if (isKnoraUserIriStr(iri)) { + toSparqlEncodedString(iri, errorFun) + } else { + errorFun + } +} + +object V2UuidValidation { + + /** + * The length of the canonical representation of a UUID. + */ + val CanonicalUuidLength = 36 + + /** + * The length of a Base64-encoded UUID. + */ + val Base64UuidLength = 22 + + /** + * Checks if a string is the right length to be a canonical or Base64-encoded UUID. + * + * @param s the string to check. + * @return TRUE if the string is the right length to be a canonical or Base64-encoded UUID. + */ + def hasUuidLength(s: String): Boolean = + s.length == CanonicalUuidLength || s.length == Base64UuidLength + + /** + * Checks if UUID used to create IRI has correct version (4 and 5 are allowed). + * @param s the string (IRI) to be checked. + * @return TRUE for correct versions, FALSE for incorrect. + */ + def isUuidVersion4Or5(s: String): Boolean = + getUUIDVersion(s) == 4 || getUUIDVersion(s) == 5 + + /** + * Gets the last segment of IRI, decodes UUID and gets the version. + * @param s the string (IRI) to be checked. + * @return UUID version. + */ + def getUUIDVersion(s: String): Int = { + val encodedUUID = s.split("/").last + decodeUuid(encodedUUID).version() + } + + /** + * Calls `decodeUuidWithErr`, throwing [[InconsistentRepositoryDataException]] if the string cannot be parsed. + */ + def decodeUuid(uuidStr: String): UUID = + decodeUuidWithErr(uuidStr, throw V2.BadRequestException(s"Invalid UUID: $uuidStr")) + + /** + * Decodes a string representing a UUID in one of two formats: + * + * - The canonical 36-character format. + * - The 22-character Base64-encoded format returned by [[base64EncodeUuid]]. + * + * Shorter strings are padded with leading zeroes to 22 characters and parsed in Base64 format + * (this is non-reversible, and is needed only for working with test data). + * + * @param uuidStr the string to be decoded. + * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. + * @return the decoded [[UUID]]. + */ + def decodeUuidWithErr(uuidStr: String, errorFun: => Nothing): UUID = + if (uuidStr.length == CanonicalUuidLength) { + UUID.fromString(uuidStr) + } else if (uuidStr.length == Base64UuidLength) { + base64DecodeUuid(uuidStr) + } else if (uuidStr.length < Base64UuidLength) { + base64DecodeUuid(uuidStr.reverse.padTo(Base64UuidLength, '0').reverse) + } else { + errorFun + } + + private val base64Decoder = Base64.getUrlDecoder + + /** + * Decodes a Base64-encoded UUID. + * + * @param base64Uuid the Base64-encoded UUID to be decoded. + * @return the equivalent [[UUID]]. + */ + def base64DecodeUuid(base64Uuid: String): UUID = { + val bytes = base64Decoder.decode(base64Uuid) + val byteBuffer = ByteBuffer.wrap(bytes) + new UUID(byteBuffer.getLong, byteBuffer.getLong) + } +} + +object V2ProjectIriValidation { + // A regex sub-pattern for project IDs, which must consist of 4 hexadecimal digits. + private val ProjectIDPattern: String = + """\p{XDigit}{4,4}""" + + // A regex for matching a string containing the project ID. + private val ProjectIDRegex: Regex = ("^" + ProjectIDPattern + "$").r + + /** + * Given the project shortcode, checks if it is in a valid format, and converts it to upper case. + * + * @param shortcode the project's shortcode. + * @return the shortcode in upper case. + */ + def validateProjectShortcode(shortcode: String, errorFun: => Nothing): String = + ProjectIDRegex.findFirstIn(shortcode.toUpperCase) match { + case Some(value) => value + case None => errorFun + } + + // A regex sub-pattern for ontology prefix labels and local entity names. According to + // , a prefix label in Turtle must be a valid XML NCName + // . Knora also requires a local entity name to + // be an XML NCName. + private val NCNamePattern: String = + """[\p{L}_][\p{L}0-9_.-]*""" + + // A regex for matching a string containing only an ontology prefix label or a local entity name. + private val NCNameRegex: Regex = ("^" + NCNamePattern + "$").r + + // A regex sub-pattern matching the random IDs generated by KnoraIdUtil, which are Base64-encoded + // using the "URL and Filename safe" Base 64 alphabet, without padding, as specified in Table 2 of + // RFC 4648. + private val Base64UrlPattern = "[A-Za-z0-9_-]+" + + private val Base64UrlPatternRegex: Regex = ("^" + Base64UrlPattern + "$").r + + /** + * Check that the string represents a valid project shortname. + * + * @param value the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * project shortname. + * @return the same string. + */ + def validateAndEscapeProjectShortname(shortname: String, errorFun: => Nothing): String = { + // Check that shortname matches NCName pattern + val ncNameMatch = NCNameRegex.findFirstIn(shortname) match { + case Some(value) => value + case None => errorFun + } + // Check that shortname is URL safe + Base64UrlPatternRegex.findFirstIn(ncNameMatch) match { + case Some(shortname) => V2IriValidation.toSparqlEncodedString(shortname, errorFun) + case None => errorFun + } + } +} diff --git a/dsp-value-objects/src/test/scala/dsp/valueobjects/GroupSpec.scala b/dsp-value-objects/src/test/scala/dsp/valueobjects/GroupSpec.scala new file mode 100644 index 0000000000..a35a15459f --- /dev/null +++ b/dsp-value-objects/src/test/scala/dsp/valueobjects/GroupSpec.scala @@ -0,0 +1,114 @@ +/* + * 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 dsp.valueobjects.Group._ +import zio.prelude.Validation +import zio.test._ + +/** + * This spec is used to test the [[Group]] value objects creation. + */ +object GroupSpec extends ZIOSpecDefault { + private val validName = "Valid group name" + private val invalidName = "Invalid group name\r" + private val validDescription = Seq(V2.StringLiteralV2(value = "Valid group description", language = Some("en"))) + private val invalidDescription = Seq( + V2.StringLiteralV2(value = "Invalid group description \r", language = Some("en")) + ) + + def spec = (groupNameTest + groupDescriptionsTest + groupStatusTest + groupSelfJoinTest) + + private val groupNameTest = suite("GroupSpec - GroupName")( + test("pass an empty value and throw an error") { + assertTrue(GroupName.make("") == Validation.fail(V2.BadRequestException(GroupErrorMessages.GroupNameMissing))) && + assertTrue( + GroupName.make(Some("")) == Validation.fail(V2.BadRequestException(GroupErrorMessages.GroupNameMissing)) + ) + }, + test("pass an invalid value and throw an error") { + assertTrue( + GroupName.make(invalidName) == Validation.fail( + V2.BadRequestException(GroupErrorMessages.GroupNameInvalid) + ) + ) && + assertTrue( + GroupName.make(Some(invalidName)) == Validation.fail( + V2.BadRequestException(GroupErrorMessages.GroupNameInvalid) + ) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(GroupName.make(validName).toOption.get.value == validName) && + assertTrue(GroupName.make(Option(validName)).getOrElse(null).get.value == validName) + }, + test("successfully validate passing None") { + assertTrue( + GroupName.make(None) == Validation.succeed(None) + ) + } + ) + + private val groupDescriptionsTest = suite("GroupSpec - GroupDescriptions")( + test("pass an empty object and throw an error") { + assertTrue( + GroupDescriptions.make(Seq.empty) == Validation.fail( + V2.BadRequestException(GroupErrorMessages.GroupDescriptionsMissing) + ) + ) && + assertTrue( + GroupDescriptions.make(Some(Seq.empty)) == Validation.fail( + V2.BadRequestException(GroupErrorMessages.GroupDescriptionsMissing) + ) + ) + }, + test("pass an invalid object and throw an error") { + assertTrue( + GroupDescriptions.make(invalidDescription) == Validation.fail( + V2.BadRequestException(GroupErrorMessages.GroupDescriptionsInvalid) + ) + ) && + assertTrue( + GroupDescriptions.make(Some(invalidDescription)) == Validation.fail( + V2.BadRequestException(GroupErrorMessages.GroupDescriptionsInvalid) + ) + ) + }, + test("pass a valid object and successfully create value object") { + assertTrue(GroupDescriptions.make(validDescription).toOption.get.value == validDescription) && + assertTrue(GroupDescriptions.make(Option(validDescription)).getOrElse(null).get.value == validDescription) + }, + test("successfully validate passing None") { + assertTrue( + GroupDescriptions.make(None) == Validation.succeed(None) + ) + } + ) + + private val groupStatusTest = suite("GroupSpec - GroupStatus")( + test("pass a valid object and successfully create value object") { + assertTrue(GroupStatus.make(true).toOption.get.value == true) && + assertTrue(GroupStatus.make(Some(false)).getOrElse(null).get.value == false) + }, + test("successfully validate passing None") { + assertTrue( + GroupStatus.make(None) == Validation.succeed(None) + ) + } + ) + + private val groupSelfJoinTest = suite("GroupSpec - GroupSelfJoin")( + test("pass a valid object and successfully create value object") { + assertTrue(GroupSelfJoin.make(true).toOption.get.value == true) && + assertTrue(GroupSelfJoin.make(Some(false)).getOrElse(null).get.value == false) + }, + test("successfully validate passing None") { + assertTrue( + GroupSelfJoin.make(None) == Validation.succeed(None) + ) + } + ) +} diff --git a/dsp-value-objects/src/test/scala/dsp/valueobjects/IriSpec.scala b/dsp-value-objects/src/test/scala/dsp/valueobjects/IriSpec.scala new file mode 100644 index 0000000000..640288282d --- /dev/null +++ b/dsp-value-objects/src/test/scala/dsp/valueobjects/IriSpec.scala @@ -0,0 +1,195 @@ +/* + * 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 dsp.valueobjects.Iri._ +import zio.prelude.Validation +import zio.test._ + +/** + * This spec is used to test the [[Iri]] value objects creation. + */ +object IriSpec extends ZIOSpecDefault { + val invalidIri = "Invalid IRI" + val validGroupIri = "http://rdfh.ch/groups/0803/qBCJAdzZSCqC_2snW5Q7Nw" + val groupIriWithUUIDVersion3 = "http://rdfh.ch/groups/0803/rKAU0FNjPUKWqOT8MEW_UQ" + val validListIri = "http://rdfh.ch/lists/0803/qBCJAdzZSCqC_2snW5Q7Nw" + val listIriWithUUIDVersion3 = "http://rdfh.ch/lists/0803/6_xROK_UN1S2ZVNSzLlSXQ" + val validProjectIri = "http://rdfh.ch/projects/0001" + val projectIriWithUUIDVersion3 = "http://rdfh.ch/projects/tZjZhGSZMeCLA5VeUmwAmg" + val validUserIri = "http://rdfh.ch/users/jDEEitJESRi3pDaDjjQ1WQ" + val userIriWithUUIDVersion3 = "http://rdfh.ch/users/cCmdcpn2MO211YYOplR1hQ" + + def spec = (groupIriTest + listIriTest + projectIriTest) + + private val groupIriTest = suite("IriSpec - GroupIri")( + test("pass an empty value and throw an error") { + assertTrue(GroupIri.make("") == Validation.fail(V2.BadRequestException(IriErrorMessages.GroupIriMissing))) && + assertTrue( + GroupIri.make(Some("")) == Validation.fail(V2.BadRequestException(IriErrorMessages.GroupIriMissing)) + ) + }, + test("pass an invalid value and throw an error") { + assertTrue( + GroupIri.make(invalidIri) == Validation.fail( + V2.BadRequestException(IriErrorMessages.GroupIriInvalid) + ) + ) && + assertTrue( + GroupIri.make(Some(invalidIri)) == Validation.fail( + V2.BadRequestException(IriErrorMessages.GroupIriInvalid) + ) + ) + }, + test("pass an invalid IRI containing unsupported UUID version and throw an error") { + assertTrue( + GroupIri.make(groupIriWithUUIDVersion3) == Validation.fail( + V2.BadRequestException(IriErrorMessages.UuidVersionInvalid) + ) + ) && + assertTrue( + GroupIri.make(Some(groupIriWithUUIDVersion3)) == Validation.fail( + V2.BadRequestException(IriErrorMessages.UuidVersionInvalid) + ) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(GroupIri.make(validGroupIri).toOption.get.value == validGroupIri) && + assertTrue(GroupIri.make(Option(validGroupIri)).getOrElse(null).get.value == validGroupIri) + }, + test("successfully validate passing None") { + assertTrue( + GroupIri.make(None) == Validation.succeed(None) + ) + } + ) + + private val listIriTest = suite("IriSpec - ListIri")( + test("pass an empty value and throw an error") { + assertTrue(ListIri.make("") == Validation.fail(V2.BadRequestException(IriErrorMessages.ListIriMissing))) && + assertTrue( + ListIri.make(Some("")) == Validation.fail(V2.BadRequestException(IriErrorMessages.ListIriMissing)) + ) + }, + test("pass an invalid value and throw an error") { + assertTrue( + ListIri.make(invalidIri) == Validation.fail( + V2.BadRequestException(IriErrorMessages.ListIriInvalid) + ) + ) && + assertTrue( + ListIri.make(Some(invalidIri)) == Validation.fail( + V2.BadRequestException(IriErrorMessages.ListIriInvalid) + ) + ) + }, + test("pass an invalid IRI containing unsupported UUID version and throw an error") { + assertTrue( + ListIri.make(listIriWithUUIDVersion3) == Validation.fail( + V2.BadRequestException(IriErrorMessages.UuidVersionInvalid) + ) + ) && + assertTrue( + ListIri.make(Some(listIriWithUUIDVersion3)) == Validation.fail( + V2.BadRequestException(IriErrorMessages.UuidVersionInvalid) + ) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(ListIri.make(validListIri).toOption.get.value == validListIri) && + assertTrue(ListIri.make(Option(validListIri)).getOrElse(null).get.value == validListIri) + }, + test("successfully validate passing None") { + assertTrue( + ListIri.make(None) == Validation.succeed(None) + ) + } + ) + + private val projectIriTest = suite("IriSpec - ProjectIri")( + test("pass an empty value and throw an error") { + assertTrue(ProjectIri.make("") == Validation.fail(V2.BadRequestException(IriErrorMessages.ProjectIriMissing))) && + assertTrue( + ProjectIri.make(Some("")) == Validation.fail(V2.BadRequestException(IriErrorMessages.ProjectIriMissing)) + ) + }, + test("pass an invalid value and throw an error") { + assertTrue( + ProjectIri.make(invalidIri) == Validation.fail( + V2.BadRequestException(IriErrorMessages.ProjectIriInvalid) + ) + ) && + assertTrue( + ProjectIri.make(Some(invalidIri)) == Validation.fail( + V2.BadRequestException(IriErrorMessages.ProjectIriInvalid) + ) + ) + }, + test("pass an invalid IRI containing unsupported UUID version and throw an error") { + assertTrue( + ProjectIri.make(projectIriWithUUIDVersion3) == Validation.fail( + V2.BadRequestException(IriErrorMessages.UuidVersionInvalid) + ) + ) && + assertTrue( + ProjectIri.make(Some(projectIriWithUUIDVersion3)) == Validation.fail( + V2.BadRequestException(IriErrorMessages.UuidVersionInvalid) + ) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(ProjectIri.make(validProjectIri).toOption.get.value == validProjectIri) && + assertTrue(ProjectIri.make(Option(validProjectIri)).getOrElse(null).get.value == validProjectIri) + }, + test("successfully validate passing None") { + assertTrue( + ProjectIri.make(None) == Validation.succeed(None) + ) + } + ) + + private val UserIriTest = suite("IriSpec - ProjectIri")( + test("pass an empty value and throw an error") { + assertTrue(UserIri.make("") == Validation.fail(V2.BadRequestException(IriErrorMessages.UserIriMissing))) && + assertTrue( + UserIri.make(Some("")) == Validation.fail(V2.BadRequestException(IriErrorMessages.UserIriMissing)) + ) + }, + test("pass an invalid value and throw an error") { + assertTrue( + UserIri.make(invalidIri) == Validation.fail( + V2.BadRequestException(IriErrorMessages.UserIriInvalid) + ) + ) && + assertTrue( + UserIri.make(Some(invalidIri)) == Validation.fail( + V2.BadRequestException(IriErrorMessages.UserIriInvalid) + ) + ) + }, + test("pass an invalid IRI containing unsupported UUID version and throw an error") { + assertTrue( + UserIri.make(userIriWithUUIDVersion3) == Validation.fail( + V2.BadRequestException(IriErrorMessages.UuidVersionInvalid) + ) + ) && + assertTrue( + UserIri.make(Some(userIriWithUUIDVersion3)) == Validation.fail( + V2.BadRequestException(IriErrorMessages.UuidVersionInvalid) + ) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(UserIri.make(validUserIri).toOption.get.value == validUserIri) && + assertTrue(UserIri.make(Option(validUserIri)).getOrElse(null).get.value == validUserIri) + }, + test("successfully validate passing None") { + assertTrue( + UserIri.make(None) == Validation.succeed(None) + ) + } + ) +} diff --git a/dsp-value-objects/src/test/scala/dsp/valueobjects/ListSpec.scala b/dsp-value-objects/src/test/scala/dsp/valueobjects/ListSpec.scala new file mode 100644 index 0000000000..c4956e7bc6 --- /dev/null +++ b/dsp-value-objects/src/test/scala/dsp/valueobjects/ListSpec.scala @@ -0,0 +1,152 @@ +/* + * 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 dsp.valueobjects.List._ +import zio.prelude.Validation +import zio.test._ + +/** + * This spec is used to test the [[List]] value objects creation. + */ +object ListSpec extends ZIOSpecDefault { + private val validName = "Valid list name" + private val invalidName = "Invalid list name\r" + private val validPosition = 0 + private val invalidPosition = -2 + private val validLabel = Seq(V2.StringLiteralV2(value = "Valid list label", language = Some("en"))) + private val invalidLabel = Seq(V2.StringLiteralV2(value = "Invalid list label \r", language = Some("en"))) + private val validComment = Seq(V2.StringLiteralV2(value = "Valid list comment", language = Some("en"))) + private val invalidComment = Seq(V2.StringLiteralV2(value = "Invalid list comment \r", language = Some("en"))) + + def spec = (listNameTest + positionTest + labelsTest + commentsTest) + + private val listNameTest = suite("ListSpec - ListName")( + test("pass an empty value and throw an error") { + assertTrue(ListName.make("") == Validation.fail(V2.BadRequestException(ListErrorMessages.ListNameMissing))) && + assertTrue( + ListName.make(Some("")) == Validation.fail(V2.BadRequestException(ListErrorMessages.ListNameMissing)) + ) + }, + test("pass an invalid value and throw an error") { + assertTrue( + ListName.make(invalidName) == Validation.fail( + V2.BadRequestException(ListErrorMessages.ListNameInvalid) + ) + ) && + assertTrue( + ListName.make(Some(invalidName)) == Validation.fail( + V2.BadRequestException(ListErrorMessages.ListNameInvalid) + ) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(ListName.make(validName).toOption.get.value == validName) && + assertTrue(ListName.make(Option(validName)).getOrElse(null).get.value == validName) + }, + test("successfully validate passing None") { + assertTrue( + ListName.make(None) == Validation.succeed(None) + ) + } + ) + + private val positionTest = suite("ListSpec - Position")( + test("pass an invalid value and throw an error") { + assertTrue( + Position.make(invalidPosition) == Validation.fail( + V2.BadRequestException(ListErrorMessages.InvalidPosition) + ) + ) && + assertTrue( + Position.make(Some(invalidPosition)) == Validation.fail( + V2.BadRequestException(ListErrorMessages.InvalidPosition) + ) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(Position.make(validPosition).toOption.get.value == validPosition) && + assertTrue(Position.make(Option(validPosition)).getOrElse(null).get.value == validPosition) + }, + test("successfully validate passing None") { + assertTrue( + Position.make(None) == Validation.succeed(None) + ) + } + ) + + private val labelsTest = suite("ListSpec - Labels")( + test("pass an empty object and throw an error") { + assertTrue( + Labels.make(Seq.empty) == Validation.fail( + V2.BadRequestException(ListErrorMessages.LabelsMissing) + ) + ) && + assertTrue( + Labels.make(Some(Seq.empty)) == Validation.fail( + V2.BadRequestException(ListErrorMessages.LabelsMissing) + ) + ) + }, + test("pass an invalid object and throw an error") { + assertTrue( + Labels.make(invalidLabel) == Validation.fail( + V2.BadRequestException(ListErrorMessages.LabelsInvalid) + ) + ) && + assertTrue( + Labels.make(Some(invalidLabel)) == Validation.fail( + V2.BadRequestException(ListErrorMessages.LabelsInvalid) + ) + ) + }, + test("pass a valid object and successfully create value object") { + assertTrue(Labels.make(validLabel).toOption.get.value == validLabel) && + assertTrue(Labels.make(Option(validLabel)).getOrElse(null).get.value == validLabel) + }, + test("successfully validate passing None") { + assertTrue( + Labels.make(None) == Validation.succeed(None) + ) + } + ) + + private val commentsTest = suite("ListSpec - Comments")( + test("pass an empty object and throw an error") { + assertTrue( + Comments.make(Seq.empty) == Validation.fail( + V2.BadRequestException(ListErrorMessages.CommentsMissing) + ) + ) && + assertTrue( + Comments.make(Some(Seq.empty)) == Validation.fail( + V2.BadRequestException(ListErrorMessages.CommentsMissing) + ) + ) + }, + test("pass an invalid object and throw an error") { + assertTrue( + Comments.make(invalidComment) == Validation.fail( + V2.BadRequestException(ListErrorMessages.CommentsInvalid) + ) + ) && + assertTrue( + Comments.make(Some(invalidComment)) == Validation.fail( + V2.BadRequestException(ListErrorMessages.CommentsInvalid) + ) + ) + }, + test("pass a valid object and successfully create value object") { + assertTrue(Comments.make(validComment).toOption.get.value == validComment) && + assertTrue(Comments.make(Option(validComment)).getOrElse(null).get.value == validComment) + }, + test("successfully validate passing None") { + assertTrue( + Comments.make(None) == Validation.succeed(None) + ) + } + ) +} diff --git a/dsp-value-objects/src/test/scala/dsp/valueobjects/ProjectSpec.scala b/dsp-value-objects/src/test/scala/dsp/valueobjects/ProjectSpec.scala new file mode 100644 index 0000000000..d5914535c8 --- /dev/null +++ b/dsp-value-objects/src/test/scala/dsp/valueobjects/ProjectSpec.scala @@ -0,0 +1,207 @@ +/* + * 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 dsp.valueobjects.Project._ +import zio.prelude.Validation +import zio.test._ + +/** + * This spec is used to test the [[Project]] value objects creation. + */ +object ProjectSpec extends ZIOSpecDefault { + private val validShortcode = "1234" + private val invalidShortcode = "12345" + private val validShortname = "validShortname" + private val invalidShortname = "~!@#$%^&*()_+" + private val validLongname = "That is the project longname" + private val validDescription = Seq( + V2.StringLiteralV2(value = "Valid project description", language = Some("en")) + ) + private val validKeywords = Seq("key", "word") + private val validLogo = "/fu/bar/baz.jpg" + + def spec = + (shortcodeTest + shortnameTest + longnameTest + projectDescriptionsTest + keywordsTest + logoTest + projectStatusTest + projectSelfJoinTest) + + private val shortcodeTest = suite("ProjectSpec - Shortcode")( + test("pass an empty value and throw an error") { + assertTrue( + Shortcode.make("") == Validation.fail(V2.BadRequestException(ProjectErrorMessages.ShortcodeMissing)) + ) && + assertTrue( + Shortcode.make(Some("")) == Validation.fail(V2.BadRequestException(ProjectErrorMessages.ShortcodeMissing)) + ) + }, + test("pass an invalid value and throw an error") { + assertTrue( + Shortcode.make(invalidShortcode) == Validation.fail( + V2.BadRequestException(ProjectErrorMessages.ShortcodeInvalid) + ) + ) && + assertTrue( + Shortcode.make(Some(invalidShortcode)) == Validation.fail( + V2.BadRequestException(ProjectErrorMessages.ShortcodeInvalid) + ) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(Shortcode.make(validShortcode).toOption.get.value == validShortcode) && + assertTrue(Shortcode.make(Option(validShortcode)).getOrElse(null).get.value == validShortcode) + }, + test("successfully validate passing None") { + assertTrue( + Shortcode.make(None) == Validation.succeed(None) + ) + } + ) + + private val shortnameTest = suite("ProjectSpec - Shortname")( + test("pass an empty value and throw an error") { + assertTrue( + Shortname.make("") == Validation.fail(V2.BadRequestException(ProjectErrorMessages.ShortnameMissing)) + ) && + assertTrue( + Shortname.make(Some("")) == Validation.fail(V2.BadRequestException(ProjectErrorMessages.ShortnameMissing)) + ) + }, + test("pass an invalid value and throw an error") { + assertTrue( + Shortname.make(invalidShortname) == Validation.fail( + V2.BadRequestException(ProjectErrorMessages.ShortnameInvalid) + ) + ) && + assertTrue( + Shortname.make(Some(invalidShortname)) == Validation.fail( + V2.BadRequestException(ProjectErrorMessages.ShortnameInvalid) + ) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(Shortname.make(validShortname).toOption.get.value == validShortname) && + assertTrue(Shortname.make(Option(validShortname)).getOrElse(null).get.value == validShortname) + }, + test("successfully validate passing None") { + assertTrue( + Shortcode.make(None) == Validation.succeed(None) + ) + } + ) + + private val longnameTest = suite("ProjectSpec - Longname")( + test("pass an empty value and throw an error") { + assertTrue(Longname.make("") == Validation.fail(V2.BadRequestException(ProjectErrorMessages.LongnameMissing))) && + assertTrue( + Longname.make(Some("")) == Validation.fail(V2.BadRequestException(ProjectErrorMessages.LongnameMissing)) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(Longname.make(validLongname).toOption.get.value == validLongname) && + assertTrue(Longname.make(Option(validLongname)).getOrElse(null).get.value == validLongname) + }, + test("successfully validate passing None") { + assertTrue( + Shortcode.make(None) == Validation.succeed(None) + ) + } + ) + + private val projectDescriptionsTest = suite("ProjectSpec - ProjectDescriptions")( + test("pass an empty object and throw an error") { + assertTrue( + ProjectDescription.make(Seq.empty) == Validation.fail( + V2.BadRequestException(ProjectErrorMessages.ProjectDescriptionsMissing) + ) + ) && + assertTrue( + ProjectDescription.make(Some(Seq.empty)) == Validation.fail( + V2.BadRequestException(ProjectErrorMessages.ProjectDescriptionsMissing) + ) + ) + }, + test("pass a valid object and successfully create value object") { + assertTrue(ProjectDescription.make(validDescription).toOption.get.value == validDescription) && + assertTrue(ProjectDescription.make(Option(validDescription)).getOrElse(null).get.value == validDescription) + }, + test("successfully validate passing None") { + assertTrue( + ProjectDescription.make(None) == Validation.succeed(None) + ) + } + ) + + private val keywordsTest = suite("ProjectSpec - Keywords")( + test("pass an empty object and throw an error") { + assertTrue( + Keywords.make(Seq.empty) == Validation.fail( + V2.BadRequestException(ProjectErrorMessages.KeywordsMissing) + ) + ) && + assertTrue( + Keywords.make(Some(Seq.empty)) == Validation.fail( + V2.BadRequestException(ProjectErrorMessages.KeywordsMissing) + ) + ) + }, + test("pass a valid object and successfully create value object") { + assertTrue(Keywords.make(validKeywords).toOption.get.value == validKeywords) && + assertTrue(Keywords.make(Option(validKeywords)).getOrElse(null).get.value == validKeywords) + }, + test("successfully validate passing None") { + assertTrue( + Keywords.make(None) == Validation.succeed(None) + ) + } + ) + + private val logoTest = suite("ProjectSpec - Logo")( + test("pass an empty object and throw an error") { + assertTrue( + Logo.make("") == Validation.fail( + V2.BadRequestException(ProjectErrorMessages.LogoMissing) + ) + ) && + assertTrue( + Logo.make(Some("")) == Validation.fail( + V2.BadRequestException(ProjectErrorMessages.LogoMissing) + ) + ) + }, + test("pass a valid object and successfully create value object") { + assertTrue(Logo.make(validLogo).toOption.get.value == validLogo) && + assertTrue(Logo.make(Option(validLogo)).getOrElse(null).get.value == validLogo) + }, + test("successfully validate passing None") { + assertTrue( + Keywords.make(None) == Validation.succeed(None) + ) + } + ) + + private val projectStatusTest = suite("ProjectSpec - ProjectStatus")( + test("pass a valid object and successfully create value object") { + assertTrue(ProjectStatus.make(true).toOption.get.value == true) && + assertTrue(ProjectStatus.make(Some(false)).getOrElse(null).get.value == false) + }, + test("successfully validate passing None") { + assertTrue( + ProjectStatus.make(None) == Validation.succeed(None) + ) + } + ) + + private val projectSelfJoinTest = suite("ProjectSpec - ProjectSelfJoin")( + test("pass a valid object and successfully create value object") { + assertTrue(ProjectSelfJoin.make(true).toOption.get.value == true) && + assertTrue(ProjectSelfJoin.make(Some(false)).getOrElse(null).get.value == false) + }, + test("successfully validate passing None") { + assertTrue( + ProjectSelfJoin.make(None) == Validation.succeed(None) + ) + } + ) +} diff --git a/dsp-value-objects/src/test/scala/dsp/valueobjects/UserSpec.scala b/dsp-value-objects/src/test/scala/dsp/valueobjects/UserSpec.scala new file mode 100644 index 0000000000..e1c2720770 --- /dev/null +++ b/dsp-value-objects/src/test/scala/dsp/valueobjects/UserSpec.scala @@ -0,0 +1,284 @@ +/* + * 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 dsp.valueobjects.User._ +import zio.prelude.Validation +import zio.test._ + +/** + * This spec is used to test the [[User]] value objects creation. + */ +object UserSpec extends ZIOSpecDefault { + private val validUsername = "user008" + private val tooShortUsername = "123" + private val tooLongUsername = "01234567890123456789012345678901234567890123456789011" + private val invalidUsernameWithUnderscoreAsFirstChar = "_123" + private val invalidUsernameWithUnderscoreAsLastChar = "123_" + private val invalidUsernameWithMultipleUnderscoresInRow = "12__3" + private val invalidUsernameWithDotAsFirstChar = ".123" + private val invalidUsernameWithDotAsLastChar = "123." + private val invalidUsernameWithMultipleDotsInRow = "12..3" + private val invalidUsername = "user!@#$%^&*()_+" + private val validEmailAddress = "address@ch" + private val invalidEmailAddress = "invalid_email_address" + private val validPassword = "pass-word" + private val validGivenName = "John" + private val validFamilyName = "Rambo" + private val validLanguageCode = "de" + private val invalidLanguageCode = "00" + + def spec = + (usernameTest + emailTest + givenNameTest + familyNameTest + passwordTest + languageCodeTest + systemAdminTest) + + private val usernameTest = suite("UserSpec - Username")( + test("pass an empty value and throw an error") { + assertTrue(Username.make("") == Validation.fail(V2.BadRequestException(UserErrorMessages.UsernameMissing))) && + assertTrue( + Username.make(Some("")) == Validation.fail(V2.BadRequestException(UserErrorMessages.UsernameMissing)) + ) + }, + test("pass an invalid value and throw an error") { + assertTrue( + Username.make(invalidUsername) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) && + assertTrue( + Username.make(Some(invalidUsername)) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) + }, + test("pass too short value and throw an error") { + assertTrue( + Username.make(tooShortUsername) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) && + assertTrue( + Username.make(Some(tooShortUsername)) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) + }, + test("pass too long value and throw an error") { + assertTrue( + Username.make(tooLongUsername) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) && + assertTrue( + Username.make(Some(tooLongUsername)) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) + }, + test("pass an invalid value with '_' as the first char and throw an error") { + assertTrue( + Username.make(invalidUsernameWithUnderscoreAsFirstChar) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) && + assertTrue( + Username.make(Some(invalidUsernameWithUnderscoreAsFirstChar)) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) + }, + test("pass an invalid value with '_' as the last char and throw an error") { + assertTrue( + Username.make(invalidUsernameWithUnderscoreAsLastChar) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) && + assertTrue( + Username.make(Some(invalidUsernameWithUnderscoreAsLastChar)) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) + }, + test("pass an invalid value with '_' used multiple times in a row and throw an error") { + assertTrue( + Username.make(invalidUsernameWithMultipleUnderscoresInRow) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) && + assertTrue( + Username.make(Some(invalidUsernameWithMultipleUnderscoresInRow)) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) + }, + test("pass an invalid value with '.' as the first char and throw an error") { + assertTrue( + Username.make(invalidUsernameWithDotAsFirstChar) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) && + assertTrue( + Username.make(Some(invalidUsernameWithDotAsFirstChar)) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) + }, + test("pass an invalid value with '.' as the last char and throw an error") { + assertTrue( + Username.make(invalidUsernameWithDotAsLastChar) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) && + assertTrue( + Username.make(Some(invalidUsernameWithDotAsLastChar)) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) + }, + test("pass an invalid value with '.' used multiple times in a row and throw an error") { + assertTrue( + Username.make(invalidUsernameWithMultipleDotsInRow) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) && + assertTrue( + Username.make(Some(invalidUsernameWithMultipleDotsInRow)) == Validation.fail( + V2.BadRequestException(UserErrorMessages.UsernameInvalid) + ) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(Username.make(validUsername).toOption.get.value == validUsername) + assertTrue(Username.make(Option(validUsername)).getOrElse(null).get.value == validUsername) + } + + test("successfully validate passing None") { + assertTrue( + Username.make(None) == Validation.succeed(None) + ) + } + ) + + private val emailTest = suite("UserSpec - Email")( + test("pass an empty value and throw an error") { + assertTrue(Email.make("") == Validation.fail(V2.BadRequestException(UserErrorMessages.EmailMissing))) && + assertTrue( + Email.make(Some("")) == Validation.fail(V2.BadRequestException(UserErrorMessages.EmailMissing)) + ) + }, + test("pass an invalid value and throw an error") { + assertTrue( + Email.make(invalidEmailAddress) == Validation.fail( + V2.BadRequestException(UserErrorMessages.EmailInvalid) + ) + ) && + assertTrue( + Email.make(Some(invalidEmailAddress)) == Validation.fail( + V2.BadRequestException(UserErrorMessages.EmailInvalid) + ) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(Email.make(validEmailAddress).toOption.get.value == validEmailAddress) && + assertTrue(Email.make(Option(validEmailAddress)).getOrElse(null).get.value == validEmailAddress) + }, + test("successfully validate passing None") { + assertTrue( + Email.make(None) == Validation.succeed(None) + ) + } + ) + + private val givenNameTest = suite("UserSpec - GivenName")( + test("pass an empty value and throw an error") { + assertTrue(GivenName.make("") == Validation.fail(V2.BadRequestException(UserErrorMessages.GivenNameMissing))) && + assertTrue( + GivenName.make(Some("")) == Validation.fail(V2.BadRequestException(UserErrorMessages.GivenNameMissing)) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(GivenName.make(validGivenName).toOption.get.value == validGivenName) && + assertTrue(GivenName.make(Option(validGivenName)).getOrElse(null).get.value == validGivenName) + }, + test("successfully validate passing None") { + assertTrue( + GivenName.make(None) == Validation.succeed(None) + ) + } + ) + + private val familyNameTest = suite("UserSpec - FamilyName")( + test("pass an empty value and throw an error") { + assertTrue(FamilyName.make("") == Validation.fail(V2.BadRequestException(UserErrorMessages.FamilyNameMissing))) && + assertTrue( + FamilyName.make(Some("")) == Validation.fail(V2.BadRequestException(UserErrorMessages.FamilyNameMissing)) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(FamilyName.make(validFamilyName).toOption.get.value == validFamilyName) && + assertTrue(FamilyName.make(Option(validFamilyName)).getOrElse(null).get.value == validFamilyName) + }, + test("successfully validate passing None") { + assertTrue( + FamilyName.make(None) == Validation.succeed(None) + ) + } + ) + + private val passwordTest = suite("UserSpec - Password")( + test("pass an empty value and throw an error") { + assertTrue(Password.make("") == Validation.fail(V2.BadRequestException(UserErrorMessages.PasswordMissing))) && + assertTrue( + Password.make(Some("")) == Validation.fail(V2.BadRequestException(UserErrorMessages.PasswordMissing)) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(Password.make(validPassword).toOption.get.value == validPassword) && + assertTrue(Password.make(Option(validPassword)).getOrElse(null).get.value == validPassword) + }, + test("successfully validate passing None") { + assertTrue( + Password.make(None) == Validation.succeed(None) + ) + } + ) + + private val languageCodeTest = suite("UserSpec - LanguageCode")( + test("pass an empty value and throw an error") { + assertTrue( + LanguageCode.make("") == Validation.fail(V2.BadRequestException(UserErrorMessages.LanguageCodeMissing)) + ) && + assertTrue( + LanguageCode.make(Some("")) == Validation.fail(V2.BadRequestException(UserErrorMessages.LanguageCodeMissing)) + ) + }, + test("pass an invalid value and throw an error") { + assertTrue( + LanguageCode.make(invalidLanguageCode) == Validation.fail( + V2.BadRequestException(UserErrorMessages.LanguageCodeInvalid) + ) + ) && + assertTrue( + LanguageCode.make(Some(invalidLanguageCode)) == Validation.fail( + V2.BadRequestException(UserErrorMessages.LanguageCodeInvalid) + ) + ) + }, + test("pass a valid value and successfully create value object") { + assertTrue(LanguageCode.make(validLanguageCode).toOption.get.value == validLanguageCode) && + assertTrue(LanguageCode.make(Option(validLanguageCode)).getOrElse(null).get.value == validLanguageCode) + }, + test("successfully validate passing None") { + assertTrue( + LanguageCode.make(None) == Validation.succeed(None) + ) + } + ) + + private val systemAdminTest = suite("GroupSpec - SystemAdmin")( + test("pass a valid object and successfully create value object") { + assertTrue(SystemAdmin.make(true).toOption.get.value == true) + } + ) +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index ebfa5d2c2b..9fa5581d0c 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -103,6 +103,9 @@ object Dependencies { val scalaTest = "org.scalatest" %% "scalatest" % "3.2.2" // Scala 3 compatible val testcontainers = "org.testcontainers" % "testcontainers" % "1.16.3" + // found/added by the plugin but deleted anyway + val commonsLang3 = "org.apache.commons" % "commons-lang3" % "3.12.0" + val webapiLibraryDependencies = Seq( akkaActor, akkaHttp, @@ -114,6 +117,7 @@ object Dependencies { akkaStreamTestkit % Test, akkaTestkit % Test, commonsValidator, + commonsLang3, diff, ehcache, gatlingHighcharts % Test, @@ -170,4 +174,13 @@ object Dependencies { val schemaRepoLibraryDependencies = Seq() val schemaRepoEventStoreServiceLibraryDependencies = Seq() val schemaRepoSearchServiceLibraryDependencies = Seq() + + val valueObjectsLibraryDependencies = Seq( + commonsLang3, + commonsValidator, + gwtServlet, + zioPrelude, + zioTest % Test, + zioTestSbt % Test + ) } diff --git a/webapi/src/main/scala/org/knora/webapi/http/handler/KnoraExceptionHandler.scala b/webapi/src/main/scala/org/knora/webapi/http/handler/KnoraExceptionHandler.scala index 7cba7abfc0..5162f57a1b 100644 --- a/webapi/src/main/scala/org/knora/webapi/http/handler/KnoraExceptionHandler.scala +++ b/webapi/src/main/scala/org/knora/webapi/http/handler/KnoraExceptionHandler.scala @@ -23,6 +23,7 @@ import spray.json.JsNumber import spray.json.JsObject import spray.json.JsString import spray.json.JsValue +import dsp.valueobjects.V2 /** * The Knora exception handler is used by akka-http to convert any exceptions thrown during route processing @@ -48,8 +49,6 @@ object KnoraExceptionHandler extends LazyLogging { complete(exceptionToJsonHttpResponseV1(rre, settingsImpl)) } else if (url.startsWith("/v2")) { complete(exceptionToJsonHttpResponseV2(rre, settingsImpl)) - } else if (url.startsWith("/admin")) { - complete(exceptionToJsonHttpResponseADM(rre, settingsImpl)) } else { complete(exceptionToJsonHttpResponseADM(rre, settingsImpl)) } @@ -67,13 +66,24 @@ object KnoraExceptionHandler extends LazyLogging { complete(exceptionToJsonHttpResponseV1(ise, settingsImpl)) } else if (url.startsWith("/v2")) { complete(exceptionToJsonHttpResponseV2(ise, settingsImpl)) - } else if (url.startsWith("/admin")) { - complete(exceptionToJsonHttpResponseADM(ise, settingsImpl)) } else { complete(exceptionToJsonHttpResponseADM(ise, settingsImpl)) } } + case bre: V2.BadRequestException => + extractRequest { request => + val url = request.uri.path.toString + + if (url.startsWith("/v1")) { + complete(exceptionToJsonHttpResponseV1(bre, settingsImpl)) + } else if (url.startsWith("/v2")) { + complete(exceptionToJsonHttpResponseV2(bre, settingsImpl)) + } else { + complete(exceptionToJsonHttpResponseADM(bre, settingsImpl)) + } + } + case other => extractRequest { request => val uri = request.uri @@ -86,8 +96,6 @@ object KnoraExceptionHandler extends LazyLogging { complete(exceptionToJsonHttpResponseV1(other, settingsImpl)) } else if (url.startsWith("/v2")) { complete(exceptionToJsonHttpResponseV2(other, settingsImpl)) - } else if (url.startsWith("/admin")) { - complete(exceptionToJsonHttpResponseADM(other, settingsImpl)) } else { complete(exceptionToJsonHttpResponseADM(other, settingsImpl)) } diff --git a/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesV2.scala b/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesV2.scala index 6320cd9f14..fc363aa3c5 100644 --- a/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/http/status/ApiStatusCodesV2.scala @@ -8,6 +8,7 @@ package org.knora.webapi.http.status import akka.http.scaladsl.model.StatusCode import akka.http.scaladsl.model.StatusCodes import org.knora.webapi.exceptions._ +import dsp.valueobjects.V2 /** * The possible values for the HTTP status code that is returned as part of each Knora API v2 response. @@ -30,6 +31,7 @@ object ApiStatusCodesV2 { case OntologyConstraintException(_) => StatusCodes.BadRequest case EditConflictException(_) => StatusCodes.Conflict case RequestRejectedException(_) => StatusCodes.BadRequest + case V2.BadRequestException(_, _) => StatusCodes.BadRequest // Subclasses of InternalServerException (which must be last in this group) case UpdateNotPerformedException(_) => StatusCodes.Conflict diff --git a/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala b/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala index 64f9c5b88a..1f106a684d 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala @@ -46,13 +46,12 @@ import scala.util.Success import scala.util.Try import scala.util.control.Exception._ import scala.util.matching.Regex +import dsp.valueobjects.IriErrorMessages /** * Provides instances of [[StringFormatter]], as well as string formatting constants. */ object StringFormatter { - val UUID_INVALID_ERROR = "Invalid UUID used to create IRI. Only versions 4 and 5 are supported." - // A non-printing delimiter character, Unicode INFORMATION SEPARATOR ONE, that should never occur in data. val INFORMATION_SEPARATOR_ONE = '\u001F' @@ -2981,7 +2980,7 @@ class StringFormatter private ( * @param s the string (IRI) to be checked. * @return TRUE for correct versions, FALSE for incorrect. */ - def isUUIDVersion4Or5(s: IRI): Boolean = + def isUuidVersion4Or5(s: IRI): Boolean = if (getUUIDVersion(s) == 4 || getUUIDVersion(s) == 5) { true } else { @@ -2994,7 +2993,7 @@ class StringFormatter private ( * @param s the string to check. * @return TRUE if the string is the right length to be a canonical or Base64-encoded UUID. */ - def hasUUIDLength(s: String): Boolean = + def hasUuidLength(s: String): Boolean = s.length == CanonicalUuidLength || s.length == Base64UuidLength /** @@ -3002,8 +3001,8 @@ class StringFormatter private ( * @param iri to be validated */ def validateUUIDOfResourceIRI(iri: SmartIri): Unit = - if (iri.isKnoraResourceIri && hasUUIDLength(iri.toString.split("/").last) && !isUUIDVersion4Or5(iri.toString)) { - throw BadRequestException(UUID_INVALID_ERROR) + if (iri.isKnoraResourceIri && hasUuidLength(iri.toString.split("/").last) && !isUuidVersion4Or5(iri.toString)) { + throw BadRequestException(IriErrorMessages.UuidVersionInvalid) } /** @@ -3011,8 +3010,8 @@ class StringFormatter private ( * @param iri to be validated. */ def validatePermissionIRI(iri: IRI): Unit = - if (isKnoraPermissionIriStr(iri) && !isUUIDVersion4Or5(iri)) { - throw BadRequestException(UUID_INVALID_ERROR) + if (isKnoraPermissionIriStr(iri) && !isUuidVersion4Or5(iri)) { + throw BadRequestException(IriErrorMessages.UuidVersionInvalid) } else { validatePermissionIri(iri, throw BadRequestException(s"Invalid permission IRI ${iri} is given.")) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsErrorMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsErrorMessagesADM.scala deleted file mode 100644 index 17f3aae494..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsErrorMessagesADM.scala +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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 org.knora.webapi.messages.admin.responder.groupsmessages - -object GroupsErrorMessagesADM { - val GROUP_IRI_MISSING_ERROR = "Group IRI cannot be empty." - val GROUP_IRI_INVALID_ERROR = "Group IRI is invalid." - val GROUP_NAME_MISSING_ERROR = "Group name cannot be empty." - val GROUP_NAME_INVALID_ERROR = "Group name is invalid." - val GROUP_DESCRIPTION_MISSING_ERROR = "Group description cannot be empty." - val GROUP_DESCRIPTION_INVALID_ERROR = "Group description is invalid." -} diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsMessagesADM.scala index d496eb504f..f73d785236 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsMessagesADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsMessagesADM.scala @@ -21,6 +21,7 @@ import spray.json.JsonFormat import spray.json.RootJsonFormat import java.util.UUID +import dsp.valueobjects.V2 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // API requests @@ -38,7 +39,7 @@ import java.util.UUID case class CreateGroupApiRequestADM( id: Option[IRI] = None, name: String, - descriptions: Seq[StringLiteralV2], + descriptions: Seq[V2.StringLiteralV2], project: IRI, status: Boolean, selfjoin: Boolean @@ -60,7 +61,7 @@ case class CreateGroupApiRequestADM( */ case class ChangeGroupApiRequestADM( name: Option[String] = None, - descriptions: Option[Seq[StringLiteralV2]] = None, + descriptions: Option[Seq[V2.StringLiteralV2]] = None, status: Option[Boolean] = None, selfjoin: Option[Boolean] = None ) extends GroupsADMJsonProtocol { diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsPayloadsADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsPayloadsADM.scala index 7373e49b23..bee9d4dbf7 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsPayloadsADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/groupsmessages/GroupsPayloadsADM.scala @@ -5,16 +5,17 @@ package org.knora.webapi.messages.admin.responder.groupsmessages -import org.knora.webapi.messages.admin.responder.valueObjects._ +import dsp.valueobjects.Iri._ +import dsp.valueobjects.Group._ /** * Group create payload */ final case class GroupCreatePayloadADM( - id: Option[GroupIRI] = None, + id: Option[GroupIri] = None, name: GroupName, descriptions: GroupDescriptions, - project: ProjectIRI, + project: ProjectIri, status: GroupStatus, selfjoin: GroupSelfJoin ) diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsErrorMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsErrorMessagesADM.scala deleted file mode 100644 index 891a187467..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsErrorMessagesADM.scala +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 org.knora.webapi.messages.admin.responder.listsmessages - -object ListsErrorMessagesADM { - val LIST_IRI_MISSING_ERROR = "List IRI cannot be empty." - val LIST_IRI_INVALID_ERROR = "List IRI cannot be empty." - val LIST_NODE_IRI_MISSING_ERROR = "List node IRI cannot be empty." - val LIST_NODE_IRI_INVALID_ERROR = "List node IRI is invalid." - val LIST_NAME_MISSING_ERROR = "List name cannot be empty." - val LIST_NAME_INVALID_ERROR = "List name is invalid." - val LABEL_MISSING_ERROR = "At least one label needs to be supplied." - val LABEL_INVALID_ERROR = "Invalid label." - val COMMENT_MISSING_ERROR = "At least one comment needs to be supplied." - val COMMENT_INVALID_ERROR = "Invalid comment." - val LIST_CREATE_PERMISSION_ERROR = "A list can only be created by the project or system administrator." - val LIST_NODE_CREATE_PERMISSION_ERROR = "A list node can only be created by the project or system administrator." - val LIST_CHANGE_PERMISSION_ERROR = "A list can only be changed by the project or system administrator." - val UPDATE_REQUEST_EMPTY_LABEL_ERROR = "List labels cannot be empty." - val INVALID_POSITION = "Invalid position value is given, position should be either a positive value or -1." -} diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADM.scala index 9526a8e544..f1327ed8f1 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADM.scala @@ -14,7 +14,6 @@ import org.knora.webapi.messages.admin.responder.KnoraRequestADM import org.knora.webapi.messages.admin.responder.KnoraResponseADM import org.knora.webapi.messages.admin.responder.listsmessages.ListNodeCreatePayloadADM.ListChildNodeCreatePayloadADM import org.knora.webapi.messages.admin.responder.listsmessages.ListNodeCreatePayloadADM.ListRootNodeCreatePayloadADM -import org.knora.webapi.messages.admin.responder.listsmessages.ListsErrorMessagesADM._ import org.knora.webapi.messages.admin.responder.usersmessages._ import org.knora.webapi.messages.store.triplestoremessages.StringLiteralSequenceV2 import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 @@ -22,7 +21,8 @@ import org.knora.webapi.messages.store.triplestoremessages.TriplestoreJsonProtoc import spray.json._ import java.util.UUID -import org.knora.webapi.messages.admin.responder.valueObjects.Comments +import dsp.valueobjects.ListErrorMessages +import dsp.valueobjects.V2 /////////////// API requests @@ -40,8 +40,8 @@ case class ListRootNodeCreateApiRequestADM( id: Option[IRI] = None, projectIri: IRI, name: Option[String] = None, - labels: Seq[StringLiteralV2], - comments: Seq[StringLiteralV2] + labels: Seq[V2.StringLiteralV2], + comments: Seq[V2.StringLiteralV2] ) extends ListADMJsonProtocol { def toJsValue: JsValue = createListRootNodeApiRequestADMFormat.write(this) } @@ -67,8 +67,8 @@ case class ListChildNodeCreateApiRequestADM( projectIri: IRI, name: Option[String] = None, position: Option[Int] = None, - labels: Seq[StringLiteralV2], - comments: Option[Seq[StringLiteralV2]] + labels: Seq[V2.StringLiteralV2], + comments: Option[Seq[V2.StringLiteralV2]] ) extends ListADMJsonProtocol { def toJsValue: JsValue = createListChildNodeApiRequestADMFormat.write(this) } @@ -90,8 +90,8 @@ case class ListNodeChangeApiRequestADM( hasRootNode: Option[IRI] = None, position: Option[Int] = None, name: Option[String] = None, - labels: Option[Seq[StringLiteralV2]] = None, - comments: Option[Seq[StringLiteralV2]] = None + labels: Option[Seq[V2.StringLiteralV2]] = None, + comments: Option[Seq[V2.StringLiteralV2]] = None ) extends ListADMJsonProtocol { def toJsValue: JsValue = changeListInfoApiRequestADMFormat.write(this) } @@ -111,7 +111,7 @@ case class ChangeNodeNameApiRequestADM(name: String) extends ListADMJsonProtocol * * @param labels the new labels of the node */ -case class ChangeNodeLabelsApiRequestADM(labels: Seq[StringLiteralV2]) extends ListADMJsonProtocol { +case class ChangeNodeLabelsApiRequestADM(labels: Seq[V2.StringLiteralV2]) extends ListADMJsonProtocol { def toJsValue: JsValue = changeNodeLabelsApiRequestADMFormat.write(this) } @@ -121,7 +121,7 @@ case class ChangeNodeLabelsApiRequestADM(labels: Seq[StringLiteralV2]) extends L * * @param comments the new comments of the node. */ -case class ChangeNodeCommentsApiRequestADM(comments: Seq[StringLiteralV2]) extends ListADMJsonProtocol { +case class ChangeNodeCommentsApiRequestADM(comments: Seq[V2.StringLiteralV2]) extends ListADMJsonProtocol { def toJsValue: JsValue = changeNodeCommentsApiRequestADMFormat.write(this) } @@ -143,7 +143,7 @@ case class ChangeNodePositionApiRequestADM(position: Int, parentIri: IRI) extend } if (position < -1) { - throw BadRequestException(INVALID_POSITION) + throw BadRequestException(ListErrorMessages.InvalidPosition) } def toJsValue: JsValue = changeNodePositionApiRequestADMFormat.write(this) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsPayloadsADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsPayloadsADM.scala index d0a02ec2c4..4ac11e8c00 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsPayloadsADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsPayloadsADM.scala @@ -5,7 +5,8 @@ package org.knora.webapi.messages.admin.responder.listsmessages -import org.knora.webapi.messages.admin.responder.valueObjects._ +import dsp.valueobjects.Iri._ +import dsp.valueobjects.List._ /** * List root node and child node creation payloads @@ -16,16 +17,16 @@ sealed trait ListNodeCreatePayloadADM // 2. Rethink other field names if they are descriptive enough, e.g. id should be renamed to customIri or something similar object ListNodeCreatePayloadADM { final case class ListRootNodeCreatePayloadADM( - id: Option[ListIRI] = None, - projectIri: ProjectIRI, + id: Option[ListIri] = None, + projectIri: ProjectIri, name: Option[ListName] = None, labels: Labels, comments: Comments ) extends ListNodeCreatePayloadADM final case class ListChildNodeCreatePayloadADM( - id: Option[ListIRI] = None, - parentNodeIri: ListIRI, - projectIri: ProjectIRI, + id: Option[ListIri] = None, + parentNodeIri: ListIri, + projectIri: ProjectIri, name: Option[ListName] = None, position: Option[Position] = None, labels: Labels, @@ -38,9 +39,9 @@ object ListNodeCreatePayloadADM { */ final case class ListNodeChangePayloadADM( // TODO-mpro: listIri can be probably removed here or maybe from the route?? - listIri: ListIRI, - projectIri: ProjectIRI, - hasRootNode: Option[ListIRI] = None, + listIri: ListIri, + projectIri: ProjectIri, + hasRootNode: Option[ListIri] = None, position: Option[Position] = None, name: Option[ListName] = None, labels: Option[Labels] = None, diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsErrorMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsErrorMessagesADM.scala deleted file mode 100644 index 165b4a3140..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsErrorMessagesADM.scala +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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 org.knora.webapi.messages.admin.responder.projectsmessages - -object ProjectsErrorMessagesADM { - val PROJECT_IRI_MISSING_ERROR = "Project IRI cannot be empty." - val PROJECT_IRI_INVALID_ERROR = "Project IRI is invalid." - val SHORTCODE_MISSING_ERROR = "Shortcode cannot be empty." - val SHORTCODE_INVALID_ERROR = "Shortcode is invalid." - val SHORTNAME_MISSING_ERROR = "Shortname cannot be empty." - val SHORTNAME_INVALID_ERROR = "Shortname is invalid." - val LONGNAME_MISSING_ERROR = "Longname cannot be empty." - val LONGNAME_INVALID_ERROR = "Longname is invalid." - val PROJECT_DESCRIPTION_MISSING_ERROR = "Description cannot be empty." - val PROJECT_DESCRIPTION_INVALID_ERROR = "Description is invalid." - val KEYWORDS_MISSING_ERROR = "Keywords cannot be empty." - val KEYWORDS_INVALID_ERROR = "Keywords are invalid." - val LOGO_MISSING_ERROR = "Logo cannot be empty." - val LOGO_INVALID_ERROR = "Logo is invalid." -} diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala index 27cbc0276f..94fc143b07 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala @@ -28,6 +28,7 @@ import spray.json.RootJsonFormat import java.nio.file.Path import java.util.UUID +import dsp.valueobjects.V2 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // API requests @@ -50,7 +51,7 @@ case class CreateProjectApiRequestADM( shortname: String, shortcode: String, longname: Option[String], - description: Seq[StringLiteralV2], + description: Seq[V2.StringLiteralV2], keywords: Seq[String], logo: Option[String], status: Boolean, diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsPayloadsADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsPayloadsADM.scala index 5f240bd349..2e9a6bc6e6 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsPayloadsADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsPayloadsADM.scala @@ -5,13 +5,14 @@ package org.knora.webapi.messages.admin.responder.projectsmessages -import org.knora.webapi.messages.admin.responder.valueObjects._ +import dsp.valueobjects.Iri.ProjectIri +import dsp.valueobjects.Project._ /** * Project creation payload */ final case class ProjectCreatePayloadADM( - id: Option[ProjectIRI] = None, + id: Option[ProjectIri] = None, shortname: Shortname, shortcode: Shortcode, longname: Option[Longname] = None, diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersErrorMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersErrorMessagesADM.scala deleted file mode 100644 index 7c2d1b275c..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersErrorMessagesADM.scala +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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 org.knora.webapi.messages.admin.responder.usersmessages - -object UsersErrorMessagesADM { - val USER_IRI_MISSING_ERROR = "User IRI cannot be empty." - val USER_IRI_INVALID_ERROR = "User IRI is invalid." - val USERNAME_MISSING_ERROR = "Username cannot be empty." - val USERNAME_INVALID_ERROR = "Username is invalid." - val EMAIL_MISSING_ERROR = "Email cannot be empty." - val EMAIL_INVALID_ERROR = "Email is invalid." - val PASSWORD_MISSING_ERROR = "Password cannot be empty." - val PASSWORD_INVALID_ERROR = "Password is invalid." - val GIVEN_NAME_MISSING_ERROR = "GivenName cannot be empty." - val GIVEN_NAME_INVALID_ERROR = "GivenName is invalid." - val FAMILY_NAME_MISSING_ERROR = "FamilyName cannot be empty." - val FAMILY_NAME_INVALID_ERROR = "FamilyName is invalid." - val LANGUAGE_CODE_MISSING_ERROR = "LanguageCode cannot be empty." - val LANGUAGE_CODE_INVALID_ERROR = "LanguageCode is invalid." -} diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADM.scala index 56fa89afe8..7d9b2502d1 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersMessagesADM.scala @@ -20,12 +20,12 @@ import org.knora.webapi.messages.admin.responder.permissionsmessages.Permissions import org.knora.webapi.messages.admin.responder.permissionsmessages.PermissionsDataADM import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectADM import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectsADMJsonProtocol -import org.knora.webapi.messages.admin.responder.valueObjects._ import org.knora.webapi.messages.v1.responder.projectmessages.ProjectInfoV1 import org.knora.webapi.messages.v1.responder.usermessages._ import spray.json._ import java.util.UUID +import dsp.valueobjects.User._ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // API requests diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersPayloadsADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersPayloadsADM.scala index abe107ad23..3ebe3cc461 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersPayloadsADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UsersPayloadsADM.scala @@ -5,13 +5,14 @@ package org.knora.webapi.messages.admin.responder.usersmessages -import org.knora.webapi.messages.admin.responder.valueObjects._ +import dsp.valueobjects.Iri.UserIri +import dsp.valueobjects.User._ /** * User creation payload */ final case class UserCreatePayloadADM( - id: Option[UserIRI] = None, + id: Option[UserIri] = None, username: Username, email: Email, givenName: GivenName, diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/GroupsValueObjectsADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/GroupsValueObjectsADM.scala deleted file mode 100644 index 7401a2bff7..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/GroupsValueObjectsADM.scala +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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 org.knora.webapi.messages.admin.responder.valueObjects - -import org.knora.webapi.exceptions.BadRequestException -import org.knora.webapi.messages.StringFormatter -import org.knora.webapi.messages.StringFormatter.UUID_INVALID_ERROR -import org.knora.webapi.messages.admin.responder.groupsmessages.GroupsErrorMessagesADM._ -import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 -import zio.prelude.Validation - -/** - * GroupIRI value object. - */ -sealed abstract case class GroupIRI private (value: String) -object GroupIRI { self => - private val sf: StringFormatter = StringFormatter.getGeneralInstance - - def make(value: String): Validation[Throwable, GroupIRI] = - if (value.isEmpty) { - Validation.fail(BadRequestException(GROUP_IRI_MISSING_ERROR)) - } else { - val isUUID: Boolean = sf.hasUUIDLength(value.split("/").last) - - if (!sf.isKnoraGroupIriStr(value)) { - Validation.fail(BadRequestException(GROUP_IRI_INVALID_ERROR)) - } else if (isUUID && !sf.isUUIDVersion4Or5(value)) { - Validation.fail(BadRequestException(UUID_INVALID_ERROR)) - } else { - val validatedValue = Validation( - sf.validateAndEscapeIri(value, throw BadRequestException(GROUP_IRI_INVALID_ERROR)) - ) - - 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) - } -} - -/** - * GroupName value object. - */ -sealed abstract case class GroupName private (value: String) -object GroupName { self => - private val sf: StringFormatter = StringFormatter.getGeneralInstance - - def make(value: String): Validation[Throwable, GroupName] = - if (value.isEmpty) { - Validation.fail(BadRequestException(GROUP_NAME_MISSING_ERROR)) - } else { - val validatedValue = Validation( - sf.toSparqlEncodedString(value, throw BadRequestException(GROUP_NAME_INVALID_ERROR)) - ) - - 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[StringLiteralV2]) -object GroupDescriptions { self => - private val sf: StringFormatter = StringFormatter.getGeneralInstance - - def make(value: Seq[StringLiteralV2]): Validation[Throwable, GroupDescriptions] = - if (value.isEmpty) { - Validation.fail(BadRequestException(GROUP_DESCRIPTION_MISSING_ERROR)) - } else { - val validatedDescriptions = Validation(value.map { description => - val validatedDescription = - sf.toSparqlEncodedString(description.value, throw BadRequestException(GROUP_DESCRIPTION_INVALID_ERROR)) - StringLiteralV2(value = validatedDescription, language = description.language) - }) - validatedDescriptions.map(new GroupDescriptions(_) {}) - } - - def make(value: Option[Seq[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) - } -} diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/ListsValueObjectsADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/ListsValueObjectsADM.scala deleted file mode 100644 index 5707bf55cf..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/ListsValueObjectsADM.scala +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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 org.knora.webapi.messages.admin.responder.valueObjects - -import org.knora.webapi.exceptions.BadRequestException -import org.knora.webapi.messages.StringFormatter -import org.knora.webapi.messages.StringFormatter.UUID_INVALID_ERROR -import org.knora.webapi.messages.admin.responder.listsmessages.ListsErrorMessagesADM._ -import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 -import zio.prelude.Validation - -/** - * List ListIRI value object. - */ -sealed abstract case class ListIRI private (value: String) -object ListIRI { self => - val sf: StringFormatter = StringFormatter.getGeneralInstance - - def make(value: String): Validation[Throwable, ListIRI] = - if (value.isEmpty) { - Validation.fail(BadRequestException(LIST_NODE_IRI_MISSING_ERROR)) - } else { - val isUUID: Boolean = sf.hasUUIDLength(value.split("/").last) - - if (!sf.isKnoraListIriStr(value)) { - Validation.fail(BadRequestException(LIST_NODE_IRI_INVALID_ERROR)) - } else if (isUUID && !sf.isUUIDVersion4Or5(value)) { - Validation.fail(BadRequestException(UUID_INVALID_ERROR)) - } else { - val validatedValue = Validation( - sf.validateAndEscapeIri(value, throw BadRequestException(LIST_NODE_IRI_INVALID_ERROR)) - ) - - 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) - } -} - -/** - * List ListName value object. - */ -sealed abstract case class ListName private (value: String) -object ListName { self => - val sf: StringFormatter = StringFormatter.getGeneralInstance - - def make(value: String): Validation[Throwable, ListName] = - if (value.isEmpty) { - Validation.fail(BadRequestException(LIST_NAME_MISSING_ERROR)) - } else { - val validatedValue = Validation( - sf.toSparqlEncodedString(value, throw BadRequestException(LIST_NAME_INVALID_ERROR)) - ) - - validatedValue.map(new ListName(_) {}) - } - - def make(value: Option[String]): Validation[Throwable, Option[ListName]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} - -/** - * List Position value object. - */ -sealed abstract case class Position private (value: Int) -object Position { self => - def make(value: Int): Validation[Throwable, Position] = - if (value < -1) { - Validation.fail(BadRequestException(INVALID_POSITION)) - } else { - Validation.succeed(new Position(value) {}) - } - - def make(value: Option[Int]): Validation[Throwable, Option[Position]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} - -/** - * List Labels value object. - */ -sealed abstract case class Labels private (value: Seq[StringLiteralV2]) -object Labels { self => - val sf: StringFormatter = StringFormatter.getGeneralInstance - - def make(value: Seq[StringLiteralV2]): Validation[Throwable, Labels] = - if (value.isEmpty) { - Validation.fail(BadRequestException(LABEL_MISSING_ERROR)) - } else { - val validatedLabels = Validation(value.map { label => - val validatedLabel = - sf.toSparqlEncodedString(label.value, throw BadRequestException(LABEL_INVALID_ERROR)) - StringLiteralV2(value = validatedLabel, language = label.language) - }) - - validatedLabels.map(new Labels(_) {}) - } - - def make(value: Option[Seq[StringLiteralV2]]): Validation[Throwable, Option[Labels]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} - -/** - * List Comments value object. - */ -sealed abstract case class Comments private (value: Seq[StringLiteralV2]) -object Comments { self => - val sf: StringFormatter = StringFormatter.getGeneralInstance - - def make(value: Seq[StringLiteralV2]): Validation[Throwable, Comments] = - if (value.isEmpty) { - Validation.fail(BadRequestException(COMMENT_MISSING_ERROR)) - } else { - val validatedComments = Validation(value.map { comment => - val validatedComment = - sf.toSparqlEncodedString(comment.value, throw BadRequestException(COMMENT_INVALID_ERROR)) - StringLiteralV2(value = validatedComment, language = comment.language) - }) - - validatedComments.map(new Comments(_) {}) - } - - def make(value: Option[Seq[StringLiteralV2]]): Validation[Throwable, Option[Comments]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/ProjectsValueObjectsADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/ProjectsValueObjectsADM.scala deleted file mode 100644 index b08c22e1e9..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/ProjectsValueObjectsADM.scala +++ /dev/null @@ -1,199 +0,0 @@ -/* - * 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 org.knora.webapi.messages.admin.responder.valueObjects - -import org.knora.webapi.exceptions.BadRequestException -import org.knora.webapi.messages.StringFormatter -import org.knora.webapi.messages.StringFormatter.UUID_INVALID_ERROR -import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectsErrorMessagesADM._ -import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 -import zio.prelude.Validation - -/** - * ProjectIRI value object. - */ -sealed abstract case class ProjectIRI private (value: String) -object ProjectIRI { self => - private val sf: StringFormatter = StringFormatter.getGeneralInstance - - def make(value: String): Validation[Throwable, ProjectIRI] = - if (value.isEmpty) { - Validation.fail(BadRequestException(PROJECT_IRI_MISSING_ERROR)) - } else { - val isUUID: Boolean = sf.hasUUIDLength(value.split("/").last) - - if (!sf.isKnoraProjectIriStr(value)) { - Validation.fail(BadRequestException(PROJECT_IRI_INVALID_ERROR)) - } else if (isUUID && !sf.isUUIDVersion4Or5(value)) { - Validation.fail(BadRequestException(UUID_INVALID_ERROR)) - } else { - val validatedValue = Validation( - sf.validateAndEscapeProjectIri(value, throw BadRequestException(PROJECT_IRI_INVALID_ERROR)) - ) - - 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) - } -} - -/** - * Project Shortcode value object. - */ -sealed abstract case class Shortcode private (value: String) -object Shortcode { self => - val sf: StringFormatter = StringFormatter.getGeneralInstance - - def make(value: String): Validation[Throwable, Shortcode] = - if (value.isEmpty) { - Validation.fail(BadRequestException(SHORTCODE_MISSING_ERROR)) - } else { - val validatedValue: Validation[Throwable, String] = Validation( - sf.validateProjectShortcode(value, throw BadRequestException(SHORTCODE_INVALID_ERROR)) - ) - validatedValue.map(new Shortcode(_) {}) - } - - def make(value: Option[String]): Validation[Throwable, Option[Shortcode]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} - -/** - * Project Shortname value object. - */ -sealed abstract case class Shortname private (value: String) -object Shortname { self => - val sf: StringFormatter = StringFormatter.getGeneralInstance - - def make(value: String): Validation[Throwable, Shortname] = - if (value.isEmpty) { - Validation.fail(BadRequestException(SHORTNAME_MISSING_ERROR)) - } else { - val validatedValue = Validation( - sf.validateAndEscapeProjectShortname(value, throw BadRequestException(SHORTNAME_INVALID_ERROR)) - ) - validatedValue.map(new Shortname(_) {}) - } - - def make(value: Option[String]): Validation[Throwable, Option[Shortname]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} - -/** - * Project Longname value object. - */ -sealed abstract case class Longname private (value: String) -object Longname { self => - def make(value: String): Validation[Throwable, Longname] = - if (value.isEmpty) { - Validation.fail(BadRequestException(LONGNAME_MISSING_ERROR)) - } else { - 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(_)) - } -} - -/** - * ProjectDescription value object. - */ -sealed abstract case class ProjectDescription private (value: Seq[StringLiteralV2]) -object ProjectDescription { self => - def make(value: Seq[StringLiteralV2]): Validation[Throwable, ProjectDescription] = - if (value.isEmpty) { - Validation.fail(BadRequestException(PROJECT_DESCRIPTION_MISSING_ERROR)) - } else { - Validation.succeed(new ProjectDescription(value) {}) - } - - def make(value: Option[Seq[StringLiteralV2]]): Validation[Throwable, Option[ProjectDescription]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} - -/** - * Project Keywords value object. - */ -sealed abstract case class Keywords private (value: Seq[String]) -object Keywords { self => - def make(value: Seq[String]): Validation[Throwable, Keywords] = - if (value.isEmpty) { - Validation.fail(BadRequestException(KEYWORDS_MISSING_ERROR)) - } else { - Validation.succeed(new Keywords(value) {}) - } - - def make(value: Option[Seq[String]]): Validation[Throwable, Option[Keywords]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} - -/** - * Project Logo value object. - */ -sealed abstract case class Logo private (value: String) -object Logo { self => - def make(value: String): Validation[Throwable, Logo] = - if (value.isEmpty) { - Validation.fail(BadRequestException(LOGO_MISSING_ERROR)) - } else { - Validation.succeed(new Logo(value) {}) - } - def make(value: Option[String]): Validation[Throwable, Option[Logo]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} - -/** - * ProjectSelfjoin value object. - */ -sealed abstract case class ProjectSelfJoin private (value: Boolean) -object ProjectSelfJoin { self => - def make(value: Boolean): Validation[Throwable, ProjectSelfJoin] = - Validation.succeed(new ProjectSelfJoin(value) {}) - - def make(value: Option[Boolean]): Validation[Throwable, Option[ProjectSelfJoin]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} - -/** - * ProjectStatus value object. - */ -sealed abstract case class ProjectStatus private (value: Boolean) -object ProjectStatus { self => - def make(value: Boolean): Validation[Throwable, ProjectStatus] = - Validation.succeed(new ProjectStatus(value) {}) - - def make(value: Option[Boolean]): Validation[Throwable, Option[ProjectStatus]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/UsersValueObjectsADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/UsersValueObjectsADM.scala deleted file mode 100644 index 340f198ad1..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/valueObjects/UsersValueObjectsADM.scala +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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 org.knora.webapi.messages.admin.responder.valueObjects - -import org.knora.webapi.LanguageCodes -import org.knora.webapi.exceptions.BadRequestException -import org.knora.webapi.messages.StringFormatter -import org.knora.webapi.messages.StringFormatter.UUID_INVALID_ERROR -import org.knora.webapi.messages.admin.responder.usersmessages.UsersErrorMessagesADM._ -import zio.prelude.Validation - -import scala.util.matching.Regex - -/** - * UserIRI value object. - */ -sealed abstract case class UserIRI private (value: String) -object UserIRI { self => - private val sf: StringFormatter = StringFormatter.getGeneralInstance - - def make(value: String): Validation[Throwable, UserIRI] = - if (value.isEmpty) { - Validation.fail(BadRequestException(USER_IRI_MISSING_ERROR)) - } else { - val isUUID: Boolean = sf.hasUUIDLength(value.split("/").last) - - if (!sf.isKnoraUserIriStr(value)) { - Validation.fail(BadRequestException(USER_IRI_INVALID_ERROR)) - } else if (isUUID && !sf.isUUIDVersion4Or5(value)) { - Validation.fail(BadRequestException(UUID_INVALID_ERROR)) - } else { - val validatedValue = Validation( - sf.validateAndEscapeUserIri(value, throw BadRequestException(USER_IRI_INVALID_ERROR)) - ) - - 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) - } -} - -/** - * Username value object. - */ -sealed abstract case class Username private (value: String) -object Username { self => - - /** - * A regex that matches a valid username - * - 4 - 50 characters long - * - Only contains alphanumeric characters, underscore and dot. - * - Underscore and dot can't be at the end or start of a username - * - Underscore or dot can't be used multiple times in a row - */ - private val UsernameRegex: Regex = - """^(?=.{4,50}$)(?![_.])(?!.*[_.]{2})[a-zA-Z0-9._]+(? Validation.succeed(new Username(value) {}) - case None => Validation.fail(BadRequestException(USERNAME_INVALID_ERROR)) - } - } - - def make(value: Option[String]): Validation[Throwable, Option[Username]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} - -/** - * Email value object. - */ -sealed abstract case class Email private (value: String) -object Email { self => - private val EmailRegex: Regex = """^.+@.+$""".r - - def make(value: String): Validation[Throwable, Email] = - if (value.isEmpty) { - Validation.fail(BadRequestException(EMAIL_MISSING_ERROR)) - } else { - EmailRegex.findFirstIn(value) match { - case Some(value) => Validation.succeed(new Email(value) {}) - case None => Validation.fail(BadRequestException(EMAIL_INVALID_ERROR)) - } - } - - def make(value: Option[String]): Validation[Throwable, Option[Email]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} - -/** - * GivenName value object. - */ -sealed abstract case class GivenName private (value: String) -object GivenName { self => - def make(value: String): Validation[Throwable, GivenName] = - if (value.isEmpty) { - Validation.fail(BadRequestException(GIVEN_NAME_MISSING_ERROR)) - } else { - Validation.succeed(new GivenName(value) {}) - } - - def make(value: Option[String]): Validation[Throwable, Option[GivenName]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} - -/** - * FamilyName value object. - */ -sealed abstract case class FamilyName private (value: String) -object FamilyName { self => - def make(value: String): Validation[Throwable, FamilyName] = - if (value.isEmpty) { - Validation.fail(BadRequestException(FAMILY_NAME_MISSING_ERROR)) - } else { - Validation.succeed(new FamilyName(value) {}) - } - - def make(value: Option[String]): Validation[Throwable, Option[FamilyName]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} - -/** - * Password value object. - */ -sealed abstract case class Password private (value: String) -object Password { self => - private val PasswordRegex: Regex = """^[\s\S]*$""".r - - def make(value: String): Validation[Throwable, Password] = - if (value.isEmpty) { - Validation.fail(BadRequestException(PASSWORD_MISSING_ERROR)) - } else { - PasswordRegex.findFirstIn(value) match { - case Some(value) => Validation.succeed(new Password(value) {}) - case None => Validation.fail(BadRequestException(PASSWORD_INVALID_ERROR)) - } - } - - def make(value: Option[String]): Validation[Throwable, Option[Password]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} - -/** - * UserStatus value object. - */ -sealed abstract case class UserStatus private (value: Boolean) -object UserStatus { - def make(value: Boolean): Validation[Throwable, UserStatus] = - Validation.succeed(new UserStatus(value) {}) -} - -/** - * LanguageCode value object. - */ -sealed abstract case class LanguageCode private (value: String) -object LanguageCode { self => - def make(value: String): Validation[Throwable, LanguageCode] = - if (value.isEmpty) { - Validation.fail(BadRequestException(LANGUAGE_CODE_MISSING_ERROR)) - } else if (!LanguageCodes.SupportedLanguageCodes.contains(value)) { - Validation.fail(BadRequestException(LANGUAGE_CODE_INVALID_ERROR)) - } else { - Validation.succeed(new LanguageCode(value) {}) - } - - def make(value: Option[String]): Validation[Throwable, Option[LanguageCode]] = - value match { - case Some(v) => self.make(v).map(Some(_)) - case None => Validation.succeed(None) - } -} - -/** - * SystemAdmin value object. - */ -sealed abstract case class SystemAdmin private (value: Boolean) -object SystemAdmin { - def make(value: Boolean): Validation[Throwable, SystemAdmin] = - Validation.succeed(new SystemAdmin(value) {}) -} diff --git a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala index 540b900dd6..7e9ee20526 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/store/triplestoremessages/TriplestoreMessages.scala @@ -27,6 +27,7 @@ import scala.collection.mutable import scala.util.Failure import scala.util.Success import scala.util.Try +import dsp.valueobjects.V2 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Messages @@ -802,6 +803,62 @@ trait TriplestoreJsonProtocol extends SprayJsonSupport with DefaultJsonProtocol } } + // TODO-mpro: below object needs to be here because of moving value object to separate project which are also partially used in V2. + // Once dsp.valueobjects.V2.StringLiteralV2 is replaced by LangString value object, it can be removed. + // By then it is quick fix solution. + implicit object V2LiteralV2Format extends JsonFormat[V2.StringLiteralV2] { + + /** + * Converts a [[StringLiteralV2]] to a [[JsValue]]. + * + * @param string a [[StringLiteralV2]]. + * @return a [[JsValue]]. + */ + def write(string: V2.StringLiteralV2): JsValue = + if (string.language.isDefined) { + // have language tag + JsObject( + Map( + "value" -> string.value.toJson, + "language" -> string.language.toJson + ) + ) + } else { + // no language tag + JsObject( + Map( + "value" -> string.value.toJson + ) + ) + } + + /** + * Converts a [[JsValue]] to a [[StringLiteralV2]]. + * + * @param json a [[JsValue]]. + * @return a [[StringLiteralV2]]. + */ + def read(json: JsValue): V2.StringLiteralV2 = json match { + case stringWithLang: JsObject => + stringWithLang.getFields("value", "language") match { + case Seq(JsString(value), JsString(language)) => + V2.StringLiteralV2( + value = value, + language = Some(language) + ) + case Seq(JsString(value)) => + V2.StringLiteralV2( + value = value, + language = None + ) + case _ => + throw DeserializationException("JSON object with 'value', or 'value' and 'language' fields expected.") + } + case JsString(value) => V2.StringLiteralV2(value, None) + case _ => throw DeserializationException("JSON object with 'value', or 'value' and 'language' expected. ") + } + } + implicit val rdfDataObjectFormat: RootJsonFormat[RdfDataObject] = jsonFormat2(RdfDataObject) implicit val resetTriplestoreContentFormat: RootJsonFormat[ResetRepositoryContent] = jsonFormat2( ResetRepositoryContent diff --git a/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/XMLToStandoffUtil.scala b/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/XMLToStandoffUtil.scala index 8b75ba65ec..ff6d79f5ce 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/XMLToStandoffUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/util/standoff/XMLToStandoffUtil.scala @@ -746,7 +746,7 @@ class XMLToStandoffUtil( case Some(existingUuid) => existingUuid case None => // Otherwise, try to parse the ID as a UUID. - if (stringFormatter.hasUUIDLength(id)) { + if (stringFormatter.hasUuidLength(id)) { stringFormatter.decodeUuid(id) } else { // If the ID doesn't seem to be a UUID, replace it with a random UUID. TODO: this should throw an exception instead. diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala index d82bfb67f9..ebc1fda650 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala @@ -20,7 +20,6 @@ import org.knora.webapi.messages.IriConversions._ import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.SmartIri import org.knora.webapi.messages.StringFormatter -import org.knora.webapi.messages.StringFormatter.UUID_INVALID_ERROR import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectADM import org.knora.webapi.messages.admin.responder.usersmessages.UserADM import org.knora.webapi.messages.store.sipimessages.GetFileMetadataRequest @@ -41,6 +40,7 @@ import java.time.Instant import java.util.UUID import scala.concurrent.ExecutionContext import scala.concurrent.Future +import dsp.valueobjects.IriErrorMessages /** * A tagging trait for requests handled by [[org.knora.webapi.responders.v2.ValuesResponderV2]]. @@ -528,10 +528,10 @@ object DeleteValueRequestV2 extends KnoraJsonLDRequestReaderV2[DeleteValueReques } if ( - stringFormatter.hasUUIDLength(valueIri.toString.split("/").last) - && !stringFormatter.isUUIDVersion4Or5(valueIri.toString) + stringFormatter.hasUuidLength(valueIri.toString.split("/").last) + && !stringFormatter.isUuidVersion4Or5(valueIri.toString) ) { - throw BadRequestException(UUID_INVALID_ERROR) + throw BadRequestException(IriErrorMessages.UuidVersionInvalid) } val valueTypeIri: SmartIri = jsonLDObject.requireTypeAsKnoraApiV2ComplexTypeIri diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/GroupsResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/GroupsResponderADM.scala index 69acedc2f5..a4ecbd64f1 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/GroupsResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/GroupsResponderADM.scala @@ -18,7 +18,6 @@ import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectADM import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectGetADM import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentifierADM import org.knora.webapi.messages.admin.responder.usersmessages._ -import org.knora.webapi.messages.admin.responder.valueObjects.GroupStatus import org.knora.webapi.messages.store.triplestoremessages._ import org.knora.webapi.messages.util.KnoraSystemInstances import org.knora.webapi.messages.util.ResponderData @@ -29,6 +28,7 @@ import org.knora.webapi.responders.Responder.handleUnexpectedMessage import java.util.UUID import scala.concurrent.Future +import dsp.valueobjects.Group.GroupStatus /** * Returns information about Knora projects. diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/ListsResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/ListsResponderADM.scala index 93af1e6cdd..54e4d9ba48 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/ListsResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/ListsResponderADM.scala @@ -15,15 +15,11 @@ import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.SmartIri import org.knora.webapi.messages.admin.responder.listsmessages.ListNodeCreatePayloadADM.ListChildNodeCreatePayloadADM import org.knora.webapi.messages.admin.responder.listsmessages.ListNodeCreatePayloadADM.ListRootNodeCreatePayloadADM -import org.knora.webapi.messages.admin.responder.listsmessages.ListsErrorMessagesADM._ import org.knora.webapi.messages.admin.responder.listsmessages._ import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectADM import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectGetADM import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentifierADM import org.knora.webapi.messages.admin.responder.usersmessages._ -import org.knora.webapi.messages.admin.responder.valueObjects.ListIRI -import org.knora.webapi.messages.admin.responder.valueObjects.ListName -import org.knora.webapi.messages.admin.responder.valueObjects.ProjectIRI import org.knora.webapi.messages.store.triplestoremessages._ import org.knora.webapi.messages.util.KnoraSystemInstances import org.knora.webapi.messages.util.ResponderData @@ -35,6 +31,9 @@ import org.knora.webapi.responders.Responder.handleUnexpectedMessage import java.util.UUID import scala.annotation.tailrec import scala.concurrent.Future +import dsp.valueobjects.Iri._ +import dsp.valueobjects.ListErrorMessages +import dsp.valueobjects.List.ListName /** * A responder that returns information about hierarchical lists. @@ -852,10 +851,8 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde createNodeRequest: ListNodeCreatePayloadADM, featureFactoryConfig: FeatureFactoryConfig ): Future[IRI] = { - -// println("ZZZZZ-createNode", createNodeRequest) // TODO-mpro: it's quickfix, refactor - val parentNode: Option[ListIRI] = createNodeRequest match { + val parentNode: Option[ListIri] = createNodeRequest match { case ListRootNodeCreatePayloadADM(_, _, _, _, _) => None case ListChildNodeCreatePayloadADM(_, parentNodeIri, _, _, _, _, _) => Some(parentNodeIri) } @@ -1259,13 +1256,13 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde // check if the requesting user is allowed to perform operation _ = if (!requestingUser.permissions.isProjectAdmin(projectIri) && !requestingUser.permissions.isSystemAdmin) { // not project or a system admin - throw ForbiddenException(LIST_CHANGE_PERMISSION_ERROR) + throw ForbiddenException(ListErrorMessages.ListChangePermission) } changeNodeNameSparqlString <- getUpdateNodeInfoSparqlStatement( changeNodeInfoRequest = ListNodeChangePayloadADM( - listIri = ListIRI.make(nodeIri).fold(e => throw e.head, v => v), - projectIri = ProjectIRI.make(projectIri).fold(e => throw e.head, v => v), + listIri = ListIri.make(nodeIri).fold(e => throw e.head, v => v), + projectIri = ProjectIri.make(projectIri).fold(e => throw e.head, v => v), name = Some(changeNodeNameRequest.name) ), featureFactoryConfig = featureFactoryConfig @@ -1338,12 +1335,12 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde // check if the requesting user is allowed to perform operation _ = if (!requestingUser.permissions.isProjectAdmin(projectIri) && !requestingUser.permissions.isSystemAdmin) { // not project or a system admin - throw ForbiddenException(LIST_CHANGE_PERMISSION_ERROR) + throw ForbiddenException(ListErrorMessages.ListChangePermission) } changeNodeLabelsSparqlString <- getUpdateNodeInfoSparqlStatement( changeNodeInfoRequest = ListNodeChangePayloadADM( - listIri = ListIRI.make(nodeIri).fold(e => throw e.head, v => v), - projectIri = ProjectIRI.make(projectIri).fold(e => throw e.head, v => v), + listIri = ListIri.make(nodeIri).fold(e => throw e.head, v => v), + projectIri = ProjectIri.make(projectIri).fold(e => throw e.head, v => v), labels = Some(changeNodeLabelsRequest.labels) ), featureFactoryConfig = featureFactoryConfig @@ -1416,13 +1413,13 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde // check if the requesting user is allowed to perform operation _ = if (!requestingUser.permissions.isProjectAdmin(projectIri) && !requestingUser.permissions.isSystemAdmin) { // not project or a system admin - throw ForbiddenException(LIST_CHANGE_PERMISSION_ERROR) + throw ForbiddenException(ListErrorMessages.ListChangePermission) } changeNodeCommentsSparqlString <- getUpdateNodeInfoSparqlStatement( changeNodeInfoRequest = ListNodeChangePayloadADM( - listIri = ListIRI.make(nodeIri).fold(e => throw e.head, v => v), - projectIri = ProjectIRI.make(projectIri).fold(e => throw e.head, v => v), + listIri = ListIri.make(nodeIri).fold(e => throw e.head, v => v), + projectIri = ProjectIri.make(projectIri).fold(e => throw e.head, v => v), comments = Some(changeNodeCommentsRequest.comments) ), featureFactoryConfig = featureFactoryConfig @@ -1752,7 +1749,7 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde // check if the requesting user is allowed to perform operation _ = if (!requestingUser.permissions.isProjectAdmin(projectIri) && !requestingUser.permissions.isSystemAdmin) { // not project or a system admin - throw ForbiddenException(LIST_CHANGE_PERMISSION_ERROR) + throw ForbiddenException(ListErrorMessages.ListChangePermission) } // get node in its current position @@ -2056,7 +2053,7 @@ class ListsResponderADM(responderData: ResponderData) extends Responder(responde // check if the requesting user is allowed to perform operation _ = if (!requestingUser.permissions.isProjectAdmin(projectIri) && !requestingUser.permissions.isSystemAdmin) { // not project or a system admin - throw ForbiddenException(LIST_CHANGE_PERMISSION_ERROR) + throw ForbiddenException(ListErrorMessages.ListChangePermission) } maybeNode: Option[ListNodeADM] <- listNodeGetADM( diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/UsersResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/UsersResponderADM.scala index 2a3a589c83..3e8ca2ef8f 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/UsersResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/UsersResponderADM.scala @@ -25,11 +25,6 @@ import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectGetADM import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentifierADM import org.knora.webapi.messages.admin.responder.usersmessages.UserChangeRequestADM import org.knora.webapi.messages.admin.responder.usersmessages._ -import org.knora.webapi.messages.admin.responder.valueObjects.Email -import org.knora.webapi.messages.admin.responder.valueObjects.Password -import org.knora.webapi.messages.admin.responder.valueObjects.SystemAdmin -import org.knora.webapi.messages.admin.responder.valueObjects.UserStatus -import org.knora.webapi.messages.admin.responder.valueObjects.Username import org.knora.webapi.messages.store.cacheservicemessages.CacheServiceGetUserADM import org.knora.webapi.messages.store.cacheservicemessages.CacheServicePutUserADM import org.knora.webapi.messages.store.cacheservicemessages.CacheServiceRemoveValues @@ -44,6 +39,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder import java.util.UUID import scala.concurrent.Future +import dsp.valueobjects.User._ /** * Provides information about Knora users to other responders. 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 8bfd28332d..e22dca4981 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 @@ -12,7 +12,6 @@ import io.swagger.annotations._ import org.knora.webapi.exceptions.BadRequestException import org.knora.webapi.feature.FeatureFactoryConfig import org.knora.webapi.messages.admin.responder.groupsmessages._ -import org.knora.webapi.messages.admin.responder.valueObjects._ import org.knora.webapi.routing.Authenticator import org.knora.webapi.routing.KnoraRoute import org.knora.webapi.routing.KnoraRouteData @@ -21,6 +20,8 @@ import zio.prelude.Validation import java.util.UUID import javax.ws.rs.Path +import dsp.valueobjects.Iri._ +import dsp.valueobjects.Group._ object GroupsRouteADM { val GroupsBasePath: PathMatcher[Unit] = PathMatcher("admin" / "groups") @@ -141,10 +142,10 @@ class GroupsRouteADM(routeData: KnoraRouteData) private def createGroup(featureFactoryConfig: FeatureFactoryConfig): Route = path(GroupsBasePath) { post { entity(as[CreateGroupApiRequestADM]) { apiRequest => requestContext => - val id: Validation[Throwable, Option[GroupIRI]] = GroupIRI.make(apiRequest.id) + val id: Validation[Throwable, Option[GroupIri]] = GroupIri.make(apiRequest.id) val name: Validation[Throwable, GroupName] = GroupName.make(apiRequest.name) val descriptions: Validation[Throwable, GroupDescriptions] = GroupDescriptions.make(apiRequest.descriptions) - val project: Validation[Throwable, ProjectIRI] = ProjectIRI.make(apiRequest.project) + val project: Validation[Throwable, ProjectIri] = ProjectIri.make(apiRequest.project) val status: Validation[Throwable, GroupStatus] = GroupStatus.make(apiRequest.status) val selfjoin: Validation[Throwable, GroupSelfJoin] = GroupSelfJoin.make(apiRequest.selfjoin) 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 28076e92b1..e83e2947b9 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 @@ -24,7 +24,6 @@ import org.knora.webapi.annotation.ApiMayChange import org.knora.webapi.exceptions.BadRequestException import org.knora.webapi.feature.FeatureFactoryConfig import org.knora.webapi.messages.admin.responder.projectsmessages._ -import org.knora.webapi.messages.admin.responder.valueObjects._ import org.knora.webapi.routing.Authenticator import org.knora.webapi.routing.KnoraRoute import org.knora.webapi.routing.KnoraRouteData @@ -36,6 +35,8 @@ import java.util.UUID import javax.ws.rs.Path import scala.concurrent.Future import scala.util.Try +import dsp.valueobjects.Iri.ProjectIri +import dsp.valueobjects.Project._ object ProjectsRouteADM { val ProjectsBasePath: PathMatcher[Unit] = PathMatcher("admin" / "projects") @@ -136,7 +137,7 @@ class ProjectsRouteADM(routeData: KnoraRouteData) post { entity(as[CreateProjectApiRequestADM]) { apiRequest => requestContext => // zio prelude: validation - val id: Validation[Throwable, Option[ProjectIRI]] = ProjectIRI.make(apiRequest.id) + val id: Validation[Throwable, Option[ProjectIri]] = ProjectIri.make(apiRequest.id) val shortname: Validation[Throwable, Shortname] = Shortname.make(apiRequest.shortname) val shortcode: Validation[Throwable, Shortcode] = Shortcode.make(apiRequest.shortcode) val longname: Validation[Throwable, Option[Longname]] = Longname.make(apiRequest.longname) 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 f8416ee181..49265e5b3c 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 @@ -14,7 +14,6 @@ import org.knora.webapi.exceptions.BadRequestException import org.knora.webapi.feature.FeatureFactoryConfig import org.knora.webapi.messages.admin.responder.usersmessages.UsersADMJsonProtocol._ import org.knora.webapi.messages.admin.responder.usersmessages._ -import org.knora.webapi.messages.admin.responder.valueObjects._ import org.knora.webapi.messages.util.KnoraSystemInstances import org.knora.webapi.routing.Authenticator import org.knora.webapi.routing.KnoraRoute @@ -25,6 +24,8 @@ import zio.prelude.Validation import java.util.UUID import javax.ws.rs.Path import scala.concurrent.Future +import dsp.valueobjects.Iri.UserIri +import dsp.valueobjects.User._ object UsersRouteADM { val UsersBasePath: PathMatcher[Unit] = PathMatcher("admin" / "users") @@ -119,7 +120,7 @@ class UsersRouteADM(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit post { entity(as[CreateUserApiRequestADM]) { apiRequest => requestContext => // get all values from request and make value objects from it - val id: Validation[Throwable, Option[UserIRI]] = UserIRI.make(apiRequest.id) + val id: Validation[Throwable, Option[UserIri]] = UserIri.make(apiRequest.id) val username: Validation[Throwable, Username] = Username.make(apiRequest.username) val email: Validation[Throwable, Email] = Email.make(apiRequest.email) val givenName: Validation[Throwable, GivenName] = GivenName.make(apiRequest.givenName) diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/OldListsRouteADMFeature.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/OldListsRouteADMFeature.scala index e654c36368..39574195f2 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/OldListsRouteADMFeature.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/OldListsRouteADMFeature.scala @@ -16,10 +16,7 @@ import org.knora.webapi.feature.Feature import org.knora.webapi.feature.FeatureFactoryConfig import org.knora.webapi.messages.admin.responder.listsmessages.ListNodeCreatePayloadADM.ListChildNodeCreatePayloadADM import org.knora.webapi.messages.admin.responder.listsmessages.ListNodeCreatePayloadADM.ListRootNodeCreatePayloadADM -import org.knora.webapi.messages.admin.responder.listsmessages.ListsErrorMessagesADM.LIST_CREATE_PERMISSION_ERROR -import org.knora.webapi.messages.admin.responder.listsmessages.ListsErrorMessagesADM.LIST_NODE_CREATE_PERMISSION_ERROR import org.knora.webapi.messages.admin.responder.listsmessages._ -import org.knora.webapi.messages.admin.responder.valueObjects._ import org.knora.webapi.routing.Authenticator import org.knora.webapi.routing.KnoraRoute import org.knora.webapi.routing.KnoraRouteData @@ -29,6 +26,9 @@ import zio.prelude.Validation import java.util.UUID import javax.ws.rs.Path import scala.concurrent.Future +import dsp.valueobjects.Iri._ +import dsp.valueobjects.List._ +import dsp.valueobjects.ListErrorMessages object OldListsRouteADMFeature { val ListsBasePath: PathMatcher[Unit] = PathMatcher("admin" / "lists") @@ -221,8 +221,8 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) private def createListRootNode(featureFactoryConfig: FeatureFactoryConfig): Route = path(ListsBasePath) { post { entity(as[ListRootNodeCreateApiRequestADM]) { apiRequest => requestContext => - val maybeId: Validation[Throwable, Option[ListIRI]] = ListIRI.make(apiRequest.id) - val projectIri: Validation[Throwable, ProjectIRI] = ProjectIRI.make(apiRequest.projectIri) + val maybeId: Validation[Throwable, Option[ListIri]] = ListIri.make(apiRequest.id) + val projectIri: Validation[Throwable, ProjectIri] = ProjectIri.make(apiRequest.projectIri) val maybeName: Validation[Throwable, Option[ListName]] = ListName.make(apiRequest.name) val labels: Validation[Throwable, Labels] = Labels.make(apiRequest.labels) val comments: Validation[Throwable, Comments] = Comments.make(apiRequest.comments) @@ -240,7 +240,7 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) ) && !requestingUser.permissions.isSystemAdmin ) { // not project or a system admin - throw ForbiddenException(LIST_CREATE_PERMISSION_ERROR) + throw ForbiddenException(ListErrorMessages.ListCreatePermission) } } yield ListRootNodeCreateRequestADM( createRootNode = payload, @@ -292,14 +292,14 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) post { entity(as[ListChildNodeCreateApiRequestADM]) { apiRequest => requestContext => // check if requested ListIri matches the Iri passed in the route - val parentNodeIri: Validation[Throwable, ListIRI] = if (iri == apiRequest.parentNodeIri) { - ListIRI.make(apiRequest.parentNodeIri) + val parentNodeIri: Validation[Throwable, ListIri] = if (iri == apiRequest.parentNodeIri) { + ListIri.make(apiRequest.parentNodeIri) } else { Validation.fail(throw BadRequestException("Route and payload parentNodeIri mismatch.")) } - val id: Validation[Throwable, Option[ListIRI]] = ListIRI.make(apiRequest.id) - val projectIri: Validation[Throwable, ProjectIRI] = ProjectIRI.make(apiRequest.projectIri) + val id: Validation[Throwable, Option[ListIri]] = ListIri.make(apiRequest.id) + val projectIri: Validation[Throwable, ProjectIri] = ProjectIri.make(apiRequest.projectIri) val name: Validation[Throwable, Option[ListName]] = ListName.make(apiRequest.name) val position: Validation[Throwable, Option[Position]] = Position.make(apiRequest.position) val labels: Validation[Throwable, Labels] = Labels.make(apiRequest.labels) @@ -320,7 +320,7 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) ) && !requestingUser.permissions.isSystemAdmin ) { // not project or a system admin - throw ForbiddenException(LIST_CREATE_PERMISSION_ERROR) + throw ForbiddenException(ListErrorMessages.ListCreatePermission) } } yield ListChildNodeCreateRequestADM( createChildNodeRequest = payload, @@ -371,14 +371,14 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) put { entity(as[ListNodeChangeApiRequestADM]) { apiRequest => requestContext => // check if requested Iri matches the route Iri - val listIri: Validation[Throwable, ListIRI] = if (iri == apiRequest.listIri) { - ListIRI.make(apiRequest.listIri) + val listIri: Validation[Throwable, ListIri] = if (iri == apiRequest.listIri) { + ListIri.make(apiRequest.listIri) } else { Validation.fail(throw BadRequestException("Route and payload listIri mismatch.")) } - val projectIri: Validation[Throwable, ProjectIRI] = ProjectIRI.make(apiRequest.projectIri) - val hasRootNode: Validation[Throwable, Option[ListIRI]] = ListIRI.make(apiRequest.hasRootNode) + val projectIri: Validation[Throwable, ProjectIri] = ProjectIri.make(apiRequest.projectIri) + val hasRootNode: Validation[Throwable, Option[ListIri]] = ListIri.make(apiRequest.hasRootNode) val position: Validation[Throwable, Option[Position]] = Position.make(apiRequest.position) val name: Validation[Throwable, Option[ListName]] = ListName.make(apiRequest.name) val labels: Validation[Throwable, Option[Labels]] = Labels.make(apiRequest.labels) @@ -399,7 +399,7 @@ class OldListsRouteADMFeature(routeData: KnoraRouteData) ) && !requestingUser.permissions.isSystemAdmin ) { // not project or a system admin - throw ForbiddenException(LIST_NODE_CREATE_PERMISSION_ERROR) + throw ForbiddenException(ListErrorMessages.ListNodeCreatePermission) } } yield NodeInfoChangeRequestADM( listIri = listIri.toOption.get.value, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/UpdateListItemsRouteADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/UpdateListItemsRouteADM.scala index bd9def74d1..97f1317bea 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/UpdateListItemsRouteADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/admin/lists/UpdateListItemsRouteADM.scala @@ -13,9 +13,6 @@ import org.knora.webapi.exceptions.BadRequestException import org.knora.webapi.feature.Feature import org.knora.webapi.feature.FeatureFactoryConfig import org.knora.webapi.messages.admin.responder.listsmessages._ -import org.knora.webapi.messages.admin.responder.valueObjects.Comments -import org.knora.webapi.messages.admin.responder.valueObjects.Labels -import org.knora.webapi.messages.admin.responder.valueObjects.ListName import org.knora.webapi.routing.Authenticator import org.knora.webapi.routing.KnoraRoute import org.knora.webapi.routing.KnoraRouteData @@ -24,6 +21,7 @@ import org.knora.webapi.routing.RouteUtilADM import java.util.UUID import javax.ws.rs.Path import scala.concurrent.Future +import dsp.valueobjects.List._ object UpdateListItemsRouteADM { val ListsBasePath: PathMatcher[Unit] = PathMatcher("admin" / "lists") diff --git a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/createNewGroup.scala.txt b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/createNewGroup.scala.txt index d57696ce5a..9255f6ed79 100644 --- a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/createNewGroup.scala.txt +++ b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/createNewGroup.scala.txt @@ -4,7 +4,7 @@ *@ @import org.knora.webapi.IRI -@import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 +@import dsp.valueobjects.V2 @* * Creates a new group. @@ -22,7 +22,7 @@ groupIri: IRI, groupClassIri: IRI, name: String, - descriptions: Seq[StringLiteralV2], + descriptions: Seq[V2.StringLiteralV2], projectIri: IRI, status: Boolean, hasSelfJoinEnabled: Boolean) diff --git a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/createNewListNode.scala.txt b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/createNewListNode.scala.txt index 3e26354e23..f14dfd127f 100644 --- a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/createNewListNode.scala.txt +++ b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/createNewListNode.scala.txt @@ -4,7 +4,7 @@ *@ @import org.knora.webapi.IRI -@import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 +@import dsp.valueobjects.V2 @* * Creates a new root list node. @@ -29,8 +29,8 @@ rootNodeIri: Option[IRI], position: Option[Int], maybeName: Option[String], - maybeLabels: Seq[StringLiteralV2], - maybeComments: Option[Seq[StringLiteralV2]] + maybeLabels: Seq[V2.StringLiteralV2], + maybeComments: Option[Seq[V2.StringLiteralV2]] ) PREFIX xsd: diff --git a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/createNewProject.scala.txt b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/createNewProject.scala.txt index f82a3f7539..83e0cbc7f3 100644 --- a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/createNewProject.scala.txt +++ b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/createNewProject.scala.txt @@ -4,7 +4,7 @@ *@ @import org.knora.webapi.IRI -@import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 +@import dsp.valueobjects.V2 @* * Creates a new project. @@ -28,7 +28,7 @@ shortname: String, shortcode: String, maybeLongname: Option[String], - maybeDescriptions: Option[Seq[StringLiteralV2]], + maybeDescriptions: Option[Seq[V2.StringLiteralV2]], maybeKeywords: Option[Seq[String]], maybeLogo: Option[String], status: Boolean, diff --git a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/updateGroup.scala.txt b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/updateGroup.scala.txt index 143cbc2b04..8ac1f29a7c 100644 --- a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/updateGroup.scala.txt +++ b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/updateGroup.scala.txt @@ -4,7 +4,7 @@ *@ @import org.knora.webapi.IRI -@import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 +@import dsp.valueobjects.V2 @** * Updates an existing group with the provided values. @@ -20,7 +20,7 @@ @(adminNamedGraphIri: IRI, groupIri: IRI, maybeName: Option[String], - maybeDescriptions: Option[Seq[StringLiteralV2]], + maybeDescriptions: Option[Seq[V2.StringLiteralV2]], maybeProject: Option[IRI], maybeStatus: Option[Boolean], maybeSelfjoin: Option[Boolean]) diff --git a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/updateListInfo.scala.txt b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/updateListInfo.scala.txt index 547333e9f5..c7d4fdd27e 100644 --- a/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/updateListInfo.scala.txt +++ b/webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/admin/updateListInfo.scala.txt @@ -4,7 +4,7 @@ *@ @import org.knora.webapi.IRI -@import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 +@import dsp.valueobjects.V2 @** * Updates an existing list with the provided values. @@ -26,8 +26,8 @@ hasOldName: Boolean, isRootNode: Boolean, maybeName : Option[String], - maybeLabels: Option[Seq[StringLiteralV2]], - maybeComments: Option[Seq[StringLiteralV2]]) + maybeLabels: Option[Seq[V2.StringLiteralV2]], + maybeComments: Option[Seq[V2.StringLiteralV2]]) PREFIX rdf: PREFIX rdfs: diff --git a/webapi/src/test/scala/org/knora/webapi/messages/StringFormatterSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/StringFormatterSpec.scala index 5b45212520..a0fdb31285 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/StringFormatterSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/StringFormatterSpec.scala @@ -1367,9 +1367,9 @@ class StringFormatterSpec extends CoreSpec() { val iri4 = "http://rdfh.ch/0001/cmfk1DMHRBiR4-_6HXpEFA" val iri5 = "http://rdfh.ch/080C/Ef9heHjPWDS7dMR_gGax2Q" - val testIRIFromVersion3UUID = stringFormatter.isUUIDVersion4Or5(iri3) - val testIRIFromVersion4UUID = stringFormatter.isUUIDVersion4Or5(iri4) - val testIRIFromVersion5UUID = stringFormatter.isUUIDVersion4Or5(iri5) + val testIRIFromVersion3UUID = stringFormatter.isUuidVersion4Or5(iri3) + val testIRIFromVersion4UUID = stringFormatter.isUuidVersion4Or5(iri4) + val testIRIFromVersion5UUID = stringFormatter.isUuidVersion4Or5(iri5) val iri = "http://rdfh.ch/0001/rYAMw7wSTbGw3boYHefByg" @@ -1378,8 +1378,8 @@ class StringFormatterSpec extends CoreSpec() { stringFormatter.makeRandomBase64EncodedUuid, stringFormatter.makeRandomBase64EncodedUuid, stringFormatter.getUUIDVersion(iri), - stringFormatter.hasUUIDLength(iri.split("/").last), - stringFormatter.isUUIDVersion4Or5(iri) + stringFormatter.hasUuidLength(iri.split("/").last), + stringFormatter.isUuidVersion4Or5(iri) ) testIRIFromVersion3UUID should be(false) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala index fa787e05dc..2267c8149a 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/listsmessages/ListsMessagesADMSpec.scala @@ -6,11 +6,13 @@ package org.knora.webapi.messages.admin.responder.listsmessages import com.typesafe.config.ConfigFactory +import dsp.valueobjects.Iri._ +import dsp.valueobjects.List._ +import dsp.valueobjects.ListErrorMessages +import dsp.valueobjects.V2 import org.knora.webapi.CoreSpec import org.knora.webapi.exceptions.BadRequestException import org.knora.webapi.messages.admin.responder.listsmessages.ListNodeCreatePayloadADM.ListChildNodeCreatePayloadADM -import org.knora.webapi.messages.admin.responder.listsmessages.ListsErrorMessagesADM._ -import org.knora.webapi.messages.admin.responder.valueObjects._ import org.knora.webapi.messages.store.triplestoremessages.StringLiteralSequenceV2 import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 import org.knora.webapi.sharedtestdata.SharedListsTestDataADM @@ -127,18 +129,18 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li } "throw 'BadRequestException' if invalid position given in payload of `createChildNodeRequest`" in { - val caught = intercept[BadRequestException]( + val caught = intercept[V2.BadRequestException]( ListChildNodeCreateRequestADM( createChildNodeRequest = ListChildNodeCreatePayloadADM( - parentNodeIri = ListIRI.make(exampleListIri).fold(e => throw e.head, v => v), - projectIri = ProjectIRI.make(SharedTestDataADM.IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), + parentNodeIri = ListIri.make(exampleListIri).fold(e => throw e.head, v => v), + projectIri = ProjectIri.make(SharedTestDataADM.IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), position = Some(Position.make(-3).fold(e => throw e.head, v => v)), labels = Labels - .make(Seq(StringLiteralV2(value = "New child node", language = Some("en")))) + .make(Seq(V2.StringLiteralV2(value = "New child node", language = Some("en")))) .fold(e => throw e.head, v => v), comments = Some( Comments - .make(Seq(StringLiteralV2(value = "New child comment", language = Some("en")))) + .make(Seq(V2.StringLiteralV2(value = "New child comment", language = Some("en")))) .fold(e => throw e.head, v => v) ) ), @@ -147,7 +149,7 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li apiRequestID = UUID.randomUUID() ) ) - assert(caught.getMessage === INVALID_POSITION) + assert(caught.getMessage === ListErrorMessages.InvalidPosition) } "throw 'BadRequestException' for `ChangeNodePositionApiRequestADM` when no parent node iri is given" in { @@ -190,7 +192,7 @@ class ListsMessagesADMSpec extends CoreSpec(ListsMessagesADMSpec.config) with Li val thrown = the[BadRequestException] thrownBy payload.parseJson.convertTo[ChangeNodePositionApiRequestADM] - thrown.getMessage should equal(INVALID_POSITION) + thrown.getMessage should equal(ListErrorMessages.InvalidPosition) } } } diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesADMSpec.scala index 8d55571c26..73d5931186 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/permissionsmessages/PermissionsMessagesADMSpec.scala @@ -5,13 +5,13 @@ package org.knora.webapi.messages.admin.responder.permissionsmessages +import dsp.valueobjects.IriErrorMessages import org.knora.webapi.CoreSpec import org.knora.webapi.exceptions.BadRequestException import org.knora.webapi.exceptions.ForbiddenException import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.OntologyConstants.KnoraAdmin.AdministrativePermissionAbbreviations import org.knora.webapi.messages.OntologyConstants.KnoraBase.EntityPermissionAbbreviations -import org.knora.webapi.messages.StringFormatter.UUID_INVALID_ERROR import org.knora.webapi.messages.admin.responder.permissionsmessages.PermissionsMessagesUtilADM.PermissionTypeAndCodes import org.knora.webapi.sharedtestdata.SharedOntologyTestDataADM._ import org.knora.webapi.sharedtestdata.SharedTestDataV1._ @@ -163,7 +163,7 @@ class PermissionsMessagesADMSpec extends CoreSpec() { apiRequestID = UUID.randomUUID() ) ) - assert(caught.getMessage === UUID_INVALID_ERROR) + assert(caught.getMessage === IriErrorMessages.UuidVersionInvalid) } "return 'BadRequest' if the no permissions supplied for AdministrativePermissionCreateRequestADM" in { @@ -560,7 +560,7 @@ class PermissionsMessagesADMSpec extends CoreSpec() { apiRequestID = UUID.randomUUID() ) ) - assert(caught.getMessage === UUID_INVALID_ERROR) + assert(caught.getMessage === IriErrorMessages.UuidVersionInvalid) } "return 'BadRequest' if the no permissions supplied for DefaultObjectAccessPermissionCreateRequestADM" in { diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/GroupsValueObjectsADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/GroupsValueObjectsADMSpec.scala deleted file mode 100644 index 1d9c3bc24e..0000000000 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/GroupsValueObjectsADMSpec.scala +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 org.knora.webapi.messages.admin.responder.valueObjects - -import org.knora.webapi.UnitSpec -import org.knora.webapi.exceptions.BadRequestException -import org.knora.webapi.messages.StringFormatter.UUID_INVALID_ERROR -import org.knora.webapi.messages.admin.responder.groupsmessages.GroupsErrorMessagesADM._ -import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 -import zio.prelude.Validation - -/** - * This spec is used to test the [[GroupsValueObjectsADM]] value objects creation. - */ -class GroupsValueObjectsADMSpec extends UnitSpec(ValueObjectsADMSpec.config) { - "GroupIRI value object" when { - val validGroupIri = "http://rdfh.ch/groups/0803/qBCJAdzZSCqC_2snW5Q7Nw" - val groupIRIWithUUIDVersion3 = "http://rdfh.ch/groups/0803/rKAU0FNjPUKWqOT8MEW_UQ" - - "created using empty value" should { - "throw BadRequestException" in { - GroupIRI.make("") should equal(Validation.fail(BadRequestException(GROUP_IRI_MISSING_ERROR))) - } - } - "created using invalid value" should { - "throw BadRequestException" in { - GroupIRI.make("not a group IRI") should equal(Validation.fail(BadRequestException(GROUP_IRI_INVALID_ERROR))) - GroupIRI.make(groupIRIWithUUIDVersion3) should equal( - Validation.fail(BadRequestException(UUID_INVALID_ERROR)) - ) - } - } - "created using valid value" should { - "not throw BadRequestException" in { - GroupIRI.make(validGroupIri) should not equal Validation.fail(BadRequestException(GROUP_IRI_INVALID_ERROR)) - } - "return value passed to value object" in { - GroupIRI.make(validGroupIri).toOption.get.value should equal(validGroupIri) - } - } - } - - "GroupName value object" when { - val validGroupName = "Valid group name" - - "created using empty value" should { - "throw BadRequestException" in { - GroupName.make("") should equal(Validation.fail(BadRequestException(GROUP_NAME_MISSING_ERROR))) - } - } - "created using invalid value" should { - "throw BadRequestException" in { - GroupName.make("Invalid group name\r") should equal( - Validation.fail(BadRequestException(GROUP_NAME_INVALID_ERROR)) - ) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - GroupName.make(validGroupName) should not equal Validation.fail(BadRequestException(GROUP_NAME_INVALID_ERROR)) - } - "return value passed to value object" in { - GroupName.make(validGroupName).toOption.get.value should equal(validGroupName) - } - } - } - - "GroupDescriptions value object" when { - val validDescription = Seq(StringLiteralV2(value = "Valid description", language = Some("en"))) - val invalidDescription = Seq(StringLiteralV2(value = "Invalid description \r", language = Some("en"))) - - "created using empty value" should { - "throw BadRequestException" in { - GroupDescriptions.make(Seq.empty) should equal( - Validation.fail(BadRequestException(GROUP_DESCRIPTION_MISSING_ERROR)) - ) - } - } - "created using invalid value" should { - "throw BadRequestException" in { - GroupDescriptions.make(invalidDescription) should equal( - Validation.fail(BadRequestException(GROUP_DESCRIPTION_INVALID_ERROR)) - ) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - GroupDescriptions.make(validDescription).toOption.get.value should not equal - BadRequestException(GROUP_DESCRIPTION_INVALID_ERROR) - } - "return value passed to value object" in { - GroupDescriptions.make(validDescription).toOption.get.value should equal(validDescription) - } - } - } - - "GroupStatus value object" when { - "created using valid value" should { - "return value passed to value object" in { - GroupStatus.make(true).toOption.get.value should equal(true) - GroupStatus.make(false).toOption.get.value should equal(false) - } - } - } - - "GroupSelfJoin value object" when { - "created using valid value" should { - "return value passed to value object" in { - GroupSelfJoin.make(false).toOption.get.value should equal(false) - GroupSelfJoin.make(true).toOption.get.value should equal(true) - } - } - } -} diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/ListsValueObjectsADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/ListsValueObjectsADMSpec.scala deleted file mode 100644 index 5024f2cc22..0000000000 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/ListsValueObjectsADMSpec.scala +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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 org.knora.webapi.messages.admin.responder.valueObjects - -import org.knora.webapi.UnitSpec -import org.knora.webapi.exceptions.BadRequestException -import org.knora.webapi.messages.StringFormatter.UUID_INVALID_ERROR -import org.knora.webapi.messages.admin.responder.listsmessages.ListsErrorMessagesADM._ -import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 -import zio.prelude.Validation - -/** - * This spec is used to test the [[ListsValueObjectsADM]] value objects creation. - */ -class ListsValueObjectsADMSpec extends UnitSpec(ValueObjectsADMSpec.config) { - "ListIRI value object" when { - val validListIri = "http://rdfh.ch/lists/0803/qBCJAdzZSCqC_2snW5Q7Nw" - val listIRIWithUUIDVersion3 = "http://rdfh.ch/lists/0803/6_xROK_UN1S2ZVNSzLlSXQ" - - "created using empty value" should { - "throw BadRequestException" in { - ListIRI.make("") should equal(Validation.fail(BadRequestException(LIST_NODE_IRI_MISSING_ERROR))) - } - } - "created using invalid value" should { - "throw BadRequestException" in { - ListIRI.make("not a list IRI") should equal(Validation.fail(BadRequestException(LIST_NODE_IRI_INVALID_ERROR))) - ListIRI.make(listIRIWithUUIDVersion3) should equal(Validation.fail(BadRequestException(UUID_INVALID_ERROR))) - } - } - "created using valid value" should { - "return value object that value equals to the value used to its creation" in { - ListIRI.make(validListIri) should not equal Validation.fail(BadRequestException(LIST_NODE_IRI_INVALID_ERROR)) - } - "return value passed to value object" in { - ListIRI.make(validListIri).toOption.get.value should equal(validListIri) - } - } - } - - "ListName value object" when { - val validListName = "Valid list name" - - "created using empty value" should { - "throw BadRequestException" in { - ListName.make("") should equal(Validation.fail(BadRequestException(LIST_NAME_MISSING_ERROR))) - } - } - "created using invalid value" should { - "throw BadRequestException" in { - ListName.make("\r") should equal(Validation.fail(BadRequestException(LIST_NAME_INVALID_ERROR))) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - ListName.make(validListName) should not equal Validation.fail(BadRequestException(LIST_NAME_INVALID_ERROR)) - } - "return value passed to value object" in { - ListName.make(validListName).toOption.get.value should equal(validListName) - } - } - } - - "Position value object" when { - val validPosition = 0 - - "created using invalid value" should { - "throw BadRequestException" in { - Position.make(-2) should equal(Validation.fail(BadRequestException(INVALID_POSITION))) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - Position.make(validPosition) should not equal Validation.fail(BadRequestException(INVALID_POSITION)) - } - "return value passed to value object" in { - Position.make(validPosition).toOption.get.value should equal(validPosition) - } - } - } - - "Labels value object" when { - val validLabels = Seq(StringLiteralV2(value = "New Label", language = Some("en"))) - val invalidLabels = Seq(StringLiteralV2(value = "\r", language = Some("en"))) - - "created using empty value" should { - "throw BadRequestException" in { - Labels.make(Seq.empty) should equal(Validation.fail(BadRequestException(LABEL_MISSING_ERROR))) - } - } - "created using invalid value" should { - "throw BadRequestException" in { - Labels.make(invalidLabels) should equal(Validation.fail(BadRequestException(LABEL_INVALID_ERROR))) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - Labels.make(validLabels) should not equal Validation.fail(BadRequestException(LABEL_INVALID_ERROR)) - } - "return value passed to value object" in { - Labels.make(validLabels).toOption.get.value should equal(validLabels) - } - } - } - - "Comments value object" when { - val validComments = Seq(StringLiteralV2(value = "Valid comment", language = Some("en"))) - val invalidComments = Seq(StringLiteralV2(value = "Invalid comment \r", language = Some("en"))) - - "created using empty value" should { - "throw BadRequestException" in { - Comments.make(Seq.empty) should equal(Validation.fail(BadRequestException(COMMENT_MISSING_ERROR))) - } - } - "created using invalid value" should { - "throw BadRequestException" in { - Comments.make(invalidComments) should equal(Validation.fail(BadRequestException(COMMENT_INVALID_ERROR))) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - Comments.make(validComments) should not equal Validation.fail(BadRequestException(COMMENT_INVALID_ERROR)) - } - "return value passed to value object" in { - Comments.make(validComments).toOption.get.value should equal(validComments) - } - } - } -} diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/ProjectsValueObjectsADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/ProjectsValueObjectsADMSpec.scala deleted file mode 100644 index b7ab53d0c9..0000000000 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/ProjectsValueObjectsADMSpec.scala +++ /dev/null @@ -1,209 +0,0 @@ -/* - * 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 org.knora.webapi.messages.admin.responder.valueObjects - -import org.knora.webapi.UnitSpec -import org.knora.webapi.exceptions.BadRequestException -import org.knora.webapi.messages.StringFormatter.UUID_INVALID_ERROR -import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectsErrorMessagesADM._ -import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 -import zio.prelude.Validation - -/** - * This spec is used to test the [[ProjectsValueObjectsADM]] value objects creation. - */ -class ProjectsValueObjectsADMSpec extends UnitSpec(ValueObjectsADMSpec.config) { - "ProjectIRI value object" when { - val validProjectIri = "http://rdfh.ch/projects/0001" - val projectIRIWithUUIDVersion3 = "http://rdfh.ch/projects/tZjZhGSZMeCLA5VeUmwAmg" - - "created using empty value" should { - "throw BadRequestException" in { - ProjectIRI.make("") should equal(Validation.fail(BadRequestException(PROJECT_IRI_MISSING_ERROR))) - } - } - "created using invalid value" should { - "throw BadRequestException" in { - ProjectIRI.make("not a project IRI") should equal( - Validation.fail(BadRequestException(PROJECT_IRI_INVALID_ERROR)) - ) - ProjectIRI.make(projectIRIWithUUIDVersion3) should equal( - Validation.fail(BadRequestException(UUID_INVALID_ERROR)) - ) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - ProjectIRI.make(validProjectIri) should not equal Validation.fail( - BadRequestException(PROJECT_IRI_INVALID_ERROR) - ) - } - "return value passed to value object" in { - ProjectIRI.make(validProjectIri).toOption.get.value should equal(validProjectIri) - } - } - } - - "Shortcode value object" when { - val validShortcode = "1234" - val invalidShortcode = "12345" - - "created using empty value" should { - "throw BadRequestException" in { - Shortcode.make("") should equal( - Validation.fail(BadRequestException(SHORTCODE_MISSING_ERROR)) - ) - } - } - "created using invalid value" should { - "throw BadRequestException" in { - Shortcode.make(invalidShortcode) should equal( - Validation.fail(BadRequestException(SHORTCODE_INVALID_ERROR)) - ) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - Shortcode.make(validShortcode).toOption.get.value should not equal - BadRequestException(SHORTCODE_INVALID_ERROR) - } - "return value passed to value object" in { - Shortcode.make(validShortcode).toOption.get.value should equal(validShortcode) - } - } - } - - "Shortname value object" when { - val validShortname = "validShortname" - val invalidShortname = "~!@#$%^&*()_+" - - "created using empty value" should { - "throw BadRequestException" in { - Shortname.make("") should equal( - Validation.fail(BadRequestException(SHORTNAME_MISSING_ERROR)) - ) - } - } - "created using invalid value" should { - "throw BadRequestException" in { - Shortname.make(invalidShortname) should equal( - Validation.fail(BadRequestException(SHORTNAME_INVALID_ERROR)) - ) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - Shortname.make(validShortname).toOption.get.value should not equal - BadRequestException(SHORTNAME_INVALID_ERROR) - } - "return value passed to value object" in { - Shortname.make(validShortname).toOption.get.value should equal(validShortname) - } - } - } - - "Longname value object" when { - val validLongname = "That's the project longname" - - "created using empty value" should { - "throw BadRequestException" in { - Longname.make("") should equal( - Validation.fail(BadRequestException(LONGNAME_MISSING_ERROR)) - ) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - Longname.make(validLongname).toOption.get.value should not equal - BadRequestException(LONGNAME_INVALID_ERROR) - } - "return value passed to value object" in { - Longname.make(validLongname).toOption.get.value should equal(validLongname) - } - } - } - - "ProjectDescription value object" when { - val validProjectDescription = Seq(StringLiteralV2(value = "Valid description", language = Some("en"))) - - "created using empty value" should { - "throw BadRequestException" in { - ProjectDescription.make(Seq.empty) should equal( - Validation.fail(BadRequestException(PROJECT_DESCRIPTION_MISSING_ERROR)) - ) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - ProjectDescription.make(validProjectDescription).toOption.get.value should not equal - BadRequestException(PROJECT_DESCRIPTION_INVALID_ERROR) - } - "return value passed to value object" in { - ProjectDescription.make(validProjectDescription).toOption.get.value should equal(validProjectDescription) - } - } - } - - "Keywords value object" when { - val validKeywords = Seq("key", "word") - - "created using empty value" should { - "throw BadRequestException" in { - Keywords.make(Seq.empty) should equal( - Validation.fail(BadRequestException(KEYWORDS_MISSING_ERROR)) - ) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - Keywords.make(validKeywords).toOption.get.value should not equal - BadRequestException(KEYWORDS_INVALID_ERROR) - } - "return value passed to value object" in { - Keywords.make(validKeywords).toOption.get.value should equal(validKeywords) - } - } - } - - "Logo value object" when { - val validLogo = "/fu/bar/baz.jpg" - - "created using empty value" should { - "throw BadRequestException" in { - Logo.make("") should equal( - Validation.fail(BadRequestException(LOGO_MISSING_ERROR)) - ) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - Logo.make(validLogo).toOption.get.value should not equal - BadRequestException(LOGO_INVALID_ERROR) - } - "return value passed to value object" in { - Logo.make(validLogo).toOption.get.value should equal(validLogo) - } - } - } - - "ProjectSelfJoin value object" when { - "created using valid value" should { - "return value passed to value object" in { - ProjectSelfJoin.make(false).toOption.get.value should equal(false) - ProjectSelfJoin.make(true).toOption.get.value should equal(true) - } - } - } - - "ProjectStatus value object" when { - "created using valid value" should { - "return value passed to value object" in { - ProjectStatus.make(true).toOption.get.value should equal(true) - ProjectStatus.make(false).toOption.get.value should equal(false) - } - } - } -} diff --git a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/UsersValueObjectsADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/UsersValueObjectsADMSpec.scala deleted file mode 100644 index a6ca4770ee..0000000000 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/UsersValueObjectsADMSpec.scala +++ /dev/null @@ -1,232 +0,0 @@ -/* - * 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 org.knora.webapi.messages.admin.responder.valueObjects - -import org.knora.webapi.UnitSpec -import org.knora.webapi.exceptions.BadRequestException -import org.knora.webapi.messages.StringFormatter.UUID_INVALID_ERROR -import org.knora.webapi.messages.admin.responder.usersmessages.UsersErrorMessagesADM._ -import zio.prelude.Validation - -/** - * This spec is used to test the [[UsersValueObjectsADM]] value objects creation. - */ -class UsersValueObjectsADMSpec extends UnitSpec(ValueObjectsADMSpec.config) { - "UserIRI value object" when { - val validUserIRI = "http://rdfh.ch/users/jDEEitJESRi3pDaDjjQ1WQ" - val userIRIWithUUIDVersion3 = "http://rdfh.ch/users/cCmdcpn2MO211YYOplR1hQ" - - "created using empty value" should { - "throw BadRequestException" in { - UserIRI.make("") should equal(Validation.fail(BadRequestException(USER_IRI_MISSING_ERROR))) - } - } - "created using invalid value" should { - "throw BadRequestException" in { - UserIRI.make("not a user IRI") should equal(Validation.fail(BadRequestException(USER_IRI_INVALID_ERROR))) - UserIRI.make(userIRIWithUUIDVersion3) should equal(Validation.fail(BadRequestException(UUID_INVALID_ERROR))) - } - } - "created using valid value" should { - "not throw BadRequestException" in { - UserIRI.make(validUserIRI) should not equal Validation.fail(BadRequestException(USER_IRI_INVALID_ERROR)) - } - "return value passed to value object" in { - UserIRI.make(validUserIRI).toOption.get.value should equal(validUserIRI) - } - } - } - - "Username value object" when { - val validUsername = "user008" - val invalidUsername = "user!@#$%^&*()_+" - - "created using empty value" should { - "throw BadRequestException" in { - Username.make("") should equal(Validation.fail(BadRequestException(USERNAME_MISSING_ERROR))) - } - } - "created using invalid value" should { - "throw BadRequestException for username less than 4 characters long" in { - Username.make("123") should equal( - Validation.fail(BadRequestException(USERNAME_INVALID_ERROR)) - ) - } - "throw BadRequestException for username containg more than 50 characters" in { - Username.make("01234567890123456789012345678901234567890123456789011") should equal( - Validation.fail(BadRequestException(USERNAME_INVALID_ERROR)) - ) - } - "throw BadRequestException for username started with underscore" in { - Username.make("_123") should equal( - Validation.fail(BadRequestException(USERNAME_INVALID_ERROR)) - ) - } - "throw BadRequestException for username ended with underscore" in { - Username.make("123_") should equal( - Validation.fail(BadRequestException(USERNAME_INVALID_ERROR)) - ) - } - "throw BadRequestException for username started with dot" in { - Username.make(".123") should equal( - Validation.fail(BadRequestException(USERNAME_INVALID_ERROR)) - ) - } - "throw BadRequestException for username ended with dot" in { - Username.make("123.") should equal( - Validation.fail(BadRequestException(USERNAME_INVALID_ERROR)) - ) - } - "throw BadRequestException for username with underscore used multiple times in a row" in { - Username.make("1__23") should equal( - Validation.fail(BadRequestException(USERNAME_INVALID_ERROR)) - ) - } - "throw BadRequestException for username with dot used multiple times in a row" in { - Username.make("1..23") should equal( - Validation.fail(BadRequestException(USERNAME_INVALID_ERROR)) - ) - } - "throw BadRequestException for username created with bad forbidden characters" in { - Username.make(invalidUsername) should equal( - Validation.fail(BadRequestException(USERNAME_INVALID_ERROR)) - ) - } - } - "created using valid characters" should { - "not throw BadRequestExceptions" in { - Username.make(validUsername) should not equal Validation.fail(BadRequestException(USERNAME_INVALID_ERROR)) - } - "return value passed to value object" in { - Username.make(validUsername).toOption.get.value should equal(validUsername) - } - } - } - - "Email value object" when { - val validEmailAddress = "address@ch" - val invalidEmailAddress = "invalid_email_address" - - "created using empty value" should { - "throw BadRequestException" in { - Email.make("") should equal( - Validation.fail(BadRequestException(EMAIL_MISSING_ERROR)) - ) - } - } - "created using invalid value" should { - "throw BadRequestException" in { - Email.make(invalidEmailAddress) should equal( - Validation.fail(BadRequestException(EMAIL_INVALID_ERROR)) - ) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - Email.make(validEmailAddress).toOption.get.value should not equal - BadRequestException(EMAIL_INVALID_ERROR) - } - "return value passed to value object" in { - Email.make(validEmailAddress).toOption.get.value should equal(validEmailAddress) - } - } - } - - "Password value object" when { - val validassword = "pass-word" - - "created using empty value" should { - "throw BadRequestException" in { - Password.make("") should equal(Validation.fail(BadRequestException(PASSWORD_MISSING_ERROR))) - } - } - "created using valid characters" should { - "not throw BadRequestExceptions" in { - Password.make(validassword) should not equal Validation.fail(BadRequestException(USERNAME_INVALID_ERROR)) - } - "return value passed to value object" in { - Password.make(validassword).toOption.get.value should equal(validassword) - } - } - } - - "GivenName value object" when { - val validGivenName = "John" - - "created using empty value" should { - "throw BadRequestException" in { - GivenName.make("") should equal( - Validation.fail(BadRequestException(GIVEN_NAME_MISSING_ERROR)) - ) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - GivenName.make(validGivenName).toOption.get.value should not equal - BadRequestException(GIVEN_NAME_INVALID_ERROR) - } - "return value passed to value object" in { - GivenName.make(validGivenName).toOption.get.value should equal(validGivenName) - } - } - } - - "FamilyName value object" when { - val validFamilyName = "Rambo" - - "created using empty value" should { - "throw BadRequestException" in { - FamilyName.make("") should equal( - Validation.fail(BadRequestException(FAMILY_NAME_MISSING_ERROR)) - ) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - FamilyName.make(validFamilyName).toOption.get.value should not equal - BadRequestException(FAMILY_NAME_INVALID_ERROR) - } - "return value passed to value object" in { - FamilyName.make(validFamilyName).toOption.get.value should equal(validFamilyName) - } - } - } - - "LanguageCode value object" when { - "created using empty value" should { - "throw BadRequestException" in { - LanguageCode.make("") should equal( - Validation.fail(BadRequestException(LANGUAGE_CODE_MISSING_ERROR)) - ) - } - } - "created using invalid value" should { - "throw BadRequestException" in { - LanguageCode.make("kk") should equal( - Validation.fail(BadRequestException(LANGUAGE_CODE_INVALID_ERROR)) - ) - } - } - "created using valid value" should { - "not throw BadRequestExceptions" in { - LanguageCode.make("de").toOption.get.value should not equal - BadRequestException(LANGUAGE_CODE_INVALID_ERROR) - } - "return value passed to value object" in { - LanguageCode.make("en").toOption.get.value should equal("en") - } - } - } - - "SystemAdmin value object" when { - "created using valid value" should { - "return value passed to value object" in { - SystemAdmin.make(true).toOption.get.value should equal(true) - SystemAdmin.make(false).toOption.get.value should equal(false) - } - } - } -} 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 deleted file mode 100644 index 0f86319a50..0000000000 --- a/webapi/src/test/scala/org/knora/webapi/messages/admin/responder/valueObjects/ValueObjectsADMSpec.scala +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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 org.knora.webapi.messages.admin.responder.valueObjects -import com.typesafe.config.Config -import com.typesafe.config.ConfigFactory - -object ValueObjectsADMSpec { - val config: Config = ConfigFactory.parseString(""" - akka.loglevel = "DEBUG" - akka.stdout-loglevel = "DEBUG" - """.stripMargin) -} 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 809ef16b17..b0946947a3 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 @@ -13,13 +13,15 @@ import akka.actor.Status.Failure import akka.testkit.ImplicitSender import com.typesafe.config.Config import com.typesafe.config.ConfigFactory +import dsp.valueobjects.Group._ +import dsp.valueobjects.Iri._ +import dsp.valueobjects.V2 import org.knora.webapi._ import org.knora.webapi.exceptions.BadRequestException import org.knora.webapi.exceptions.DuplicateValueException import org.knora.webapi.exceptions.NotFoundException import org.knora.webapi.messages.admin.responder.groupsmessages._ import org.knora.webapi.messages.admin.responder.usersmessages.UserInformationTypeADM -import org.knora.webapi.messages.admin.responder.valueObjects._ import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.util.MutableTestIri @@ -93,11 +95,14 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit descriptions = GroupDescriptions .make( Seq( - StringLiteralV2(value = """NewGroupDescription with "quotes" and """, language = Some("en")) + V2.StringLiteralV2( + value = """NewGroupDescription with "quotes" and """, + language = Some("en") + ) ) ) .fold(e => throw e.head, v => v), - project = ProjectIRI.make(SharedTestDataADM.IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), + project = ProjectIri.make(SharedTestDataADM.IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), status = GroupStatus.make(true).fold(e => throw e.head, v => v), selfjoin = GroupSelfJoin.make(false).fold(e => throw e.head, v => v) ), @@ -125,13 +130,13 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit responderManager ! GroupCreateRequestADM( createRequest = GroupCreatePayloadADM( id = Some( - GroupIRI.make(imagesReviewerGroup.id).fold(e => throw e.head, v => v) + GroupIri.make(imagesReviewerGroup.id).fold(e => throw e.head, v => v) ), name = GroupName.make("NewGroup").fold(e => throw e.head, v => v), descriptions = GroupDescriptions - .make(Seq(StringLiteralV2(value = "NewGroupDescription", language = Some("en")))) + .make(Seq(V2.StringLiteralV2(value = "NewGroupDescription", language = Some("en")))) .fold(e => throw e.head, v => v), - project = ProjectIRI.make(SharedTestDataADM.IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), + project = ProjectIri.make(SharedTestDataADM.IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), status = GroupStatus.make(true).fold(e => throw e.head, v => v), selfjoin = GroupSelfJoin.make(false).fold(e => throw e.head, v => v) ), @@ -153,7 +158,7 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit Some( GroupDescriptions .make( - Seq(StringLiteralV2(value = """UpdatedDescription with "quotes" and """, Some("en"))) + Seq(V2.StringLiteralV2(value = """UpdatedDescription with "quotes" and """, Some("en"))) ) .fold(e => throw e.head, v => v) ) @@ -182,7 +187,7 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit Some(GroupName.make("UpdatedGroupName").fold(e => throw e.head, v => v)), Some( GroupDescriptions - .make(Seq(StringLiteralV2(value = "UpdatedDescription", language = Some("en")))) + .make(Seq(V2.StringLiteralV2(value = "UpdatedDescription", language = Some("en")))) .fold(e => throw e.head, v => v) ) ), @@ -203,7 +208,7 @@ class GroupsResponderADMSpec extends CoreSpec(GroupsResponderADMSpec.config) wit Some(GroupName.make("Image reviewer").fold(e => throw e.head, v => v)), Some( GroupDescriptions - .make(Seq(StringLiteralV2(value = "UpdatedDescription", language = Some("en")))) + .make(Seq(V2.StringLiteralV2(value = "UpdatedDescription", language = Some("en")))) .fold(e => throw e.head, v => v) ) ), diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala index 643b6e9e92..395eb21078 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/ListsResponderADMSpec.scala @@ -9,6 +9,9 @@ import akka.actor.Status.Failure import akka.testkit._ import com.typesafe.config.Config import com.typesafe.config.ConfigFactory +import dsp.valueobjects.Iri._ +import dsp.valueobjects.List._ +import dsp.valueobjects.V2 import org.knora.webapi._ import org.knora.webapi.exceptions.BadRequestException import org.knora.webapi.exceptions.DuplicateValueException @@ -17,7 +20,6 @@ import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.admin.responder.listsmessages.ListNodeCreatePayloadADM.ListChildNodeCreatePayloadADM import org.knora.webapi.messages.admin.responder.listsmessages.ListNodeCreatePayloadADM.ListRootNodeCreatePayloadADM import org.knora.webapi.messages.admin.responder.listsmessages._ -import org.knora.webapi.messages.admin.responder.valueObjects._ import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 import org.knora.webapi.sharedtestdata.SharedListsTestDataADM @@ -168,13 +170,13 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with "create a list" in { responderManager ! ListRootNodeCreateRequestADM( createRootNode = ListRootNodeCreatePayloadADM( - projectIri = ProjectIRI.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), + projectIri = ProjectIri.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), name = Some(ListName.make("neuelistename").fold(e => throw e.head, v => v)), labels = Labels - .make(Seq(StringLiteralV2(value = "Neue Liste", language = Some("de")))) + .make(Seq(V2.StringLiteralV2(value = "Neue Liste", language = Some("de")))) .fold(e => throw e.head, v => v), comments = Comments - .make(Seq(StringLiteralV2(value = "Neuer Kommentar", language = Some("de")))) + .make(Seq(V2.StringLiteralV2(value = "Neuer Kommentar", language = Some("de")))) .fold(e => throw e.head, v => v) ), featureFactoryConfig = defaultFeatureFactoryConfig, @@ -209,13 +211,13 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with val nameWithSpecialCharacter = "a new \\\"name\\\"" responderManager ! ListRootNodeCreateRequestADM( createRootNode = ListRootNodeCreatePayloadADM( - projectIri = ProjectIRI.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), + projectIri = ProjectIri.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), name = Some(ListName.make(nameWithSpecialCharacter).fold(e => throw e.head, v => v)), labels = Labels - .make(Seq(StringLiteralV2(value = labelWithSpecialCharacter, language = Some("de")))) + .make(Seq(V2.StringLiteralV2(value = labelWithSpecialCharacter, language = Some("de")))) .fold(e => throw e.head, v => v), comments = Comments - .make(Seq(StringLiteralV2(value = commentWithSpecialCharacter, language = Some("de")))) + .make(Seq(V2.StringLiteralV2(value = commentWithSpecialCharacter, language = Some("de")))) .fold(e => throw e.head, v => v) ), featureFactoryConfig = defaultFeatureFactoryConfig, @@ -249,15 +251,15 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with val changeNodeInfoRequest = NodeInfoChangeRequestADM( listIri = newListIri.get, changeNodeRequest = ListNodeChangePayloadADM( - listIri = ListIRI.make(newListIri.get).fold(e => throw e.head, v => v), - projectIri = ProjectIRI.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), + listIri = ListIri.make(newListIri.get).fold(e => throw e.head, v => v), + projectIri = ProjectIri.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), name = Some(ListName.make("updated name").fold(e => throw e.head, v => v)), labels = Some( Labels .make( Seq( - StringLiteralV2(value = "Neue geänderte Liste", language = Some("de")), - StringLiteralV2(value = "Changed List", language = Some("en")) + V2.StringLiteralV2(value = "Neue geänderte Liste", language = Some("de")), + V2.StringLiteralV2(value = "Changed List", language = Some("en")) ) ) .fold(e => throw e.head, v => v) @@ -266,8 +268,8 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with Comments .make( Seq( - StringLiteralV2(value = "Neuer Kommentar", language = Some("de")), - StringLiteralV2(value = "New Comment", language = Some("en")) + V2.StringLiteralV2(value = "Neuer Kommentar", language = Some("de")), + V2.StringLiteralV2(value = "New Comment", language = Some("en")) ) ) .fold(e => throw e.head, v => v) @@ -305,11 +307,11 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with "not update basic list information if name is duplicate" in { val name = Some(ListName.make("sommer").fold(e => throw e.head, v => v)) - val projectIRI = ProjectIRI.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v) + val projectIRI = ProjectIri.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v) responderManager ! NodeInfoChangeRequestADM( listIri = newListIri.get, changeNodeRequest = ListNodeChangePayloadADM( - listIri = ListIRI.make(newListIri.get).fold(e => throw e.head, v => v), + listIri = ListIri.make(newListIri.get).fold(e => throw e.head, v => v), projectIri = projectIRI, name = name ), @@ -329,15 +331,15 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with "add child to list - to the root node" in { responderManager ! ListChildNodeCreateRequestADM( createChildNodeRequest = ListChildNodeCreatePayloadADM( - parentNodeIri = ListIRI.make(newListIri.get).fold(e => throw e.head, v => v), - projectIri = ProjectIRI.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), + parentNodeIri = ListIri.make(newListIri.get).fold(e => throw e.head, v => v), + projectIri = ProjectIri.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), name = Some(ListName.make("first").fold(e => throw e.head, v => v)), labels = Labels - .make(Seq(StringLiteralV2(value = "New First Child List Node Value", language = Some("en")))) + .make(Seq(V2.StringLiteralV2(value = "New First Child List Node Value", language = Some("en")))) .fold(e => throw e.head, v => v), comments = Some( Comments - .make(Seq(StringLiteralV2(value = "New First Child List Node Comment", language = Some("en")))) + .make(Seq(V2.StringLiteralV2(value = "New First Child List Node Comment", language = Some("en")))) .fold(e => throw e.head, v => v) ) ), @@ -358,7 +360,9 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with // check labels val labels: Seq[StringLiteralV2] = childNodeInfo.labels.stringLiterals labels.size should be(1) - labels.sorted should be(Seq(StringLiteralV2(value = "New First Child List Node Value", language = Some("en")))) + labels.sorted should be( + Seq(StringLiteralV2(value = "New First Child List Node Value", language = Some("en"))) + ) // check comments val comments = childNodeInfo.comments.stringLiterals @@ -381,16 +385,16 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with "add second child to list in first position - to the root node" in { responderManager ! ListChildNodeCreateRequestADM( createChildNodeRequest = ListChildNodeCreatePayloadADM( - parentNodeIri = ListIRI.make(newListIri.get).fold(e => throw e.head, v => v), - projectIri = ProjectIRI.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), + parentNodeIri = ListIri.make(newListIri.get).fold(e => throw e.head, v => v), + projectIri = ProjectIri.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), name = Some(ListName.make("second").fold(e => throw e.head, v => v)), position = Some(Position.make(0).fold(e => throw e.head, v => v)), labels = Labels - .make(Seq(StringLiteralV2(value = "New Second Child List Node Value", language = Some("en")))) + .make(Seq(V2.StringLiteralV2(value = "New Second Child List Node Value", language = Some("en")))) .fold(e => throw e.head, v => v), comments = Some( Comments - .make(Seq(StringLiteralV2(value = "New Second Child List Node Comment", language = Some("en")))) + .make(Seq(V2.StringLiteralV2(value = "New Second Child List Node Comment", language = Some("en")))) .fold(e => throw e.head, v => v) ) ), @@ -411,7 +415,9 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with // check labels val labels: Seq[StringLiteralV2] = childNodeInfo.labels.stringLiterals labels.size should be(1) - labels.sorted should be(Seq(StringLiteralV2(value = "New Second Child List Node Value", language = Some("en")))) + labels.sorted should be( + Seq(StringLiteralV2(value = "New Second Child List Node Value", language = Some("en"))) + ) // check comments val comments = childNodeInfo.comments.stringLiterals @@ -434,15 +440,15 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with "add child to second child node" in { responderManager ! ListChildNodeCreateRequestADM( createChildNodeRequest = ListChildNodeCreatePayloadADM( - parentNodeIri = ListIRI.make(secondChildIri.get).fold(e => throw e.head, v => v), - projectIri = ProjectIRI.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), + parentNodeIri = ListIri.make(secondChildIri.get).fold(e => throw e.head, v => v), + projectIri = ProjectIri.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), name = Some(ListName.make("third").fold(e => throw e.head, v => v)), labels = Labels - .make(Seq(StringLiteralV2(value = "New Third Child List Node Value", language = Some("en")))) + .make(Seq(V2.StringLiteralV2(value = "New Third Child List Node Value", language = Some("en")))) .fold(e => throw e.head, v => v), comments = Some( Comments - .make(Seq(StringLiteralV2(value = "New Third Child List Node Comment", language = Some("en")))) + .make(Seq(V2.StringLiteralV2(value = "New Third Child List Node Comment", language = Some("en")))) .fold(e => throw e.head, v => v) ) ), @@ -463,7 +469,9 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with // check labels val labels: Seq[StringLiteralV2] = childNodeInfo.labels.stringLiterals labels.size should be(1) - labels.sorted should be(Seq(StringLiteralV2(value = "New Third Child List Node Value", language = Some("en")))) + labels.sorted should be( + Seq(StringLiteralV2(value = "New Third Child List Node Value", language = Some("en"))) + ) // check comments val comments = childNodeInfo.comments.stringLiterals @@ -487,16 +495,16 @@ class ListsResponderADMSpec extends CoreSpec(ListsResponderADMSpec.config) with val givenPosition = Some(Position.make(20).fold(e => throw e.head, v => v)) responderManager ! ListChildNodeCreateRequestADM( createChildNodeRequest = ListChildNodeCreatePayloadADM( - parentNodeIri = ListIRI.make(newListIri.get).fold(e => throw e.head, v => v), - projectIri = ProjectIRI.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), + parentNodeIri = ListIri.make(newListIri.get).fold(e => throw e.head, v => v), + projectIri = ProjectIri.make(IMAGES_PROJECT_IRI).fold(e => throw e.head, v => v), name = Some(ListName.make("fourth").fold(e => throw e.head, v => v)), position = givenPosition, labels = Labels - .make(Seq(StringLiteralV2(value = "New Fourth Child List Node Value", language = Some("en")))) + .make(Seq(V2.StringLiteralV2(value = "New Fourth Child List Node Value", language = Some("en")))) .fold(e => throw e.head, v => v), comments = Some( Comments - .make(Seq(StringLiteralV2(value = "New Fourth Child List Node Comment", language = Some("en")))) + .make(Seq(V2.StringLiteralV2(value = "New Fourth Child List Node Comment", language = Some("en")))) .fold(e => throw e.head, v => v) ) ), 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 cf213a9dd2..d4c34579ed 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 @@ -13,6 +13,9 @@ import akka.actor.Status.Failure import akka.testkit.ImplicitSender import com.typesafe.config.Config import com.typesafe.config.ConfigFactory +import dsp.valueobjects.Iri.ProjectIri +import dsp.valueobjects.Project._ +import dsp.valueobjects.V2 import org.knora.webapi._ import org.knora.webapi.exceptions.BadRequestException import org.knora.webapi.exceptions.DuplicateValueException @@ -22,7 +25,6 @@ import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.admin.responder.permissionsmessages._ import org.knora.webapi.messages.admin.responder.projectsmessages._ import org.knora.webapi.messages.admin.responder.usersmessages.UserInformationTypeADM -import org.knora.webapi.messages.admin.responder.valueObjects._ import org.knora.webapi.messages.store.triplestoremessages._ import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.util.MutableTestIri @@ -196,7 +198,7 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) 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 = ProjectDescription - .make(Seq(StringLiteralV2(value = "project description", language = Some("en")))) + .make(Seq(V2.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), @@ -290,7 +292,7 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) 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 = ProjectDescription - .make(Seq(StringLiteralV2(value = "project description", language = Some("en")))) + .make(Seq(V2.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), @@ -324,7 +326,7 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) longname = Longname.make(Some(longnameWithSpecialCharacter)).fold(error => throw error.head, value => value), description = ProjectDescription - .make(Seq(StringLiteralV2(value = descriptionWithSpecialCharacter, language = Some("en")))) + .make(Seq(V2.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), @@ -357,7 +359,7 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) 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 = ProjectDescription - .make(Seq(StringLiteralV2(value = "project description", language = Some("en")))) + .make(Seq(V2.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), @@ -378,7 +380,7 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) 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 = ProjectDescription - .make(Seq(StringLiteralV2(value = "project description", language = Some("en")))) + .make(Seq(V2.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), 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 0ad60c87ee..a5ee1dd022 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 @@ -9,6 +9,8 @@ import akka.actor.Status.Failure import akka.testkit.ImplicitSender import com.typesafe.config.Config import com.typesafe.config.ConfigFactory +import dsp.valueobjects.User._ +import dsp.valueobjects.V2 import org.knora.webapi._ import org.knora.webapi.exceptions.BadRequestException import org.knora.webapi.exceptions.DuplicateValueException @@ -19,14 +21,6 @@ import org.knora.webapi.messages.admin.responder.groupsmessages.GroupMembersGetR import org.knora.webapi.messages.admin.responder.groupsmessages.GroupMembersGetResponseADM import org.knora.webapi.messages.admin.responder.projectsmessages._ import org.knora.webapi.messages.admin.responder.usersmessages._ -import org.knora.webapi.messages.admin.responder.valueObjects.Email -import org.knora.webapi.messages.admin.responder.valueObjects.FamilyName -import org.knora.webapi.messages.admin.responder.valueObjects.GivenName -import org.knora.webapi.messages.admin.responder.valueObjects.LanguageCode -import org.knora.webapi.messages.admin.responder.valueObjects.Password -import org.knora.webapi.messages.admin.responder.valueObjects.SystemAdmin -import org.knora.webapi.messages.admin.responder.valueObjects.UserStatus -import org.knora.webapi.messages.admin.responder.valueObjects.Username import org.knora.webapi.messages.util.KnoraSystemInstances import org.knora.webapi.messages.v2.routing.authenticationmessages.KnoraCredentialsV2.KnoraPasswordCredentialsV2 import org.knora.webapi.routing.Authenticator