diff --git a/backend/src/main/kotlin/org/pathoplexus/backend/controller/GroupManagementController.kt b/backend/src/main/kotlin/org/pathoplexus/backend/controller/GroupManagementController.kt new file mode 100644 index 000000000..0c72c8fa2 --- /dev/null +++ b/backend/src/main/kotlin/org/pathoplexus/backend/controller/GroupManagementController.kt @@ -0,0 +1,31 @@ +package org.pathoplexus.backend.controller + +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.Parameter +import io.swagger.v3.oas.annotations.security.SecurityRequirement +import org.pathoplexus.backend.service.GroupManagementDatabaseService +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.validation.annotation.Validated +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.ResponseStatus +import org.springframework.web.bind.annotation.RestController + +@RestController +@Validated +@SecurityRequirement(name = "bearerAuth") +class GroupManagementController( + private val groupManagementDatabaseService: GroupManagementDatabaseService, +) { + + @Operation(description = "Create a new Group. The user creating the group will be added to the group.") + @ResponseStatus(HttpStatus.NO_CONTENT) + @PostMapping("/groups/{groupName}", produces = [MediaType.APPLICATION_JSON_VALUE]) + fun createNewGroup( + @UsernameFromJwt username: String, + @Parameter( + description = "A new group name", + ) @PathVariable groupName: String, + ) = groupManagementDatabaseService.createNewGroup(groupName, username) +} diff --git a/backend/src/main/kotlin/org/pathoplexus/backend/service/GroupManagementDatabaseService.kt b/backend/src/main/kotlin/org/pathoplexus/backend/service/GroupManagementDatabaseService.kt new file mode 100644 index 000000000..0f938f856 --- /dev/null +++ b/backend/src/main/kotlin/org/pathoplexus/backend/service/GroupManagementDatabaseService.kt @@ -0,0 +1,30 @@ +package org.pathoplexus.backend.service + +import org.jetbrains.exposed.sql.insert +import org.jetbrains.exposed.sql.select +import org.pathoplexus.backend.controller.BadRequestException +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +@Transactional +class GroupManagementDatabaseService { + + fun createNewGroup(groupName: String, username: String) { + val groupEntity = GroupsTable.select { GroupsTable.groupNameColumn eq groupName } + .singleOrNull() + + if (groupEntity != null) { + throw BadRequestException("Group already exists") + } + + GroupsTable.insert { + it[GroupsTable.groupNameColumn] = groupName + } + + UserGroupsTable.insert { + it[UserGroupsTable.userNameColumn] = username + it[UserGroupsTable.groupNameColumn] = groupName + } + } +} diff --git a/backend/src/main/kotlin/org/pathoplexus/backend/service/GroupManagementTables.kt b/backend/src/main/kotlin/org/pathoplexus/backend/service/GroupManagementTables.kt index 1c1b07e86..0c78624eb 100644 --- a/backend/src/main/kotlin/org/pathoplexus/backend/service/GroupManagementTables.kt +++ b/backend/src/main/kotlin/org/pathoplexus/backend/service/GroupManagementTables.kt @@ -3,16 +3,16 @@ package org.pathoplexus.backend.service import org.jetbrains.exposed.sql.Table object GroupsTable : Table("groups_table") { - val groupName = text("group_name") + val groupNameColumn = text("group_name") - override val primaryKey = PrimaryKey(groupName) + override val primaryKey = PrimaryKey(groupNameColumn) } object UserGroupsTable : Table("user_groups_table") { - val userName = text("user_name") - val groupName = text("group_name") references GroupsTable.groupName + val userNameColumn = text("user_name") + val groupNameColumn = text("group_name") references GroupsTable.groupNameColumn - override val primaryKey = PrimaryKey(userName, groupName) + override val primaryKey = PrimaryKey(userNameColumn, groupNameColumn) } data class Group( diff --git a/backend/src/test/kotlin/org/pathoplexus/backend/controller/GroupManagementControllerTest.kt b/backend/src/test/kotlin/org/pathoplexus/backend/controller/GroupManagementControllerTest.kt new file mode 100644 index 000000000..ceed3ae05 --- /dev/null +++ b/backend/src/test/kotlin/org/pathoplexus/backend/controller/GroupManagementControllerTest.kt @@ -0,0 +1,36 @@ +package org.pathoplexus.backend.controller + +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status + +@EndpointTest +class GroupManagementControllerTest(@Autowired private val mockMvc: MockMvc) { + + @Test + fun `GIVEN invalid authorization token THEN returns 401 Unauthorized`() { + expectUnauthorizedResponse(isModifyingRequest = true) { + createNewGroup("testGroup", jwt = it) + } + } + + // TODO(668: verify that the group has one user after creation + @Test + fun `GIVEN WHEN creating a group THEN return success with 204`() { + createNewGroup("testGroup").andExpect(status().isNoContent()) + } + + @Test + fun `GIVEN an existing group WHEN creating a group with same name THEN return 400`() { + createNewGroup("testGroup") + + createNewGroup("testGroup").andExpect(status().isBadRequest()) + } + + private fun createNewGroup(groupName: String, jwt: String? = jwtForDefaultUser) = mockMvc.perform( + post("/groups/$groupName") + .withAuth(jwt), + ) +}