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 2b6285aa03..1ed46040f2 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 @@ -21,7 +21,6 @@ package org.knora.webapi.messages.admin.responder.projectsmessages import java.nio.file.Path import java.util.UUID - import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport import org.apache.commons.lang3.builder.HashCodeBuilder import org.knora.webapi.IRI @@ -29,7 +28,7 @@ import org.knora.webapi.annotation.{ApiMayChange, ServerUnique} import org.knora.webapi.exceptions.{BadRequestException, DataConversionException, OntologyConstraintException} import org.knora.webapi.feature.FeatureFactoryConfig import org.knora.webapi.messages.StringFormatter -import org.knora.webapi.messages.admin.responder.usersmessages.UserADM +import org.knora.webapi.messages.admin.responder.usersmessages.{ProjectCreatePayloadADM, UserADM} import org.knora.webapi.messages.admin.responder.{KnoraRequestADM, KnoraResponseADM} import org.knora.webapi.messages.store.triplestoremessages.{StringLiteralV2, TriplestoreJsonProtocol} import org.knora.webapi.messages.v1.responder.projectmessages.ProjectInfoV1 @@ -365,13 +364,13 @@ case class ProjectDataGetRequestADM( /** * Requests the creation of a new project. * - * @param createRequest the [[CreateProjectApiRequestADM]] information for creation a new project. + * @param createRequest the [[ProjectCreatePayloadADM]] information for creation a new project. * @param featureFactoryConfig the feature factory configuration. * @param requestingUser the user making the request. * @param apiRequestID the ID of the API request. */ case class ProjectCreateRequestADM( - createRequest: CreateProjectApiRequestADM, + createRequest: ProjectCreatePayloadADM, featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM, apiRequestID: UUID diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/ProjectPayloadsADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/ProjectPayloadsADM.scala new file mode 100644 index 0000000000..f9f1562674 --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/ProjectPayloadsADM.scala @@ -0,0 +1,45 @@ +package org.knora.webapi.messages.admin.responder.usersmessages + +import org.knora.webapi.IRI + +/** + * Project payload + */ +sealed abstract case class ProjectCreatePayloadADM private ( + id: Option[IRI], + shortname: Shortname, + shortcode: Shortcode, + longname: Option[Longname], + description: Description, + keywords: Keywords, + logo: Option[Logo], + status: Status, + selfjoin: Selfjoin +) + +object ProjectCreatePayloadADM { + + /** The create constructor */ + def create( + id: Option[IRI] = None, + shortname: Shortname, + shortcode: Shortcode, + longname: Option[Longname] = None, + description: Description, + keywords: Keywords, + logo: Option[Logo] = None, + status: Status, + selfjoin: Selfjoin + ): ProjectCreatePayloadADM = + new ProjectCreatePayloadADM( + id = id, + shortname = shortname, + shortcode = shortcode, + longname = longname, + description = description, + keywords = keywords, + logo = logo, + status = status, + selfjoin = selfjoin + ) {} +} diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/AdminEntities.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UserPayloadsADM.scala similarity index 63% rename from webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/AdminEntities.scala rename to webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UserPayloadsADM.scala index 30cd8275d0..457b32923d 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/AdminEntities.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/UserPayloadsADM.scala @@ -2,31 +2,10 @@ package org.knora.webapi.messages.admin.responder.usersmessages import org.knora.webapi.IRI -sealed trait ValidationError -case object InvalidUsername extends ValidationError -case object InvalidEmail extends ValidationError -case object InvalidGivenOrFamilyName extends ValidationError -case object InvalidPassword extends ValidationError -case object InvalidLanguageCode extends ValidationError - -trait UserCreatePayloadTraitADM { - def create( - id: Option[IRI], - username: Username, - email: Email, - givenName: GivenName, - familyName: FamilyName, - password: Password, - status: Status, - lang: LanguageCode, - systemAdmin: SystemAdmin - ): UserCreatePayloadADM -} - /** * User entity representing the payload for the create user request */ -sealed abstract case class UserCreatePayloadADM( +sealed abstract case class UserCreatePayloadADM private ( id: Option[IRI], username: Option[Username], email: Option[Email], @@ -41,10 +20,10 @@ sealed abstract case class UserCreatePayloadADM( systemAdmin: Option[SystemAdmin] ) -object UserCreatePayloadADM extends UserCreatePayloadTraitADM { +object UserCreatePayloadADM { /** The create constructor needs all attributes but id which is optional */ - override def create( + def create( id: Option[IRI] = None, username: Username, email: Email, diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/ValueObjects.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/ValueObjects.scala deleted file mode 100644 index 70a807dee1..0000000000 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/ValueObjects.scala +++ /dev/null @@ -1,146 +0,0 @@ -package org.knora.webapi.messages.admin.responder.usersmessages - -import org.knora.webapi.LanguageCodes -import org.knora.webapi.exceptions.BadRequestException - -import scala.util.matching.Regex - -trait ValueObject[T, K] { - def create(value: K): Either[Throwable, T] -} - -/** - * Username value object. - */ -sealed abstract case class Username(value: String) - -object Username extends ValueObject[Username, String] { - - /** - * 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._]+(? - Right(new Username(value) {}) - case None => Left(BadRequestException("Invalid username")) - } - } - -} - -/** - * Email value object. - */ -sealed abstract case class Email(value: String) - -object Email extends ValueObject[Email, String] { - - private val EmailRegex: Regex = """^.+@.+$""".r // TODO use proper validation - - override def create(value: String): Either[Throwable, Email] = - if (value.isEmpty) { - Left(BadRequestException("Missing email")) - } else { - EmailRegex.findFirstIn(value) match { - case Some(value) => Right(new Email(value) {}) - case None => Left(BadRequestException("Invalid email")) - } - } -} - -/** - * Password value object. - */ -sealed abstract case class Password(value: String) - -object Password extends ValueObject[Password, String] { - - private val PasswordRegex: Regex = """^[\s\S]*$""".r //TODO: add password validation - - override def create(value: String): Either[Throwable, Password] = - if (value.isEmpty) { - Left(BadRequestException("Missing password")) - } else { - PasswordRegex.findFirstIn(value) match { - case Some(value) => Right(new Password(value) {}) - case None => Left(BadRequestException("Invalid password")) - } - } -} - -/** - * GivenName value object. - */ -sealed abstract case class GivenName(value: String) - -object GivenName extends ValueObject[GivenName, String] { - // TODO use proper validation for value - override def create(value: String): Either[Throwable, GivenName] = - if (value.isEmpty) { - Left(BadRequestException("Missing given name")) - } else { - Right(new GivenName(value) {}) - } -} - -/** - * FamilyName value object. - */ -sealed abstract case class FamilyName(value: String) - -object FamilyName extends ValueObject[FamilyName, String] { - // TODO use proper validation for value - override def create(value: String): Either[Throwable, FamilyName] = - if (value.isEmpty) { - Left(BadRequestException("Missing family name")) - } else { - Right(new FamilyName(value) {}) - } -} - -/** - * LanguageCode value object. - */ -sealed abstract case class LanguageCode(value: String) - -object LanguageCode extends ValueObject[LanguageCode, String] { - override def create(value: String): Either[Throwable, LanguageCode] = - if (value.isEmpty) { - Left(BadRequestException("Missing language code")) - } else if (!LanguageCodes.SupportedLanguageCodes.contains(value)) { - Left(BadRequestException("Invalid language code")) - } else { - Right(new LanguageCode(value) {}) - } -} - -/** - * Status value object. - */ -sealed abstract case class Status(value: Boolean) - -object Status extends ValueObject[Status, Boolean] { - override def create(value: Boolean): Either[Throwable, Status] = - Right(new Status(value) {}) -} - -/** - * SystemAdmin value object. - */ -sealed abstract case class SystemAdmin(value: Boolean) - -object SystemAdmin extends ValueObject[SystemAdmin, Boolean] { - override def create(value: Boolean): Either[Throwable, SystemAdmin] = - Right(new SystemAdmin(value) {}) -} diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/ValueObjectsADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/ValueObjectsADM.scala new file mode 100644 index 0000000000..015a8e7131 --- /dev/null +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/usersmessages/ValueObjectsADM.scala @@ -0,0 +1,247 @@ +package org.knora.webapi.messages.admin.responder.usersmessages + +import org.knora.webapi.LanguageCodes +import org.knora.webapi.exceptions.{AssertionException, BadRequestException} +import org.knora.webapi.messages.StringFormatter +import org.knora.webapi.messages.store.triplestoremessages.StringLiteralV2 + +import scala.util.matching.Regex + +//trait ValueObject[T, K] { +// val stringFormatter = StringFormatter.getGeneralInstance +// def create(value: K): Either[Throwable, T] +//} + +/** + * Username value object. + */ +sealed abstract case class Username private (value: String) + +object Username { + + /** + * 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._]+(? + Right(new Username(value) {}) + case None => Left(BadRequestException("Invalid username")) + } + } + +} + +/** + * Email value object. + */ +sealed abstract case class Email private (value: String) + +object Email { + private val EmailRegex: Regex = """^.+@.+$""".r // TODO use proper validation + + def create(value: String): Either[Throwable, Email] = + if (value.isEmpty) { + Left(BadRequestException("Missing email")) + } else { + EmailRegex.findFirstIn(value) match { + case Some(value) => Right(new Email(value) {}) + case None => Left(BadRequestException("Invalid email")) + } + } +} + +/** + * Password value object. + */ +sealed abstract case class Password private (value: String) + +object Password { + private val PasswordRegex: Regex = """^[\s\S]*$""".r //TODO: add password validation + + def create(value: String): Either[Throwable, Password] = + if (value.isEmpty) { + Left(BadRequestException("Missing password")) + } else { + PasswordRegex.findFirstIn(value) match { + case Some(value) => Right(new Password(value) {}) + case None => Left(BadRequestException("Invalid password")) + } + } +} + +/** + * GivenName value object. + */ +sealed abstract case class GivenName private (value: String) + +object GivenName { + // TODO use proper validation for value + def create(value: String): Either[Throwable, GivenName] = + if (value.isEmpty) { + Left(BadRequestException("Missing given name")) + } else { + Right(new GivenName(value) {}) + } +} + +/** + * FamilyName value object. + */ +sealed abstract case class FamilyName private (value: String) + +object FamilyName { + // TODO use proper validation for value + def create(value: String): Either[Throwable, FamilyName] = + if (value.isEmpty) { + Left(BadRequestException("Missing family name")) + } else { + Right(new FamilyName(value) {}) + } +} + +/** + * LanguageCode value object. + */ +sealed abstract case class LanguageCode private (value: String) + +object LanguageCode { + def create(value: String): Either[Throwable, LanguageCode] = + if (value.isEmpty) { + Left(BadRequestException("Missing language code")) + } else if (!LanguageCodes.SupportedLanguageCodes.contains(value)) { + Left(BadRequestException("Invalid language code")) + } else { + Right(new LanguageCode(value) {}) + } +} + +/** + * Status value object. + */ +sealed abstract case class Status private (value: Boolean) + +object Status { + def create(value: Boolean): Either[Throwable, Status] = + Right(new Status(value) {}) +} + +/** + * SystemAdmin value object. + */ +sealed abstract case class SystemAdmin private (value: Boolean) + +object SystemAdmin { + def create(value: Boolean): Either[Throwable, SystemAdmin] = + Right(new SystemAdmin(value) {}) +} + +/** + * Project shortcode value object. + */ +sealed abstract case class Shortcode private (value: String) + +object Shortcode { + val stringFormatter = StringFormatter.getGeneralInstance + + def create(value: String): Either[Throwable, Shortcode] = + if (value.isEmpty) { + Left(BadRequestException("Missing shortcode")) + } else { + val shortcode: String = stringFormatter.validateProjectShortcode(value, throw AssertionException("not valid")) + Right(new Shortcode(shortcode) {}) + } +} + +/** + * Project shortname value object. + */ +sealed abstract case class Shortname private (value: String) + +object Shortname { + val stringFormatter = StringFormatter.getGeneralInstance + + def create(value: String): Either[Throwable, Shortname] = + if (value.isEmpty) { + Left(BadRequestException("Missing shortname")) + } else { + val shortname = stringFormatter.validateAndEscapeProjectShortname(value, throw AssertionException("not valid")) + Right(new Shortname(shortname) {}) + } +} + +/** + * Project longname value object. + */ +sealed abstract case class Longname private (value: String) + +object Longname { + def create(value: String): Either[Throwable, Longname] = + if (value.isEmpty) { + Left(BadRequestException("Missing long name")) + } else { + Right(new Longname(value) {}) + } +} + +/** + * Project description value object. + */ +sealed abstract case class Description private (value: Seq[StringLiteralV2]) + +object Description { + def create(value: Seq[StringLiteralV2]): Either[Throwable, Description] = + if (value.isEmpty) { + Left(BadRequestException("Missing description")) + } else { + Right(new Description(value) {}) + } +} + +/** + * Project keywords value object. + */ +sealed abstract case class Keywords private (value: Seq[String]) + +object Keywords { + def create(value: Seq[String]): Either[Throwable, Keywords] = + if (value.isEmpty) { + Left(BadRequestException("Missing keywords")) + } else { + Right(new Keywords(value) {}) + } +} + +/** + * Project logo value object. + */ +sealed abstract case class Logo private (value: String) + +object Logo { + def create(value: String): Either[Throwable, Logo] = + if (value.isEmpty) { + Left(BadRequestException("Missing logo")) + } else { + Right(new Logo(value) {}) + } +} + +/** + * selfjoin value object. + */ +sealed abstract case class Selfjoin private (value: Boolean) + +object Selfjoin { + def create(value: Boolean): Either[Throwable, Selfjoin] = + Right(new Selfjoin(value) {}) +} diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala index 9458dd34bd..f0e7ffb5aa 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala @@ -22,7 +22,6 @@ package org.knora.webapi.responders.admin import java.io.{BufferedInputStream, BufferedOutputStream} import java.nio.file.{Files, Path} import java.util.UUID - import akka.http.scaladsl.util.FastFuture import akka.pattern._ import org.knora.webapi._ @@ -33,13 +32,13 @@ import org.knora.webapi.instrumentation.InstrumentationSupport import org.knora.webapi.messages.IriConversions._ import org.knora.webapi.messages.admin.responder.projectsmessages._ import org.knora.webapi.messages.admin.responder.usersmessages.{ + ProjectCreatePayloadADM, UserADM, UserGetADM, UserIdentifierADM, UserInformationTypeADM } import org.knora.webapi.messages.admin.responder.permissionsmessages._ - import org.knora.webapi.messages.store.cacheservicemessages.{ CacheServiceGetProjectADM, CacheServicePutProjectADM, @@ -1004,7 +1003,7 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo * @throws BadRequestException in the case when the shortcode is invalid. */ private def projectCreateRequestADM( - createProjectRequest: CreateProjectApiRequestADM, + createProjectRequest: ProjectCreatePayloadADM, featureFactoryConfig: FeatureFactoryConfig, requestingUser: UserADM, apiRequestID: UUID @@ -1084,25 +1083,25 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo } yield () def projectCreateTask( - createProjectRequest: CreateProjectApiRequestADM, + createProjectRequest: ProjectCreatePayloadADM, requestingUser: UserADM ): Future[ProjectOperationResponseADM] = for { // check if the supplied shortname is unique - shortnameExists <- projectByShortnameExists(createProjectRequest.shortname) + shortnameExists <- projectByShortnameExists(createProjectRequest.shortname.value) _ = if (shortnameExists) { throw DuplicateValueException( - s"Project with the shortname: '${createProjectRequest.shortname}' already exists" + s"Project with the shortname: '${createProjectRequest.shortname.value}' already exists" ) } // check if the optionally supplied shortcode is valid and unique - shortcodeExists <- projectByShortcodeExists(createProjectRequest.shortcode) + shortcodeExists <- projectByShortcodeExists(createProjectRequest.shortcode.value) _ = if (shortcodeExists) { throw DuplicateValueException( - s"Project with the shortcode: '${createProjectRequest.shortcode}' already exists" + s"Project with the shortcode: '${createProjectRequest.shortcode.value}' already exists" ) } @@ -1116,27 +1115,37 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo customProjectIri: Option[SmartIri] = createProjectRequest.id.map(iri => iri.toSmartIri) newProjectIRI: IRI <- checkOrCreateEntityIri( customProjectIri, - stringFormatter.makeRandomProjectIri(createProjectRequest.shortcode) + stringFormatter.makeRandomProjectIri(createProjectRequest.shortcode.value) ) + maybeLongname = createProjectRequest.longname match { + case Some(value) => Some(value.value) + case None => None + } + + maybeLogo = createProjectRequest.logo match { + case Some(value) => Some(value.value) + case None => None + } + createNewProjectSparqlString = org.knora.webapi.messages.twirl.queries.sparql.admin.txt .createNewProject( adminNamedGraphIri = OntologyConstants.NamedGraphs.AdminNamedGraph, triplestore = settings.triplestoreType, projectIri = newProjectIRI, projectClassIri = OntologyConstants.KnoraAdmin.KnoraProject, - shortname = createProjectRequest.shortname, - shortcode = createProjectRequest.shortcode, - maybeLongname = createProjectRequest.longname, - maybeDescriptions = if (createProjectRequest.description.nonEmpty) { - Some(createProjectRequest.description) + shortname = createProjectRequest.shortname.value, + shortcode = createProjectRequest.shortcode.value, + maybeLongname = maybeLongname, + maybeDescriptions = if (createProjectRequest.description.value.nonEmpty) { + Some(createProjectRequest.description.value) } else None, - maybeKeywords = if (createProjectRequest.keywords.nonEmpty) { - Some(createProjectRequest.keywords) + maybeKeywords = if (createProjectRequest.keywords.value.nonEmpty) { + Some(createProjectRequest.keywords.value) } else None, - maybeLogo = createProjectRequest.logo, - status = createProjectRequest.status, - hasSelfJoinEnabled = createProjectRequest.selfjoin + maybeLogo = maybeLogo, + status = createProjectRequest.status.value, + hasSelfJoinEnabled = createProjectRequest.selfjoin.value ) .toString 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 fb1df54acc..8a5c4b1a73 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 @@ -21,7 +21,6 @@ package org.knora.webapi.routing.admin import java.nio.file.Files import java.util.UUID - import akka.Done import akka.http.scaladsl.model.headers.{ContentDispositionTypes, `Content-Disposition`} import akka.http.scaladsl.model.{ContentTypes, HttpEntity} @@ -32,12 +31,24 @@ import akka.stream.IOResult import akka.stream.scaladsl.{FileIO, Source} import akka.util.ByteString import io.swagger.annotations._ + import javax.ws.rs.Path import org.knora.webapi.IRI import org.knora.webapi.annotation.ApiMayChange import org.knora.webapi.exceptions.BadRequestException import org.knora.webapi.feature.FeatureFactoryConfig import org.knora.webapi.messages.admin.responder.projectsmessages._ +import org.knora.webapi.messages.admin.responder.usersmessages.{ + Description, + Keywords, + Logo, + Longname, + ProjectCreatePayloadADM, + Selfjoin, + Shortcode, + Shortname, + Status +} import org.knora.webapi.routing.{Authenticator, KnoraRoute, KnoraRouteData, RouteUtilADM} import scala.concurrent.Future @@ -141,13 +152,36 @@ class ProjectsRouteADM(routeData: KnoraRouteData) private def addProject(featureFactoryConfig: FeatureFactoryConfig): Route = path(ProjectsBasePath) { post { entity(as[CreateProjectApiRequestADM]) { apiRequest => requestContext => + val maybeLongname: Option[Longname] = apiRequest.longname match { + case Some(value) => Some(Longname.create(value).fold(error => throw error, value => value)) + case None => None + } + + val maybeLogo: Option[Logo] = apiRequest.logo match { + case Some(value) => Some(Logo.create(value).fold(error => throw error, value => value)) + case None => None + } + + val projectCreatePayload: ProjectCreatePayloadADM = + ProjectCreatePayloadADM.create( + id = stringFormatter + .validateAndEscapeOptionalProjectIri(apiRequest.id, throw BadRequestException(s"Invalid user IRI")), + shortname = Shortname.create(apiRequest.shortname).fold(error => throw error, value => value), + shortcode = Shortcode.create(apiRequest.shortcode).fold(error => throw error, value => value), + longname = maybeLongname, + description = Description.create(apiRequest.description).fold(error => throw error, value => value), + keywords = Keywords.create(apiRequest.keywords).fold(error => throw error, value => value), + logo = maybeLogo, + status = Status.create(apiRequest.status).fold(error => throw error, value => value), + selfjoin = Selfjoin.create(apiRequest.selfjoin).fold(error => throw error, value => value) + ) val requestMessage: Future[ProjectCreateRequestADM] = for { requestingUser <- getUserADM( requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) } yield ProjectCreateRequestADM( - createRequest = apiRequest.validateAndEscape, + createRequest = projectCreatePayload, featureFactoryConfig = featureFactoryConfig, requestingUser = requestingUser, apiRequestID = UUID.randomUUID() @@ -229,10 +263,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - checkedProjectIri = stringFormatter.validateAndEscapeProjectIri( - value, - throw BadRequestException(s"Invalid project IRI $value") - ) + checkedProjectIri = + stringFormatter.validateAndEscapeProjectIri(value, throw BadRequestException(s"Invalid project IRI $value")) } yield ProjectGetRequestADM( identifier = ProjectIdentifierADM(maybeIri = Some(checkedProjectIri)), @@ -400,10 +432,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - checkedProjectIri = stringFormatter.validateAndEscapeProjectIri( - value, - throw BadRequestException(s"Invalid project IRI $value") - ) + checkedProjectIri = + stringFormatter.validateAndEscapeProjectIri(value, throw BadRequestException(s"Invalid project IRI $value")) } yield ProjectMembersGetRequestADM( identifier = ProjectIdentifierADM(maybeIri = Some(checkedProjectIri)), @@ -502,10 +532,8 @@ class ProjectsRouteADM(routeData: KnoraRouteData) requestContext = requestContext, featureFactoryConfig = featureFactoryConfig ) - checkedProjectIri = stringFormatter.validateAndEscapeProjectIri( - value, - throw BadRequestException(s"Invalid project IRI $value") - ) + checkedProjectIri = + stringFormatter.validateAndEscapeProjectIri(value, throw BadRequestException(s"Invalid project IRI $value")) } yield ProjectAdminMembersGetRequestADM( identifier = ProjectIdentifierADM(maybeIri = Some(checkedProjectIri)), 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 bb29eddfc0..205c7c15af 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 @@ -24,12 +24,12 @@ package org.knora.webapi.responders.admin import java.util.UUID - import akka.actor.Status.Failure import akka.testkit.ImplicitSender import com.typesafe.config.{Config, ConfigFactory} import org.knora.webapi._ import org.knora.webapi.exceptions.{BadRequestException, DuplicateValueException, NotFoundException} + import org.knora.webapi.messages.{OntologyConstants, StringFormatter} import org.knora.webapi.messages.admin.responder.permissionsmessages.{ AdministrativePermissionADM, @@ -44,7 +44,18 @@ import org.knora.webapi.messages.admin.responder.permissionsmessages.{ PermissionByIriGetRequestADM } import org.knora.webapi.messages.admin.responder.projectsmessages._ -import org.knora.webapi.messages.admin.responder.usersmessages.UserInformationTypeADM +import org.knora.webapi.messages.admin.responder.usersmessages.{ + Description, + Keywords, + Logo, + Longname, + ProjectCreatePayloadADM, + Selfjoin, + Shortcode, + Shortname, + Status, + UserInformationTypeADM +} import org.knora.webapi.messages.store.triplestoremessages._ import org.knora.webapi.sharedtestdata.SharedTestDataADM import org.knora.webapi.util.MutableTestIri @@ -219,16 +230,19 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) "CREATE a project and return the project info if the supplied shortname is unique" in { val shortCode = "111c" responderManager ! ProjectCreateRequestADM( - CreateProjectApiRequestADM( - shortname = "newproject", - shortcode = shortCode, // lower case - longname = Some("project longname"), - description = Seq(StringLiteralV2(value = "project description", language = Some("en"))), - keywords = Seq("keywords"), - logo = Some("/fu/bar/baz.jpg"), - status = true, - selfjoin = false - ).validateAndEscape, + createRequest = ProjectCreatePayloadADM + .create( + shortname = Shortname.create("newproject").fold(error => throw error, value => value), + shortcode = Shortcode.create(shortCode).fold(error => throw error, value => value), // lower case + longname = Some(Longname.create("project longname").fold(error => throw error, value => value)), + description = Description + .create(Seq(StringLiteralV2(value = "project description", language = Some("en")))) + .fold(error => throw error, value => value), + keywords = Keywords.create(Seq("keywords")).fold(error => throw error, value => value), + logo = Some(Logo.create("/fu/bar/baz.jpg").fold(error => throw error, value => value)), + status = Status.create(true).fold(error => throw error, value => value), + selfjoin = Selfjoin.create(false).fold(error => throw error, value => value) + ), featureFactoryConfig = defaultFeatureFactoryConfig, SharedTestDataADM.rootUser, UUID.randomUUID() @@ -315,16 +329,19 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) "CREATE a project and return the project info if the supplied shortname and shortcode is unique" in { responderManager ! ProjectCreateRequestADM( - CreateProjectApiRequestADM( - shortname = "newproject2", - shortcode = "1112", - longname = Some("project longname"), - description = Seq(StringLiteralV2(value = "project description", language = Some("en"))), - keywords = Seq("keywords"), - logo = Some("/fu/bar/baz.jpg"), - status = true, - selfjoin = false - ).validateAndEscape, + createRequest = ProjectCreatePayloadADM + .create( + shortname = Shortname.create("newproject2").fold(error => throw error, value => value), + shortcode = Shortcode.create("1112").fold(error => throw error, value => value), // lower case + longname = Some(Longname.create("project longname").fold(error => throw error, value => value)), + description = Description + .create(Seq(StringLiteralV2(value = "project description", language = Some("en")))) + .fold(error => throw error, value => value), + keywords = Keywords.create(Seq("keywords")).fold(error => throw error, value => value), + logo = Some(Logo.create("/fu/bar/baz.jpg").fold(error => throw error, value => value)), + status = Status.create(true).fold(error => throw error, value => value), + selfjoin = Selfjoin.create(false).fold(error => throw error, value => value) + ), featureFactoryConfig = defaultFeatureFactoryConfig, SharedTestDataADM.rootUser, UUID.randomUUID() @@ -346,16 +363,19 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) val descriptionWithSpecialCharacter = "project \\\"description\\\"" val keywordWithSpecialCharacter = "new \\\"keyword\\\"" responderManager ! ProjectCreateRequestADM( - CreateProjectApiRequestADM( - shortname = "project_with_character", - shortcode = "1312", - longname = Some(longnameWithSpecialCharacter), - description = Seq(StringLiteralV2(value = descriptionWithSpecialCharacter, language = Some("en"))), - keywords = Seq(keywordWithSpecialCharacter), - logo = Some("/fu/bar/baz.jpg"), - status = true, - selfjoin = false - ).validateAndEscape, + createRequest = ProjectCreatePayloadADM + .create( + shortname = Shortname.create("project_with_character").fold(error => throw error, value => value), + shortcode = Shortcode.create("1312").fold(error => throw error, value => value), // lower case + longname = Some(Longname.create(longnameWithSpecialCharacter).fold(error => throw error, value => value)), + description = Description + .create(Seq(StringLiteralV2(value = descriptionWithSpecialCharacter, language = Some("en")))) + .fold(error => throw error, value => value), + keywords = Keywords.create(Seq(keywordWithSpecialCharacter)).fold(error => throw error, value => value), + logo = Some(Logo.create("/fu/bar/baz.jpg").fold(error => throw error, value => value)), + status = Status.create(true).fold(error => throw error, value => value), + selfjoin = Selfjoin.create(false).fold(error => throw error, value => value) + ), featureFactoryConfig = defaultFeatureFactoryConfig, SharedTestDataADM.rootUser, UUID.randomUUID() @@ -377,16 +397,19 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) "return a 'DuplicateValueException' during creation if the supplied project shortname is not unique" in { responderManager ! ProjectCreateRequestADM( - CreateProjectApiRequestADM( - shortname = "newproject", - shortcode = "111C", - longname = Some("project longname"), - description = Seq(StringLiteralV2(value = "project description", language = Some("en"))), - keywords = Seq("keywords"), - logo = Some("/fu/bar/baz.jpg"), - status = true, - selfjoin = false - ).validateAndEscape, + createRequest = ProjectCreatePayloadADM + .create( + shortname = Shortname.create("newproject").fold(error => throw error, value => value), + shortcode = Shortcode.create("111C").fold(error => throw error, value => value), // lower case + longname = Some(Longname.create("project longname").fold(error => throw error, value => value)), + description = Description + .create(Seq(StringLiteralV2(value = "project description", language = Some("en")))) + .fold(error => throw error, value => value), + keywords = Keywords.create(Seq("keywords")).fold(error => throw error, value => value), + logo = Some(Logo.create("/fu/bar/baz.jpg").fold(error => throw error, value => value)), + status = Status.create(true).fold(error => throw error, value => value), + selfjoin = Selfjoin.create(false).fold(error => throw error, value => value) + ), featureFactoryConfig = defaultFeatureFactoryConfig, SharedTestDataADM.rootUser, UUID.randomUUID() @@ -396,16 +419,19 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) "return a 'DuplicateValueException' during creation if the supplied project shortname is unique but the shortcode is not" in { responderManager ! ProjectCreateRequestADM( - CreateProjectApiRequestADM( - shortname = "newproject3", - shortcode = "111C", - longname = Some("project longname"), - description = Seq(StringLiteralV2(value = "project description", language = Some("en"))), - keywords = Seq("keywords"), - logo = Some("/fu/bar/baz.jpg"), - status = true, - selfjoin = false - ).validateAndEscape, + createRequest = ProjectCreatePayloadADM + .create( + shortname = Shortname.create("newproject3").fold(error => throw error, value => value), + shortcode = Shortcode.create("111C").fold(error => throw error, value => value), // lower case + longname = Some(Longname.create("project longname").fold(error => throw error, value => value)), + description = Description + .create(Seq(StringLiteralV2(value = "project description", language = Some("en")))) + .fold(error => throw error, value => value), + keywords = Keywords.create(Seq("keywords")).fold(error => throw error, value => value), + logo = Some(Logo.create("/fu/bar/baz.jpg").fold(error => throw error, value => value)), + status = Status.create(true).fold(error => throw error, value => value), + selfjoin = Selfjoin.create(false).fold(error => throw error, value => value) + ), featureFactoryConfig = defaultFeatureFactoryConfig, SharedTestDataADM.rootUser, UUID.randomUUID()