Skip to content

Commit

Permalink
refactor: Migrate GET /admin/users/iri/<userIri>/*memberships and `…
Browse files Browse the repository at this point in the history
…POST /admin/users` to tapir (#3021)
  • Loading branch information
seakayone authored Feb 7, 2024
1 parent a1e5db1 commit cafbc16
Show file tree
Hide file tree
Showing 13 changed files with 469 additions and 731 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@ import org.knora.webapi.slice.admin.domain.model.User
import org.knora.webapi.util.AkkaHttpUtils
import org.knora.webapi.util.MutableTestIri

import pekko.actor.ActorSystem
import pekko.http.scaladsl.model.*
import pekko.http.scaladsl.model.headers.*
import pekko.http.scaladsl.testkit.RouteTestTimeout
import pekko.http.scaladsl.unmarshalling.Unmarshal

/**
Expand All @@ -43,8 +41,6 @@ class UsersADME2ESpec
with GroupsADMJsonProtocol
with TriplestoreJsonProtocol {

implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(30.seconds)

private val rootUser = SharedTestDataADM.rootUser
private val projectAdminUser = SharedTestDataADM.imagesUser01
private val normalUser = SharedTestDataADM.normalUser
Expand Down Expand Up @@ -314,7 +310,7 @@ class UsersADME2ESpec
HttpEntity(ContentTypes.`application/json`, validUserCreationRequest)
)
val response: HttpResponse = singleAwaitingRequest(request)
response.status should be(StatusCodes.Forbidden)
response.status should be(StatusCodes.Unauthorized)
}

"create a user with the provided custom IRI" in {
Expand Down

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions webapi/src/main/scala/dsp/valueobjects/LanguageCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ import zio.json.JsonCodec
import zio.prelude.Validation

import dsp.errors.ValidationException
import org.knora.webapi.slice.common.Value.StringValue

/**
* LanguageCode value object.
*/
sealed abstract case class LanguageCode private (value: String)
final case class LanguageCode private (value: String) extends AnyVal with StringValue

object LanguageCode { self =>
implicit val codec: JsonCodec[LanguageCode] =
Expand Down Expand Up @@ -43,14 +44,14 @@ object LanguageCode { self =>
} else if (!SupportedLanguageCodes.contains(value)) {
Validation.fail(ValidationException(LanguageCodeErrorMessages.LanguageCodeInvalid(value)))
} else {
Validation.succeed(new LanguageCode(value) {})
Validation.succeed(LanguageCode(value))
}

lazy val en: LanguageCode = new LanguageCode(EN) {}
lazy val de: LanguageCode = new LanguageCode(DE) {}
lazy val fr: LanguageCode = new LanguageCode(FR) {}
lazy val it: LanguageCode = new LanguageCode(IT) {}
lazy val rm: LanguageCode = new LanguageCode(RM) {}
lazy val en: LanguageCode = LanguageCode(EN)
lazy val de: LanguageCode = LanguageCode(DE)
lazy val fr: LanguageCode = LanguageCode(FR)
lazy val it: LanguageCode = LanguageCode(IT)
lazy val rm: LanguageCode = LanguageCode(RM)
}

