Skip to content

Commit

Permalink
refactor: Move code from UsersResponder to UserService and UserRestSe…
Browse files Browse the repository at this point in the history
…rvice (#3069)
  • Loading branch information
seakayone committed Feb 28, 2024
1 parent 5345350 commit e78a106
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 334 deletions.
Expand Up @@ -32,6 +32,7 @@ import org.knora.webapi.slice.admin.api.UsersEndpoints.Requests.BasicUserInforma
import org.knora.webapi.slice.admin.api.UsersEndpoints.Requests.PasswordChangeRequest
import org.knora.webapi.slice.admin.api.UsersEndpoints.Requests.UserCreateRequest
import org.knora.webapi.slice.admin.api.service.UsersRestService
import org.knora.webapi.slice.admin.domain.model.KnoraProject.ProjectIri
import org.knora.webapi.slice.admin.domain.model.Username
import org.knora.webapi.slice.admin.domain.model.*
import org.knora.webapi.slice.admin.domain.service.UserService
Expand Down Expand Up @@ -61,6 +62,27 @@ class UsersResponderSpec extends CoreSpec with ImplicitSender {
def getAllUsers(requestingUser: User): ZIO[UsersRestService, Throwable, UsersGetResponseADM] =
ZIO.serviceWithZIO[UsersRestService](_.getAllUsers(requestingUser))

def addGroupToUserIsInGroup(
requestingUser: User,
userIri: UserIri,
groupIri: GroupIri
): ZIO[UsersRestService, Throwable, UserResponseADM] =
ZIO.serviceWithZIO[UsersRestService](_.addGroupToUserIsInGroup(requestingUser, userIri, groupIri))

def addProjectToUserIsInProject(
requestingUser: User,
userIri: UserIri,
projectIri: ProjectIri
): ZIO[UsersRestService, Throwable, UserResponseADM] =
ZIO.serviceWithZIO[UsersRestService](_.addProjectToUserIsInProject(requestingUser, userIri, projectIri))

def addProjectToUserIsInProjectAdminGroup(
requestingUser: User,
userIri: UserIri,
projectIri: ProjectIri
): ZIO[UsersRestService, Throwable, UserResponseADM] =
ZIO.serviceWithZIO[UsersRestService](_.addProjectToUserIsInProjectAdminGroup(requestingUser, userIri, projectIri))

"The UsersRestService" when {
"calling getAllUsers" should {
"with a SystemAdmin should return all real users" in {
Expand Down Expand Up @@ -216,7 +238,7 @@ class UsersResponderSpec extends CoreSpec with ImplicitSender {
"asked to update a user" should {
"UPDATE the user's basic information" in {
/* User information is updated by the user */
val response1: UserOperationResponseADM = UnsafeZioRun.runOrThrow(
val response1 = UnsafeZioRun.runOrThrow(
UsersResponder.changeBasicUserInformationADM(
SharedTestDataADM.normalUser.userIri,
BasicUserInformationChangeRequest(givenName = Some(GivenName.unsafeFrom("Donald"))),
Expand All @@ -227,7 +249,7 @@ class UsersResponderSpec extends CoreSpec with ImplicitSender {
response1.user.givenName should equal("Donald")

/* User information is updated by a system admin */
val response2: UserOperationResponseADM = UnsafeZioRun.runOrThrow(
val response2 = UnsafeZioRun.runOrThrow(
UsersResponder.changeBasicUserInformationADM(
SharedTestDataADM.normalUser.userIri,
BasicUserInformationChangeRequest(familyName = Some(FamilyName.unsafeFrom("Duck"))),
Expand All @@ -238,7 +260,7 @@ class UsersResponderSpec extends CoreSpec with ImplicitSender {
response2.user.familyName should equal("Duck")

/* User information is updated by a system admin */
val response3: UserOperationResponseADM = UnsafeZioRun.runOrThrow(
val response3 = UnsafeZioRun.runOrThrow(
UsersResponder.changeBasicUserInformationADM(
SharedTestDataADM.normalUser.userIri,
BasicUserInformationChangeRequest(
Expand Down Expand Up @@ -375,9 +397,7 @@ class UsersResponderSpec extends CoreSpec with ImplicitSender {
membershipsBeforeUpdate.projects should equal(Chunk.empty)

// add user to images project (00FF)
UnsafeZioRun.runOrThrow(
UsersResponder.addProjectToUserIsInProject(normalUser.userIri, imagesProject.projectIri, UUID.randomUUID())
)
UnsafeZioRun.runOrThrow(addProjectToUserIsInProject(rootUser, normalUser.userIri, imagesProject.projectIri))

val membershipsAfterUpdate = UnsafeZioRun.runOrThrow(getProjectMemberShipsByUserIri(normalUser.userIri))
membershipsAfterUpdate.projects.map(_.id) should equal(Chunk(imagesProject.id))
Expand All @@ -397,13 +417,7 @@ class UsersResponderSpec extends CoreSpec with ImplicitSender {
membershipsBeforeUpdate.projects.map(_.id).sorted should equal(Seq(imagesProject.id).sorted)

// add user to images project (00FF)
UnsafeZioRun.runOrThrow(
UsersResponder.addProjectToUserIsInProject(
normalUser.userIri,
incunabulaProject.projectIri,
UUID.randomUUID()
)
)
UnsafeZioRun.runOrThrow(addProjectToUserIsInProject(rootUser, normalUser.userIri, incunabulaProject.projectIri))

val membershipsAfterUpdate = UnsafeZioRun.runOrThrow(getProjectMemberShipsByUserIri(normalUser.userIri))
membershipsAfterUpdate.projects.map(_.id).sorted should equal(
Expand All @@ -428,11 +442,7 @@ class UsersResponderSpec extends CoreSpec with ImplicitSender {

// add user as project admin to images project
UnsafeZioRun.runOrThrow(
UsersResponder.addProjectToUserIsInProjectAdminGroup(
normalUser.userIri,
imagesProject.projectIri,
UUID.randomUUID()
)
addProjectToUserIsInProjectAdminGroup(rootUser, normalUser.userIri, imagesProject.projectIri)
)

// verify that the user has been added as project admin to the images project
Expand Down Expand Up @@ -475,11 +485,7 @@ class UsersResponderSpec extends CoreSpec with ImplicitSender {

// try to add user as project admin to images project (expected to fail because he is not a member of the project)
val exit = UnsafeZioRun.run(
UsersResponder.addProjectToUserIsInProjectAdminGroup(
normalUser.userIri,
imagesProject.projectIri,
UUID.randomUUID()
)
addProjectToUserIsInProjectAdminGroup(rootUser, normalUser.userIri, imagesProject.projectIri)
)
assertFailsWithA[BadRequestException](
exit,
Expand All @@ -494,17 +500,11 @@ class UsersResponderSpec extends CoreSpec with ImplicitSender {
membershipsBeforeUpdate.projects should equal(Seq())

// add user as project member to images project
UnsafeZioRun.runOrThrow(
UsersResponder.addProjectToUserIsInProject(normalUser.userIri, imagesProject.projectIri, UUID.randomUUID())
)
UnsafeZioRun.runOrThrow(addProjectToUserIsInProject(rootUser, normalUser.userIri, imagesProject.projectIri))

// add user as project admin to images project
UnsafeZioRun.runOrThrow(
UsersResponder.addProjectToUserIsInProjectAdminGroup(
normalUser.userIri,
imagesProject.projectIri,
UUID.randomUUID()
)
addProjectToUserIsInProjectAdminGroup(rootUser, normalUser.userIri, imagesProject.projectIri)
)

// get the updated project admin memberships (should contain images project)
Expand Down Expand Up @@ -553,9 +553,7 @@ class UsersResponderSpec extends CoreSpec with ImplicitSender {
val membershipsBeforeUpdate = findGroupMembershipsByIri(normalUser.userIri)
membershipsBeforeUpdate should equal(Seq())

UnsafeZioRun.runOrThrow(
UsersResponder.addGroupToUserIsInGroup(normalUser.userIri, imagesReviewerGroup.groupIri, UUID.randomUUID())
)
UnsafeZioRun.runOrThrow(addGroupToUserIsInGroup(rootUser, normalUser.userIri, imagesReviewerGroup.groupIri))

val membershipsAfterUpdate = findGroupMembershipsByIri(normalUser.userIri)
membershipsAfterUpdate.map(_.id) should equal(Seq(imagesReviewerGroup.id))
Expand Down
5 changes: 4 additions & 1 deletion webapi/src/main/scala/dsp/errors/Errors.scala
Expand Up @@ -11,6 +11,8 @@ import org.apache.commons.lang3.SerializationUtils
import zio.json.DeriveJsonCodec
import zio.json.JsonCodec

import org.knora.webapi.slice.admin.domain.service.UserService.Errors.UserServiceError

/*
How to use and extend these exceptions
Expand Down Expand Up @@ -86,8 +88,9 @@ object RequestRejectedException {
*
* @param message a description of the error.
*/
case class BadRequestException(message: String) extends RequestRejectedException(message)
final case class BadRequestException(message: String) extends RequestRejectedException(message)
object BadRequestException {
def apply(e: UserServiceError): BadRequestException = BadRequestException(e.message)
def invalidQueryParamValue(key: String): BadRequestException =
BadRequestException(s"Invalid value for query parameter '$key'")
def missingQueryParamValue(key: String): BadRequestException =
Expand Down
Expand Up @@ -10,9 +10,6 @@ import spray.json.*

import java.util.UUID

import dsp.errors.BadRequestException
import dsp.valueobjects.LanguageCode
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
Expand Down Expand Up @@ -102,15 +99,6 @@ case class UserGroupMembershipsGetResponseADM(groups: Seq[GroupADM]) extends Adm
def toJsValue: JsValue = UsersADMJsonProtocol.userGroupMembershipsGetResponseADMFormat.write(this)
}

/**
* Represents an answer to a user creating/modifying operation.
*
* @param user the new user profile of the created/modified user.
*/
case class UserOperationResponseADM(user: User) extends AdminKnoraResponseADM {
def toJsValue: JsValue = UsersADMJsonProtocol.userOperationResponseADMFormat.write(this)
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Components of messages

Expand All @@ -135,85 +123,6 @@ object UserInformationType {

}

/**
* Payload used for updating an existing user.
*
* @param username the new username.
* @param email the new email address. Needs to be unique on the server.
* @param givenName the new given name.
* @param familyName the new family name.
* @param status the new status.
* @param lang the new language.
* @param projects the new project memberships list.
* @param projectsAdmin the new projects admin membership list.
* @param groups the new group memberships list.
* @param systemAdmin the new system admin membership
*/
case class UserChangeRequestADM(
username: Option[Username] = None,
email: Option[Email] = None,
givenName: Option[GivenName] = None,
familyName: Option[FamilyName] = None,
status: Option[UserStatus] = None,
lang: Option[LanguageCode] = None,
projects: Option[Seq[IRI]] = None,
projectsAdmin: Option[Seq[IRI]] = None,
groups: Option[Seq[IRI]] = None,
systemAdmin: Option[SystemAdmin] = None
) {

val parametersCount: Int = List(
username,
email,
givenName,
familyName,
status,
lang,
projects,
projectsAdmin,
groups,
systemAdmin
).flatten.size

// something needs to be sent, i.e. everything 'None' is not allowed
if (parametersCount == 0) {
throw BadRequestException("No data sent in API request.")
}

// change status case
if (status.isDefined && parametersCount > 1) {
throw BadRequestException("Too many parameters sent for user status change.")
}

// change system admin membership case
if (systemAdmin.isDefined && parametersCount > 1) {
throw BadRequestException("Too many parameters sent for system admin membership change.")
}

// change project memberships (could also involve changing projectAdmin memberships)
if (
projects.isDefined && projectsAdmin.isDefined && parametersCount > 2 ||
projects.isDefined && projectsAdmin.isEmpty && parametersCount > 1
) {
throw BadRequestException("Too many parameters sent for project membership change.")
}

// change projectAdmin memberships only (without changing project memberships)
if (projectsAdmin.isDefined && projects.isEmpty && parametersCount > 1) {
throw BadRequestException("Too many parameters sent for projectAdmin membership change.")
}

// change group memberships
if (groups.isDefined && parametersCount > 1) {
throw BadRequestException("Too many parameters sent for group membership change.")
}

// change basic user information case
if (parametersCount > 5) {
throw BadRequestException("Too many parameters sent for basic user information change.")
}
}

/**
* Represents an answer to a group membership request.
*
Expand Down Expand Up @@ -247,7 +156,4 @@ object UsersADMJsonProtocol
: RootJsonFormat[UserProjectAdminMembershipsGetResponseADM] = jsonFormat1(UserProjectAdminMembershipsGetResponseADM)
implicit val userGroupMembershipsGetResponseADMFormat: RootJsonFormat[UserGroupMembershipsGetResponseADM] =
jsonFormat1(UserGroupMembershipsGetResponseADM)
implicit val userOperationResponseADMFormat: RootJsonFormat[UserOperationResponseADM] = jsonFormat1(
UserOperationResponseADM
)
}
Expand Up @@ -576,10 +576,10 @@ final case class GroupsResponderADMLive(
requestingUser = KnoraSystemInstances.Users.SystemUser
)

seqOfFutures: Seq[Task[UserOperationResponseADM]] =
seqOfFutures: Seq[Task[UserResponseADM]] =
members.map { (user: User) =>
messageRelay
.ask[UserOperationResponseADM](
.ask[UserResponseADM](
UserGroupMembershipRemoveRequestADM(user.userIri, changedGroup.groupIri, apiRequestID)
)
}
Expand Down

0 comments on commit e78a106

Please sign in to comment.