From bb94d4c053311f28ec4cce45721cedd60ed73cc9 Mon Sep 17 00:00:00 2001 From: Chris Rohr <51920+chrisrohr@users.noreply.github.com> Date: Tue, 7 Mar 2023 08:37:59 -0500 Subject: [PATCH 1/3] Begin process of adding users to system Relates to #140 --- .../champagne/dao/DeployableSystemDao.java | 5 ++ .../dao/mappers/SystemUserMapper.java | 19 +++++++ .../champagne/model/DeployableSystem.java | 14 +++++ .../resource/DeployableSystemResource.java | 5 +- ui/src/pages/AdminSystemPage.vue | 51 +++++++++++++++++++ 5 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 service/src/main/java/org/kiwiproject/champagne/dao/mappers/SystemUserMapper.java diff --git a/service/src/main/java/org/kiwiproject/champagne/dao/DeployableSystemDao.java b/service/src/main/java/org/kiwiproject/champagne/dao/DeployableSystemDao.java index 17acc4de..43e570a4 100644 --- a/service/src/main/java/org/kiwiproject/champagne/dao/DeployableSystemDao.java +++ b/service/src/main/java/org/kiwiproject/champagne/dao/DeployableSystemDao.java @@ -8,6 +8,7 @@ import org.jdbi.v3.sqlobject.statement.SqlUpdate; import org.kiwiproject.champagne.dao.mappers.DeployableSystemForUserMapper; import org.kiwiproject.champagne.dao.mappers.DeployableSystemMapper; +import org.kiwiproject.champagne.dao.mappers.SystemUserMapper; import org.kiwiproject.champagne.model.DeployableSystem; import java.util.List; @@ -29,6 +30,10 @@ public interface DeployableSystemDao { @SqlQuery("select count(*) from deployable_systems") long countDeployableSystems(); + @SqlQuery("select * from users_deployable_systems where deployable_system_id = :systemId") + @RegisterRowMapper(SystemUserMapper.class) + List findUsersForSystem(@Bind("systemId") long systemId); + @SqlUpdate("update deployable_systems set dev_environment_id = :envId where id = :id") int updateDevEnvironment(@Bind("id") long id, @Bind("envId") long envId); diff --git a/service/src/main/java/org/kiwiproject/champagne/dao/mappers/SystemUserMapper.java b/service/src/main/java/org/kiwiproject/champagne/dao/mappers/SystemUserMapper.java new file mode 100644 index 00000000..c4e89bf5 --- /dev/null +++ b/service/src/main/java/org/kiwiproject/champagne/dao/mappers/SystemUserMapper.java @@ -0,0 +1,19 @@ +package org.kiwiproject.champagne.dao.mappers; + +import org.jdbi.v3.core.mapper.RowMapper; +import org.jdbi.v3.core.statement.StatementContext; +import org.kiwiproject.champagne.model.DeployableSystem; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class SystemUserMapper implements RowMapper { + + @Override + public DeployableSystem.SystemUser map(ResultSet rs, StatementContext ctx) throws SQLException { + return DeployableSystem.SystemUser.builder() + .userId(rs.getLong("user_id")) + .admin(rs.getBoolean("system_admin")) + .build(); + } +} diff --git a/service/src/main/java/org/kiwiproject/champagne/model/DeployableSystem.java b/service/src/main/java/org/kiwiproject/champagne/model/DeployableSystem.java index aa4bacbb..18dc80d6 100644 --- a/service/src/main/java/org/kiwiproject/champagne/model/DeployableSystem.java +++ b/service/src/main/java/org/kiwiproject/champagne/model/DeployableSystem.java @@ -5,6 +5,7 @@ import lombok.With; import java.time.Instant; +import java.util.List; import javax.validation.constraints.NotBlank; @Builder @@ -27,4 +28,17 @@ public class DeployableSystem { */ @With boolean admin; + + /** + * Transitive property that is the list of users assigned to this system and if they are an admin + */ + @With + List users; + + @Builder + @Value + public static class SystemUser { + Long userId; + boolean admin; + } } diff --git a/service/src/main/java/org/kiwiproject/champagne/resource/DeployableSystemResource.java b/service/src/main/java/org/kiwiproject/champagne/resource/DeployableSystemResource.java index 4e3a6b7b..0803495e 100644 --- a/service/src/main/java/org/kiwiproject/champagne/resource/DeployableSystemResource.java +++ b/service/src/main/java/org/kiwiproject/champagne/resource/DeployableSystemResource.java @@ -73,7 +73,10 @@ public Response listAllSystems(@QueryParam("pageNumber") @DefaultValue("1") int var offset = zeroBasedOffset(pageNumber, pageSize); - var systems = deployableSystemDao.findPagedDeployableSystems(offset, pageSize); + var systems = deployableSystemDao.findPagedDeployableSystems(offset, pageSize).stream() + .map(system -> system.withUsers(deployableSystemDao.findUsersForSystem(system.getId()))) + .toList(); + var total = deployableSystemDao.countDeployableSystems(); var page = KiwiPage.of(pageNumber, pageSize, total, systems); diff --git a/ui/src/pages/AdminSystemPage.vue b/ui/src/pages/AdminSystemPage.vue index d03f4557..46f4f648 100644 --- a/ui/src/pages/AdminSystemPage.vue +++ b/ui/src/pages/AdminSystemPage.vue @@ -22,6 +22,9 @@ @@ -58,14 +83,23 @@ import { onMounted, ref } from 'vue' import { formatDate, fromNow } from '../utils/time' import { confirmAction } from '../utils/alerts' import { useAdminSystemStore } from 'stores/adminSystemStore' +import { useUserStore } from 'stores/userStore' const adminSystemStore = useAdminSystemStore() +const userStore = useUserStore() // Reactive data const showSystemAdd = ref(false) const activeSystem = ref({ name: '' }) +const showAssignUsers = ref(false) +const systemUsers = ref({ + systemId: null, + users: [] +}) +const allUsers = ref([]) +const systemUserNextPage = ref(2) // Constant data const systemColumns = [ @@ -109,6 +143,23 @@ function deleteSystem (id) { adminSystemStore.deleteSystem(id) } +function startAssignUsers (system) { + userStore.load() + systemUsers.value.systemId = system.id + systemUsers.value.users = system.users + showAssignUsers.value = true +} + +function onUserScroll ({ to, ref }) { + const lastPage = Math.ceil(userStore.pagination.rowsNumber / userStore.pagination.rowsPerPage) + const lastIndex = allUsers.value.length - 1 + + if (userStore.loading !== true && systemUserNextPage.value < lastPage && to === lastIndex) { + userStore.load({ pagination: { page: systemUserNextPage.value, rowsPerPage: userStore.pagination.rowsPerPage } }) + allUsers.value.push(...userStore.users) + } +} + onMounted(() => { adminSystemStore.load() }) From 16561b0eef025e143736f295af6a43b999605a3c Mon Sep 17 00:00:00 2001 From: Chris Rohr <51920+chrisrohr@users.noreply.github.com> Date: Tue, 7 Mar 2023 21:10:13 +0000 Subject: [PATCH 2/3] More updates for adding users to system --- .../champagne/dao/DeployableSystemDao.java | 17 ++++ .../resource/DeployableSystemResource.java | 14 +++ ui/src/pages/AdminSystemPage.vue | 88 ++++++++++++++++--- ui/src/stores/adminSystemStore.js | 9 +- ui/src/stores/userStore.js | 2 +- ui/src/utils/data.js | 2 + 6 files changed, 115 insertions(+), 17 deletions(-) diff --git a/service/src/main/java/org/kiwiproject/champagne/dao/DeployableSystemDao.java b/service/src/main/java/org/kiwiproject/champagne/dao/DeployableSystemDao.java index 43e570a4..4b7c6be5 100644 --- a/service/src/main/java/org/kiwiproject/champagne/dao/DeployableSystemDao.java +++ b/service/src/main/java/org/kiwiproject/champagne/dao/DeployableSystemDao.java @@ -48,4 +48,21 @@ public interface DeployableSystemDao { @SqlQuery("select true from users_deployable_systems uds join users u on u.id = uds.user_id join deployable_systems ds on ds.id = uds.deployable_system_id where u.system_identifier = :userName and ds.id = :systemId") boolean isUserBySystemIdentifierInSystem(@Bind("userName") String systemIdentifier, @Bind("systemId") long systemId); + + @SqlQuery("select true from users_deployable_systems where deployable_system_id = :systemId and user_id = :userId") + boolean isUserInSystem(@Bind("userId") long userId, @Bind("systemId") long systemId); + + default void insertOrUpdateSystemUser(long systemId, long userId, boolean isAdmin) { + if (isUserInSystem(userId, systemId)) { + updateAdminStatusForUserInSystem(systemId, userId, isAdmin); + } else { + addUserToSystem(systemId, userId, isAdmin); + } + } + + @SqlUpdate("update users_deployable_systems set admin = :admin where deployable_system_id = :systemId and user_id = :userId") + void updateAdminStatusForUserInSystem(@Bind("systemId") long systemId, @Bind("userId") long userId, @Bind("admin") boolean isAdmin); + + @SqlUpdate("insert into users_deployable_systems (deployable_system_id, user_id, system_admin) values (:systemId, :userId, :admin)") + void addUserToSystem(@Bind("systemId") long systemId, @Bind("userId") long userId, @Bind("admin") boolean isAdmin); } diff --git a/service/src/main/java/org/kiwiproject/champagne/resource/DeployableSystemResource.java b/service/src/main/java/org/kiwiproject/champagne/resource/DeployableSystemResource.java index 0803495e..95fbea90 100644 --- a/service/src/main/java/org/kiwiproject/champagne/resource/DeployableSystemResource.java +++ b/service/src/main/java/org/kiwiproject/champagne/resource/DeployableSystemResource.java @@ -15,6 +15,7 @@ import org.kiwiproject.champagne.dao.DeployableSystemDao; import org.kiwiproject.champagne.dao.UserDao; import org.kiwiproject.champagne.model.DeployableSystem; +import org.kiwiproject.champagne.model.DeployableSystem.SystemUser; import org.kiwiproject.dropwizard.error.dao.ApplicationErrorDao; import org.kiwiproject.jaxrs.exception.JaxrsNotAuthorizedException; import org.kiwiproject.spring.data.KiwiPage; @@ -155,4 +156,17 @@ public Response deleteSystem(@PathParam("id") Long id) { return Response.noContent().build(); } + + @POST + @Path("/{id}/users") + @Timed + @ExceptionMetered + @RolesAllowed({ "admin" }) + public Response addUsersToSystem(@PathParam("id") long systemId, List users) { + users.forEach(user -> { + deployableSystemDao.insertOrUpdateSystemUser(systemId, user.getUserId(), user.isAdmin()); + }); + + return Response.accepted().build(); + } } diff --git a/ui/src/pages/AdminSystemPage.vue b/ui/src/pages/AdminSystemPage.vue index 46f4f648..fdbec948 100644 --- a/ui/src/pages/AdminSystemPage.vue +++ b/ui/src/pages/AdminSystemPage.vue @@ -64,14 +64,30 @@ outlined dense style="min-width: 205px" - v-model="systemUsers.users" - :options="allUsers" - :loading="userStore.loading" - @virtual-scroll="onUserScroll"/> + v-model="selectedUser" + :options="allUsers"/> + + + + + + + + Cancel - Save + Save @@ -82,6 +98,7 @@ import { onMounted, ref } from 'vue' import { formatDate, fromNow } from '../utils/time' import { confirmAction } from '../utils/alerts' +import { _ } from 'lodash' import { useAdminSystemStore } from 'stores/adminSystemStore' import { useUserStore } from 'stores/userStore' @@ -94,12 +111,12 @@ const activeSystem = ref({ name: '' }) const showAssignUsers = ref(false) +const selectedUser = ref(null) const systemUsers = ref({ systemId: null, users: [] }) const allUsers = ref([]) -const systemUserNextPage = ref(2) // Constant data const systemColumns = [ @@ -125,6 +142,23 @@ const systemColumns = [ align: 'left' } ] +const selectedUserCols = [ + { + name: 'username', + label: 'User', + field: 'displayName', + align: 'left' + }, + { + name: 'admin', + label: 'Admin?', + align: 'left' + }, + { + name: 'action', + align: 'left' + } +] // Methods function createSystem () { @@ -144,20 +178,46 @@ function deleteSystem (id) { } function startAssignUsers (system) { - userStore.load() + userStore.load({ pagination: { page: 1, rowsPerPage: 2000 } }).then(() => { + allUsers.value.push(...userToOption(userStore.users)) + console.log(allUsers.value) + }) + systemUsers.value.systemId = system.id systemUsers.value.users = system.users showAssignUsers.value = true } -function onUserScroll ({ to, ref }) { - const lastPage = Math.ceil(userStore.pagination.rowsNumber / userStore.pagination.rowsPerPage) - const lastIndex = allUsers.value.length - 1 +function userToOption (users) { + return users + .map(e => { return { label: e.displayName, value: e.id } }) + .sort((a, b) => { + const nameA = a.label + const nameB = b.label - if (userStore.loading !== true && systemUserNextPage.value < lastPage && to === lastIndex) { - userStore.load({ pagination: { page: systemUserNextPage.value, rowsPerPage: userStore.pagination.rowsPerPage } }) - allUsers.value.push(...userStore.users) - } + if (nameA < nameB) { + return -1 + } + + if (nameB < nameA) { + return 1 + } + + return 0 + }) +} + +function addUserToSystem () { + systemUsers.value.users.push({ userId: selectedUser.value.id, displayName: selectedUser.value.label, admin: false }) +} + +function removeSelectedUser (selectedUser) { + systemUsers.value.users = _.without(systemUsers.value.users, selectedUser) +} + +function addUsersToSystem () { + console.log(systemUsers.value) + adminSystemStore.assignUsersToSystem(systemUsers.value.systemId, systemUsers.value.users) } onMounted(() => { diff --git a/ui/src/stores/adminSystemStore.js b/ui/src/stores/adminSystemStore.js index 07c1ff0a..be3763e3 100644 --- a/ui/src/stores/adminSystemStore.js +++ b/ui/src/stores/adminSystemStore.js @@ -16,7 +16,7 @@ export const useAdminSystemStore = defineStore('adminSystem', () => { doPagedRequest(loading, props, pagination, '/systems/admin', systems) } - function create (systemData) { + async function create (systemData) { return api.post('/systems', systemData) .then(() => load()) } @@ -26,5 +26,10 @@ export const useAdminSystemStore = defineStore('adminSystem', () => { .then(() => load()) } - return { systems, loading, load, create, deleteSystem } + async function assignUsersToSystem (systemId, usersToSet) { + return api.post(`/systems/${systemId}/users`, usersToSet) + .then(() => load()) + } + + return { systems, loading, load, create, deleteSystem, assignUsersToSystem } }) diff --git a/ui/src/stores/userStore.js b/ui/src/stores/userStore.js index 20b5e043..f407f7ae 100644 --- a/ui/src/stores/userStore.js +++ b/ui/src/stores/userStore.js @@ -13,7 +13,7 @@ export const useUserStore = defineStore('user', () => { }) async function load (props) { - doPagedRequest(loading, props, pagination, '/users', users) + return doPagedRequest(loading, props, pagination, '/users', users) } function create (userData) { diff --git a/ui/src/utils/data.js b/ui/src/utils/data.js index 82f0f19c..1767b1c3 100644 --- a/ui/src/utils/data.js +++ b/ui/src/utils/data.js @@ -22,6 +22,8 @@ async function doPagedRequest (loading, props, pagination, path, contents, filte contents.value = response.data.content pagination.value.rowsNumber = response.data.totalElements loading.value = false + + return Promise.resolve(contents) } export { doPagedRequest } From 3824af2ebc54ac48fa89412b0207fe08b3729f73 Mon Sep 17 00:00:00 2001 From: Chris Rohr <51920+chrisrohr@users.noreply.github.com> Date: Tue, 7 Mar 2023 22:47:31 -0500 Subject: [PATCH 3/3] Finish process of adding users to a system Relates to #140 --- .../champagne/dao/DeployableSystemDao.java | 9 +-- .../champagne/model/DeployableSystem.java | 4 ++ .../resource/DeployableSystemResource.java | 5 +- .../filter/DeployableSystemRequestFilter.java | 3 +- .../dao/DeployableSystemDaoTest.java | 72 +++++++++++++++++++ .../DeployableSystemResourceTest.java | 45 ++++++++++-- ui/src/pages/AdminSystemPage.vue | 39 +++++----- .../__tests__/pages/AdminSystemPage.spec.js | 58 +++++++++++++++ .../__tests__/stores/adminSystemStore.spec.js | 10 +++ 9 files changed, 209 insertions(+), 36 deletions(-) diff --git a/service/src/main/java/org/kiwiproject/champagne/dao/DeployableSystemDao.java b/service/src/main/java/org/kiwiproject/champagne/dao/DeployableSystemDao.java index 4b7c6be5..74c40aea 100644 --- a/service/src/main/java/org/kiwiproject/champagne/dao/DeployableSystemDao.java +++ b/service/src/main/java/org/kiwiproject/champagne/dao/DeployableSystemDao.java @@ -1,5 +1,6 @@ package org.kiwiproject.champagne.dao; +import org.apache.commons.lang3.BooleanUtils; import org.jdbi.v3.sqlobject.config.RegisterRowMapper; import org.jdbi.v3.sqlobject.customizer.Bind; import org.jdbi.v3.sqlobject.customizer.BindBean; @@ -47,20 +48,20 @@ public interface DeployableSystemDao { boolean isUserAdminOfSystem(@Bind("userId") long userId, @Bind("systemId") long systemId); @SqlQuery("select true from users_deployable_systems uds join users u on u.id = uds.user_id join deployable_systems ds on ds.id = uds.deployable_system_id where u.system_identifier = :userName and ds.id = :systemId") - boolean isUserBySystemIdentifierInSystem(@Bind("userName") String systemIdentifier, @Bind("systemId") long systemId); + Boolean isUserBySystemIdentifierInSystem(@Bind("userName") String systemIdentifier, @Bind("systemId") long systemId); @SqlQuery("select true from users_deployable_systems where deployable_system_id = :systemId and user_id = :userId") - boolean isUserInSystem(@Bind("userId") long userId, @Bind("systemId") long systemId); + Boolean isUserInSystem(@Bind("userId") long userId, @Bind("systemId") long systemId); default void insertOrUpdateSystemUser(long systemId, long userId, boolean isAdmin) { - if (isUserInSystem(userId, systemId)) { + if (BooleanUtils.isTrue(isUserInSystem(userId, systemId))) { updateAdminStatusForUserInSystem(systemId, userId, isAdmin); } else { addUserToSystem(systemId, userId, isAdmin); } } - @SqlUpdate("update users_deployable_systems set admin = :admin where deployable_system_id = :systemId and user_id = :userId") + @SqlUpdate("update users_deployable_systems set system_admin = :admin where deployable_system_id = :systemId and user_id = :userId") void updateAdminStatusForUserInSystem(@Bind("systemId") long systemId, @Bind("userId") long userId, @Bind("admin") boolean isAdmin); @SqlUpdate("insert into users_deployable_systems (deployable_system_id, user_id, system_admin) values (:systemId, :userId, :admin)") diff --git a/service/src/main/java/org/kiwiproject/champagne/model/DeployableSystem.java b/service/src/main/java/org/kiwiproject/champagne/model/DeployableSystem.java index 18dc80d6..452ecf97 100644 --- a/service/src/main/java/org/kiwiproject/champagne/model/DeployableSystem.java +++ b/service/src/main/java/org/kiwiproject/champagne/model/DeployableSystem.java @@ -1,6 +1,8 @@ package org.kiwiproject.champagne.model; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Builder; +import lombok.ToString; import lombok.Value; import lombok.With; @@ -37,6 +39,8 @@ public class DeployableSystem { @Builder @Value + @JsonIgnoreProperties(ignoreUnknown = true) + @ToString public static class SystemUser { Long userId; boolean admin; diff --git a/service/src/main/java/org/kiwiproject/champagne/resource/DeployableSystemResource.java b/service/src/main/java/org/kiwiproject/champagne/resource/DeployableSystemResource.java index 95fbea90..403199f1 100644 --- a/service/src/main/java/org/kiwiproject/champagne/resource/DeployableSystemResource.java +++ b/service/src/main/java/org/kiwiproject/champagne/resource/DeployableSystemResource.java @@ -163,10 +163,7 @@ public Response deleteSystem(@PathParam("id") Long id) { @ExceptionMetered @RolesAllowed({ "admin" }) public Response addUsersToSystem(@PathParam("id") long systemId, List users) { - users.forEach(user -> { - deployableSystemDao.insertOrUpdateSystemUser(systemId, user.getUserId(), user.isAdmin()); - }); - + users.forEach(user -> deployableSystemDao.insertOrUpdateSystemUser(systemId, user.getUserId(), user.isAdmin())); return Response.accepted().build(); } } diff --git a/service/src/main/java/org/kiwiproject/champagne/resource/filter/DeployableSystemRequestFilter.java b/service/src/main/java/org/kiwiproject/champagne/resource/filter/DeployableSystemRequestFilter.java index 57eac5fc..8192ecab 100644 --- a/service/src/main/java/org/kiwiproject/champagne/resource/filter/DeployableSystemRequestFilter.java +++ b/service/src/main/java/org/kiwiproject/champagne/resource/filter/DeployableSystemRequestFilter.java @@ -1,5 +1,6 @@ package org.kiwiproject.champagne.resource.filter; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.kiwiproject.champagne.dao.DeployableSystemDao; import org.kiwiproject.champagne.model.DeployableSystemThreadLocal; @@ -24,7 +25,7 @@ public void filter(ContainerRequestContext requestContext) { if (StringUtils.isNotBlank(system)) { var userName = requestContext.getSecurityContext().getUserPrincipal().getName(); - if (deployableSystemDao.isUserBySystemIdentifierInSystem(userName, Long.parseLong(system))) { + if (BooleanUtils.isTrue(deployableSystemDao.isUserBySystemIdentifierInSystem(userName, Long.parseLong(system)))) { DeployableSystemThreadLocal.setCurrentDeployableSystem(Long.valueOf(system)); } } diff --git a/service/src/test/java/org/kiwiproject/champagne/dao/DeployableSystemDaoTest.java b/service/src/test/java/org/kiwiproject/champagne/dao/DeployableSystemDaoTest.java index d460c428..6d22f68c 100644 --- a/service/src/test/java/org/kiwiproject/champagne/dao/DeployableSystemDaoTest.java +++ b/service/src/test/java/org/kiwiproject/champagne/dao/DeployableSystemDaoTest.java @@ -219,5 +219,77 @@ void shouldReturnFalseWhenUserIsNotAnAdminForSystem() { assertThat(result).isFalse(); } } + + @Nested + class FindUsersForSystem { + + @Test + void shouldReturnListOfUsersForASystem() { + var userId = insertUserRecord(handle, "jdoe"); + var deployableSystemId = insertDeployableSystem(handle, "system1"); + insertUserToDeployableSystemLink(handle, userId, deployableSystemId, true); + + var users = dao.findUsersForSystem(deployableSystemId); + + assertThat(users).extracting("userId", "admin").contains(tuple(userId, true)); + } + } + + @Nested + class IsUserInSystem { + + @Test + void shouldReturnTrueWhenUserIsAssignedToSystem() { + var userId = insertUserRecord(handle, "jdoe"); + var deployableSystemId = insertDeployableSystem(handle, "system1"); + insertUserToDeployableSystemLink(handle, userId, deployableSystemId, true); + + var result = dao.isUserInSystem(userId, deployableSystemId); + + assertThat(result).isTrue(); + } + + @Test + void shouldReturnNullWhenUserIsNotAssignedToSystem() { + var userId = insertUserRecord(handle, "jdoe"); + var deployableSystemId = insertDeployableSystem(handle, "system1"); + insertUserToDeployableSystemLink(handle, userId, deployableSystemId, true); + + var result = dao.isUserInSystem(userId+1, deployableSystemId); + + assertThat(result).isNull(); + } + } + + @Nested + class InsertOrUpdateSystemUser { + + @Test + void shouldInsertNewUserSystemLinkWhenDoesNotExist() { + var userId = insertUserRecord(handle, "jdoe"); + var deployableSystemId = insertDeployableSystem(handle, "system1"); + + assertThat(dao.isUserInSystem(userId, deployableSystemId)).isNull(); + + dao.insertOrUpdateSystemUser(deployableSystemId, userId, false); + + assertThat(dao.isUserInSystem(userId, deployableSystemId)).isTrue(); + } + + @Test + void shouldUpdateUserSystemLinkWhenExists() { + var userId = insertUserRecord(handle, "jdoe"); + var deployableSystemId = insertDeployableSystem(handle, "system1"); + insertUserToDeployableSystemLink(handle, userId, deployableSystemId, true); + + var systemUsers = dao.findUsersForSystem(deployableSystemId); + assertThat(systemUsers).extracting("userId", "admin").contains(tuple(userId, true)); + + dao.insertOrUpdateSystemUser(deployableSystemId, userId, false); + + systemUsers = dao.findUsersForSystem(deployableSystemId); + assertThat(systemUsers).extracting("userId", "admin").contains(tuple(userId, false)); + } + } } diff --git a/service/src/test/java/org/kiwiproject/champagne/resource/DeployableSystemResourceTest.java b/service/src/test/java/org/kiwiproject/champagne/resource/DeployableSystemResourceTest.java index d4c429c4..2e94fdc9 100644 --- a/service/src/test/java/org/kiwiproject/champagne/resource/DeployableSystemResourceTest.java +++ b/service/src/test/java/org/kiwiproject/champagne/resource/DeployableSystemResourceTest.java @@ -129,8 +129,14 @@ void shouldReturnPagedListOfDeployableSystems() { .environmentPromotionOrder("2,3,4") .build(); + var systemUser = DeployableSystem.SystemUser.builder() + .userId(1L) + .admin(true) + .build(); + when(DEPLOYABLE_SYSTEM_DAO.findPagedDeployableSystems(0, 10)).thenReturn(List.of(system)); when(DEPLOYABLE_SYSTEM_DAO.countDeployableSystems()).thenReturn(1L); + when(DEPLOYABLE_SYSTEM_DAO.findUsersForSystem(1L)).thenReturn(List.of(systemUser)); var token = generateJwt(true); var response = RESOURCES.client() @@ -151,6 +157,7 @@ void shouldReturnPagedListOfDeployableSystems() { verify(DEPLOYABLE_SYSTEM_DAO).findPagedDeployableSystems(0, 10); verify(DEPLOYABLE_SYSTEM_DAO).countDeployableSystems(); + verify(DEPLOYABLE_SYSTEM_DAO).findUsersForSystem(1L); verifyNoMoreInteractions(DEPLOYABLE_SYSTEM_DAO); } @@ -164,8 +171,14 @@ void shouldReturnPagedListOfReleasesWithDefaultPaging() { .environmentPromotionOrder("2,3,4") .build(); + var systemUser = DeployableSystem.SystemUser.builder() + .userId(1L) + .admin(true) + .build(); + when(DEPLOYABLE_SYSTEM_DAO.findPagedDeployableSystems(0, 25)).thenReturn(List.of(system)); when(DEPLOYABLE_SYSTEM_DAO.countDeployableSystems()).thenReturn(1L); + when(DEPLOYABLE_SYSTEM_DAO.findUsersForSystem(1L)).thenReturn(List.of(systemUser)); var token = generateJwt(true); var response = RESOURCES.client() @@ -184,6 +197,7 @@ void shouldReturnPagedListOfReleasesWithDefaultPaging() { verify(DEPLOYABLE_SYSTEM_DAO).findPagedDeployableSystems(0, 25); verify(DEPLOYABLE_SYSTEM_DAO).countDeployableSystems(); + verify(DEPLOYABLE_SYSTEM_DAO).findUsersForSystem(1L); verifyNoMoreInteractions(DEPLOYABLE_SYSTEM_DAO); } @@ -336,7 +350,7 @@ void shouldUpdateTheEnvOrderForTheGivenSystem() { .target("/systems/2/order") .request() .cookie("sessionToken", token) - .put(json(List.of(1,2,3))); + .put(json(List.of(1, 2, 3))); assertAcceptedResponse(response); @@ -363,7 +377,7 @@ void shouldNotAuditActionWhenNothingChanges() { .target("/systems/2/order") .request() .cookie("sessionToken", token) - .put(json(List.of(1,2,3))); + .put(json(List.of(1, 2, 3))); assertAcceptedResponse(response); @@ -389,7 +403,7 @@ void shouldThrow403WhenUserIsNotAdminInSystem() { .target("/systems/2/order") .request() .cookie("sessionToken", token) - .put(json(List.of(1,2,3))); + .put(json(List.of(1, 2, 3))); assertUnauthorizedResponse(response); @@ -409,7 +423,7 @@ void shouldThrow403WhenUserIsNotFound() { .target("/systems/2/order") .request() .cookie("sessionToken", token) - .put(json(List.of(1,2,3))); + .put(json(List.of(1, 2, 3))); assertUnauthorizedResponse(response); @@ -462,4 +476,27 @@ void shouldNotAuditIfDeleteDoesNotChangeDB() { verifyNoInteractions(AUDIT_RECORD_DAO); } } + + @Nested + class AddUsersToSystem { + + @Test + void shouldAddGivenUsersToGivenSystem() { + var systemUser = DeployableSystem.SystemUser.builder() + .userId(2L) + .admin(false) + .build(); + + var token = generateJwt(true); + var response = RESOURCES.client().target("/systems/{id}/users") + .resolveTemplate("id", 1L) + .request() + .cookie("sessionToken", token) + .post(json(List.of(systemUser))); + + assertAcceptedResponse(response); + + verify(DEPLOYABLE_SYSTEM_DAO).insertOrUpdateSystemUser(1L, 2L, false); + } + } } diff --git a/ui/src/pages/AdminSystemPage.vue b/ui/src/pages/AdminSystemPage.vue index fdbec948..0544acea 100644 --- a/ui/src/pages/AdminSystemPage.vue +++ b/ui/src/pages/AdminSystemPage.vue @@ -179,36 +179,27 @@ function deleteSystem (id) { function startAssignUsers (system) { userStore.load({ pagination: { page: 1, rowsPerPage: 2000 } }).then(() => { - allUsers.value.push(...userToOption(userStore.users)) - console.log(allUsers.value) - }) + allUsers.value = userToOption(userStore.users) - systemUsers.value.systemId = system.id - systemUsers.value.users = system.users - showAssignUsers.value = true + systemUsers.value.systemId = system.id + populateNameOnExistingUsers(system.users) + systemUsers.value.users = system.users + showAssignUsers.value = true + }) } function userToOption (users) { - return users - .map(e => { return { label: e.displayName, value: e.id } }) - .sort((a, b) => { - const nameA = a.label - const nameB = b.label - - if (nameA < nameB) { - return -1 - } - - if (nameB < nameA) { - return 1 - } + return _.sortBy(users.map(e => { return { label: e.displayName, value: e.id } }), ['label']) +} - return 0 - }) +function populateNameOnExistingUsers (users) { + users.forEach(user => { + user.displayName = _.find(allUsers.value, u => u.value === user.userId).label + }) } function addUserToSystem () { - systemUsers.value.users.push({ userId: selectedUser.value.id, displayName: selectedUser.value.label, admin: false }) + systemUsers.value.users.push({ userId: selectedUser.value.value, displayName: selectedUser.value.label, admin: false }) } function removeSelectedUser (selectedUser) { @@ -216,8 +207,10 @@ function removeSelectedUser (selectedUser) { } function addUsersToSystem () { - console.log(systemUsers.value) adminSystemStore.assignUsersToSystem(systemUsers.value.systemId, systemUsers.value.users) + .then(() => { + showAssignUsers.value = false + }) } onMounted(() => { diff --git a/ui/test/vitest/__tests__/pages/AdminSystemPage.spec.js b/ui/test/vitest/__tests__/pages/AdminSystemPage.spec.js index eed28efe..84539aef 100644 --- a/ui/test/vitest/__tests__/pages/AdminSystemPage.spec.js +++ b/ui/test/vitest/__tests__/pages/AdminSystemPage.spec.js @@ -5,6 +5,7 @@ import AdminSystemPage from 'pages/AdminSystemPage.vue' import { createTestingPinia } from '@pinia/testing' import { useAdminSystemStore } from 'src/stores/adminSystemStore' import { confirmAction } from 'src/utils/alerts' +import { useUserStore } from 'src/stores/userStore' installQuasar() @@ -59,3 +60,60 @@ describe('confirmDelete', () => { expect(confirmAction).toHaveBeenCalledWith('Are you sure you want to delete system kiwi? This will cause all related components to be deleted as well!', expect.any(Function)) }) }) + +describe('startAssignUsers', () => { + it('should setup assign users dialog', () => { + const userStore = useUserStore() + userStore.load.mockImplementation(() => Promise.resolve(1)) + + wrapper.vm.startAssignUsers({ id: 1, users: [] }) + + expect(userStore.load).toHaveBeenCalled() + }) +}) + +describe('populateNameOnExistingUsers', () => { + it('should add display name', () => { + wrapper.vm.allUsers = [{ label: 'John Doe', value: 1 }] + + const users = [ + { userId: 1, admin: false } + ] + + wrapper.vm.populateNameOnExistingUsers(users) + + expect(users[0].displayName).toEqual('John Doe') + }) +}) + +describe('addUserToSystem', () => { + it('should add the user to the list for update', () => { + wrapper.vm.selectedUser = { label: 'John Doe', value: 1 } + + wrapper.vm.addUserToSystem() + + expect(wrapper.vm.systemUsers.users).toEqual([{ userId: 1, displayName: 'John Doe', admin: false }]) + }) +}) + +describe('removeSelectedUser', () => { + it('should remove the user from the list for update', () => { + const userToRemove = { userId: 1, displayName: 'John Doe', admin: false } + wrapper.vm.systemUsers.users = userToRemove + + wrapper.vm.removeSelectedUser([userToRemove]) + + expect(wrapper.vm.systemUsers.users).toEqual([]) + }) +}) + +describe('addUsersToSystem', () => { + it('should call the adminSystemStore to update users assigned', () => { + const adminSystemStore = useAdminSystemStore() + adminSystemStore.assignUsersToSystem.mockImplementation(() => Promise.resolve(1)) + + wrapper.vm.addUsersToSystem() + + expect(adminSystemStore.assignUsersToSystem).toHaveBeenCalled() + }) +}) diff --git a/ui/test/vitest/__tests__/stores/adminSystemStore.spec.js b/ui/test/vitest/__tests__/stores/adminSystemStore.spec.js index 372b513d..545ea769 100644 --- a/ui/test/vitest/__tests__/stores/adminSystemStore.spec.js +++ b/ui/test/vitest/__tests__/stores/adminSystemStore.spec.js @@ -51,3 +51,13 @@ describe('deleteSystem', () => { expect(api.delete).toHaveBeenCalledWith('/systems/1') }) }) + +describe('assignUsersToSystem', () => { + it('should make call to update system users', async () => { + const adminSystemStore = useAdminSystemStore() + await adminSystemStore.assignUsersToSystem(1, [{ userId: 1, admin: true }]) + + expect(api.post).toHaveBeenCalled() + expect(api.post).toHaveBeenCalledWith('/systems/1/users', [{ userId: 1, admin: true }]) + }) +})