object LanguageCodeErrorMessages {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1641,16 +1641,6 @@ class StringFormatter private (
s"http://$IriDomain/projects/$uuid"
}

/**
* Creates a new person IRI based on a UUID.
*
* @return a new person IRI.
*/
def makeRandomPersonIri: IRI = {
val knoraPersonUuid = UuidUtil.makeRandomBase64EncodedUuid
s"http://$IriDomain/users/$knoraPersonUuid"
}

/**
* Validates a custom value IRI, throwing [[BadRequestException]] if the IRI is not valid.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import org.knora.webapi.*
import org.knora.webapi.core.RelayedMessage
import org.knora.webapi.messages.ResponderRequest.KnoraRequestADM
import org.knora.webapi.messages.admin.responder.AdminKnoraResponseADM
import org.knora.webapi.messages.admin.responder.KnoraResponseADM
import org.knora.webapi.messages.admin.responder.groupsmessages.GroupADM
import org.knora.webapi.messages.admin.responder.groupsmessages.GroupsADMJsonProtocol
import org.knora.webapi.messages.admin.responder.permissionsmessages.PermissionsADMJsonProtocol
Expand All @@ -31,34 +30,6 @@ import org.knora.webapi.slice.common.ToValidation.validateOptionWithFrom
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// API requests

/**
* Represents an API request payload that asks the Knora API server to create a new user.
*
* @param id the optional IRI of the user to be created (unique).
* @param username the username of the user to be created (unique).
* @param email the email of the user to be created (unique).
* @param givenName the given name of the user to be created.
* @param familyName the family name of the user to be created
* @param password the password of the user to be created.
* @param status the status of the user to be created (active = true, inactive = false).
* @param lang the default language of the user to be created.
* @param systemAdmin the system admin membership.
*/
case class CreateUserApiRequestADM(
id: Option[IRI] = None,
username: String,
email: String,
givenName: String,
familyName: String,
password: String,
status: Boolean,
lang: String,
systemAdmin: Boolean
) {

def toJsValue: JsValue = UsersADMJsonProtocol.createUserApiRequestADMFormat.write(this)
}

/**
* Represents an API request payload that asks the Knora API server to update an existing user. Information that can
* be changed are: user's username, email, given name, family name, language, user status, and system admin membership.
Expand Down Expand Up @@ -124,29 +95,13 @@ case class ChangeUserPasswordApiRequestADM(requesterPassword: Option[String], ne
*/
sealed trait UsersResponderRequestADM extends KnoraRequestADM with RelayedMessage

/**
* Get all information about all users in form of a sequence of [[User]]. Returns an empty sequence if
* no users are found. Administration permission checking is skipped.
*
* @param userInformationTypeADM the extent of the information returned.
* @param requestingUser the user that is making the request.
*/
case class UsersGetADM(
userInformationTypeADM: UserInformationTypeADM = UserInformationTypeADM.Short,
requestingUser: User
) extends UsersResponderRequestADM

/**
* Get all information about all users in form of [[UsersGetResponseADM]]. The UsersResponderRequestADM returns either
* something or a NotFound exception if there are no users found. Administration permission checking is performed.
*
* @param userInformationTypeADM the extent of the information returned.
* @param requestingUser the user initiating the request.
*/
case class UsersGetRequestADM(
userInformationTypeADM: UserInformationTypeADM = UserInformationTypeADM.Short,
requestingUser: User
) extends UsersResponderRequestADM
case class UsersGetRequestADM(requestingUser: User) extends UsersResponderRequestADM

/**
* A message that requests a user's profile by IRI. A successful response will be a [[User]].
Expand All @@ -161,19 +116,6 @@ case class UserGetByIriADM(
requestingUser: User
) extends UsersResponderRequestADM

/**
* Requests the creation of a new user.
*
* @param userCreatePayloadADM the [[UserCreatePayloadADM]] information used for creating the new user.
* @param requestingUser the user creating the new user.
* @param apiRequestID the ID of the API request.
*/
case class UserCreateRequestADM(
userCreatePayloadADM: UserCreatePayloadADM,
requestingUser: User,
apiRequestID: UUID
) extends UsersResponderRequestADM

/**
* Request updating of an existing user.
*
Expand Down Expand Up @@ -234,17 +176,6 @@ case class UserChangeSystemAdminMembershipStatusRequestADM(
apiRequestID: UUID
) extends UsersResponderRequestADM

/**
* Requests user's project memberships.
*
* @param userIri the IRI of the user.
* @param requestingUser the user initiating the request.
*/
case class UserProjectMembershipsGetRequestADM(
userIri: IRI,
requestingUser: User
) extends UsersResponderRequestADM

/**
* Requests adding the user to a project.
*
Expand Down Expand Up @@ -275,19 +206,6 @@ case class UserProjectMembershipRemoveRequestADM(
apiRequestID: UUID
) extends UsersResponderRequestADM

/**
* Requests user's project admin memberships.
*
* @param userIri the IRI of the user.
* @param requestingUser the user initiating the request.
* @param apiRequestID the ID of the API request.
*/
case class UserProjectAdminMembershipsGetRequestADM(
userIri: IRI,
requestingUser: User,
apiRequestID: UUID
) extends UsersResponderRequestADM

/**
* Requests adding the user to a project as project admin.
*
Expand Down Expand Up @@ -318,17 +236,6 @@ case class UserProjectAdminMembershipRemoveRequestADM(
apiRequestID: UUID
) extends UsersResponderRequestADM

/**
* Requests user's group memberships.
*
* @param userIri the IRI of the user.
* @param requestingUser the user initiating the request.
*/
case class UserGroupMembershipsGetRequestADM(
userIri: IRI,
requestingUser: User
) extends UsersResponderRequestADM

/**
* Requests adding the user to a group.
*
Expand Down Expand Up @@ -384,7 +291,7 @@ case class UserResponseADM(user: User) extends AdminKnoraResponseADM {
*
* @param projects a sequence of projects the user is member of.
*/
case class UserProjectMembershipsGetResponseADM(projects: Seq[ProjectADM]) extends KnoraResponseADM {
case class UserProjectMembershipsGetResponseADM(projects: Seq[ProjectADM]) extends AdminKnoraResponseADM {
def toJsValue: JsValue = UsersADMJsonProtocol.userProjectMembershipsGetResponseADMFormat.write(this)
}

Expand All @@ -393,7 +300,7 @@ case class UserProjectMembershipsGetResponseADM(projects: Seq[ProjectADM]) exten
*
* @param projects a sequence of projects the user is member of the project admin group.
*/
case class UserProjectAdminMembershipsGetResponseADM(projects: Seq[ProjectADM]) extends KnoraResponseADM {
case class UserProjectAdminMembershipsGetResponseADM(projects: Seq[ProjectADM]) extends AdminKnoraResponseADM {
def toJsValue: JsValue = UsersADMJsonProtocol.userProjectAdminMembershipsGetResponseADMFormat.write(this)
}

Expand All @@ -402,7 +309,7 @@ case class UserProjectAdminMembershipsGetResponseADM(projects: Seq[ProjectADM])
*
* @param groups a sequence of groups the user is member of.
*/
case class UserGroupMembershipsGetResponseADM(groups: Seq[GroupADM]) extends KnoraResponseADM {
case class UserGroupMembershipsGetResponseADM(groups: Seq[GroupADM]) extends AdminKnoraResponseADM {
def toJsValue: JsValue = UsersADMJsonProtocol.userGroupMembershipsGetResponseADMFormat.write(this)
}

Expand Down Expand Up @@ -575,34 +482,6 @@ case class GroupMembersGetResponseADM(members: Seq[User]) extends AdminKnoraResp
def toJsValue = UsersADMJsonProtocol.groupMembersGetResponseADMFormat.write(this)
}

final case class UserCreatePayloadADM(
id: Option[UserIri] = None,
username: Username,
email: Email,
givenName: GivenName,
familyName: FamilyName,
password: Password,
status: UserStatus,
lang: LanguageCode,
systemAdmin: SystemAdmin
)

object UserCreatePayloadADM {
def make(apiRequest: CreateUserApiRequestADM): Validation[String, UserCreatePayloadADM] =
Validation
.validateWith(
validateOptionWithFrom(apiRequest.id, UserIri.from, a => a),
validateOneWithFrom(apiRequest.username, Username.from, a => a),
validateOneWithFrom(apiRequest.email, Email.from, a => a),
validateOneWithFrom(apiRequest.givenName, GivenName.from, a => a),
validateOneWithFrom(apiRequest.familyName, FamilyName.from, a => a),
validateOneWithFrom(apiRequest.password, Password.from, a => a),
Validation.succeed(UserStatus.from(apiRequest.status)),
LanguageCode.make(apiRequest.lang).mapError(_.getMessage),
Validation.succeed(SystemAdmin.from(apiRequest.systemAdmin))
)(UserCreatePayloadADM.apply)
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// JSON formatting

Expand All @@ -619,18 +498,6 @@ object UsersADMJsonProtocol
implicit val userADMFormat: JsonFormat[User] = jsonFormat12(User)
implicit val groupMembersGetResponseADMFormat: RootJsonFormat[GroupMembersGetResponseADM] =
jsonFormat(GroupMembersGetResponseADM, "members")
implicit val createUserApiRequestADMFormat: RootJsonFormat[CreateUserApiRequestADM] = jsonFormat(
CreateUserApiRequestADM,
"id",
"username",
"email",
"givenName",
"familyName",
"password",
"status",
"lang",
"systemAdmin"
)
implicit val changeUserApiRequestADMFormat: RootJsonFormat[ChangeUserApiRequestADM] =
jsonFormat(ChangeUserApiRequestADM, "username", "email", "givenName", "familyName", "lang", "status", "systemAdmin")
implicit val changeUserPasswordApiRequestADMFormat: RootJsonFormat[ChangeUserPasswordApiRequestADM] = jsonFormat(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import org.knora.webapi.core.RelayedMessage
import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectADM
import org.knora.webapi.messages.admin.responder.projectsmessages.ProjectIdentifierADM
import org.knora.webapi.messages.store.StoreRequest
import org.knora.webapi.slice.admin.domain.model.*
import org.knora.webapi.slice.admin.domain.model.Email
import org.knora.webapi.slice.admin.domain.model.User
import org.knora.webapi.slice.admin.domain.model.UserIri
import org.knora.webapi.slice.admin.domain.model.Username

sealed trait CacheServiceRequest extends StoreRequest with RelayedMessage

Expand Down
Loading

0 comments on commit cafbc16

Please sign in to comment.