Skip to content

Commit

Permalink
Add System management page (#351)
Browse files Browse the repository at this point in the history
* Add System management page

* Add more checks to satisfy code coverage

* Fix type of wrong word in docs
  • Loading branch information
chrisrohr authored Sep 14, 2023
1 parent c5cb591 commit e873f69
Show file tree
Hide file tree
Showing 11 changed files with 300 additions and 26 deletions.
2 changes: 1 addition & 1 deletion service/src/main/java/org/kiwiproject/champagne/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public void run(AppConfig configuration, Environment environment) {
environment.jersey().register(new TaskResource(releaseDao, releaseStatusDao, taskDao, taskStatusDao, deploymentEnvironmentDao, auditRecordDao, errorDao));
environment.jersey().register(new UserResource(userDao, deployableSystemDao, auditRecordDao, errorDao));
environment.jersey().register(new ApplicationErrorResource(errorDao));
environment.jersey().register(new DeployableSystemResource(deployableSystemDao, userDao, auditRecordDao, errorDao));
environment.jersey().register(new DeployableSystemResource(deployableSystemDao, userDao, deploymentEnvironmentDao, auditRecordDao, errorDao));
environment.jersey().register(new TagResource(tagDao, auditRecordDao, errorDao));
environment.jersey().register(new MetricsResource(buildDao));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.kiwiproject.champagne.dao;

import java.util.List;

import org.apache.commons.lang3.BooleanUtils;
import org.jdbi.v3.sqlobject.config.RegisterRowMapper;
import org.jdbi.v3.sqlobject.customizer.Bind;
Expand All @@ -12,8 +14,6 @@
import org.kiwiproject.champagne.dao.mappers.SystemUserMapper;
import org.kiwiproject.champagne.model.DeployableSystem;

import java.util.List;

public interface DeployableSystemDao {

@SqlUpdate("insert into deployable_systems (name) values (:name)")
Expand All @@ -31,7 +31,7 @@ public interface DeployableSystemDao {
@SqlQuery("select count(*) from deployable_systems")
long countDeployableSystems();

@SqlQuery("select * from users_deployable_systems where deployable_system_id = :systemId")
@SqlQuery("select u.id, u.display_name, u.system_identifier, usd.system_admin from users u left join users_deployable_systems usd on u.id = usd.user_id where usd.deployable_system_id = :systemId")
@RegisterRowMapper(SystemUserMapper.class)
List<DeployableSystem.SystemUser> findUsersForSystem(@Bind("systemId") long systemId);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.kiwiproject.champagne.dao;

import java.util.List;

import org.jdbi.v3.sqlobject.config.RegisterRowMapper;
import org.jdbi.v3.sqlobject.customizer.Bind;
import org.jdbi.v3.sqlobject.customizer.BindBean;
Expand All @@ -9,8 +11,6 @@
import org.kiwiproject.champagne.dao.mappers.DeploymentEnvironmentMapper;
import org.kiwiproject.champagne.model.DeploymentEnvironment;

import java.util.List;

@RegisterRowMapper(DeploymentEnvironmentMapper.class)
public interface DeploymentEnvironmentDao {
@SqlUpdate("insert into deployment_environments (environment_name, deployable_system_id) values (:name, :deployableSystemId)")
Expand All @@ -32,4 +32,7 @@ public interface DeploymentEnvironmentDao {
@SqlUpdate("update deployment_environments set deleted = false, updated_at = current_timestamp where id = :id")
int unSoftDeleteById(@Bind("id") long id);

@SqlQuery("select environment_name from deployment_environments where id = :id")
String getEnvironmentName(@Bind("id") long id);

}
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
package org.kiwiproject.champagne.dao.mappers;

import java.sql.ResultSet;
import java.sql.SQLException;

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<DeployableSystem.SystemUser> {

@Override
public DeployableSystem.SystemUser map(ResultSet rs, StatementContext ctx) throws SQLException {
return DeployableSystem.SystemUser.builder()
.userId(rs.getLong("user_id"))
.userId(rs.getLong("id"))
.displayName(rs.getString("display_name"))
.systemIdentifier(rs.getString("system_identifier"))
.admin(rs.getBoolean("system_admin"))
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,38 @@ public class DeployableSystem {
String environmentPromotionOrder;

/**
* This is a transitive property populated through the users_system table and indicates if a specific user is
* This is a transient property populated through the users_system table and indicates if a specific user is
* an admin in this system. Listing all systems will NOT populate this field.
*/
@With
boolean admin;

/**
* Transitive property that is the list of users assigned to this system and if they are an admin
* Transient property that is the list of users assigned to this system and if they are an admin
*/
@With
List<SystemUser> users;

/**
* Transient property that is the resolved development environment name.
*/
@With
String devEnvName;

/**
* Transient property that is the list of the resolved deployment environment names in their promotion order.
*/
@With
List<String> envOrder;

@Builder
@Value
@JsonIgnoreProperties(ignoreUnknown = true)
@ToString
public static class SystemUser {
Long userId;
String displayName;
String systemIdentifier;
boolean admin;
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package org.kiwiproject.champagne.resource;

import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.kiwiproject.champagne.model.AuditRecord.Action.CREATED;
import static org.kiwiproject.champagne.model.AuditRecord.Action.DELETED;
import static org.kiwiproject.champagne.model.AuditRecord.Action.UPDATED;
import static org.kiwiproject.search.KiwiSearching.zeroBasedOffset;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import com.codahale.metrics.annotation.ExceptionMetered;
import com.codahale.metrics.annotation.Timed;
Expand All @@ -29,6 +32,7 @@
import org.dhatim.dropwizard.jwt.cookie.authentication.DefaultJwtCookiePrincipal;
import org.kiwiproject.champagne.dao.AuditRecordDao;
import org.kiwiproject.champagne.dao.DeployableSystemDao;
import org.kiwiproject.champagne.dao.DeploymentEnvironmentDao;
import org.kiwiproject.champagne.dao.UserDao;
import org.kiwiproject.champagne.model.DeployableSystem;
import org.kiwiproject.champagne.model.DeployableSystem.SystemUser;
Expand All @@ -44,12 +48,18 @@ public class DeployableSystemResource extends AuditableResource {

private final DeployableSystemDao deployableSystemDao;
private final UserDao userDao;
private final DeploymentEnvironmentDao deploymentEnvironmentDao;

public DeployableSystemResource(DeployableSystemDao deployableSystemDao,
UserDao userDao, DeploymentEnvironmentDao deploymentEnvironmentDao,
AuditRecordDao auditRecordDao,
ApplicationErrorDao errorDao) {

public DeployableSystemResource(DeployableSystemDao deployableSystemDao, UserDao userDao, AuditRecordDao auditRecordDao, ApplicationErrorDao errorDao) {
super(auditRecordDao, errorDao);

this.deployableSystemDao = deployableSystemDao;
this.userDao = userDao;
this.deploymentEnvironmentDao = deploymentEnvironmentDao;
}

@GET
Expand All @@ -75,7 +85,7 @@ public Response listAllSystems(@QueryParam("pageNumber") @DefaultValue("1") int
var offset = zeroBasedOffset(pageNumber, pageSize);

var systems = deployableSystemDao.findPagedDeployableSystems(offset, pageSize).stream()
.map(system -> system.withUsers(deployableSystemDao.findUsersForSystem(system.getId())))
.map(this::inflateSystem)
.toList();

var total = deployableSystemDao.countDeployableSystems();
Expand All @@ -84,6 +94,25 @@ public Response listAllSystems(@QueryParam("pageNumber") @DefaultValue("1") int
return Response.ok(page).build();
}

private DeployableSystem inflateSystem(DeployableSystem system) {
var updatedSystem = system.withUsers(deployableSystemDao.findUsersForSystem(system.getId()));

if (Objects.nonNull(system.getDevEnvironmentId())) {
updatedSystem = updatedSystem.withDevEnvName(deploymentEnvironmentDao.getEnvironmentName(system.getDevEnvironmentId()));
}

if (isNotBlank(system.getEnvironmentPromotionOrder())) {
var orderedEnvNames = Arrays.stream(system.getEnvironmentPromotionOrder().split(","))
.map(Long::parseLong)
.map(deploymentEnvironmentDao::getEnvironmentName)
.toList();

updatedSystem = updatedSystem.withEnvOrder(orderedEnvNames);
}

return updatedSystem;
}

@POST
@Timed
@ExceptionMetered
Expand Down Expand Up @@ -176,7 +205,4 @@ public Response removeUserFromSystem(@PathParam("systemId") long systemId, @Path
deployableSystemDao.deleteUserFromSystem(systemId, userId);
return Response.noContent().build();
}

// TODO: Add endpoint to set and unset existing system user as admin (only can be done by system admins)
// TODO: Add endpoint to retrieve a list of users for the current system (only by system admin)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import static org.kiwiproject.collect.KiwiLists.first;
import static org.kiwiproject.test.util.DateTimeTestHelper.assertTimeDifferenceWithinTolerance;

import java.time.ZoneOffset;
import java.time.ZonedDateTime;

import org.jdbi.v3.core.Handle;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
Expand All @@ -17,9 +20,6 @@
import org.kiwiproject.test.junit.jupiter.Jdbi3DaoExtension;
import org.kiwiproject.test.junit.jupiter.PostgresLiquibaseTestExtension;

import java.time.ZoneOffset;
import java.time.ZonedDateTime;

@DisplayName("DeploymentEnvironmentDao")
class DeploymentEnvironmentDaoTest {

Expand Down Expand Up @@ -71,7 +71,7 @@ void shouldInsertUserSuccessfully() {
}

@Nested
class UpdateUser {
class UpdateEnv {

@Test
void shouldUpdateDeploymentEnvironmentSuccessfully() {
Expand Down Expand Up @@ -117,6 +117,19 @@ void shouldReturnEmptyListWhenNoDeploymentEnvironmentsFound() {
}
}

@Nested
class GetEnvironmentName {

@Test
void shouldReturnNameOfGivenEnvironment() {
var systemId = insertDeployableSystem(handle, "kiwi");
var envId = insertDeploymentEnvironmentRecord(handle, "DEV", systemId);

var name = dao.getEnvironmentName(envId);
assertThat(name).isEqualTo("DEV");
}
}

@Nested
class HardDeleteById {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
Expand All @@ -28,6 +29,7 @@
import org.junit.jupiter.api.extension.ExtendWith;
import org.kiwiproject.champagne.dao.AuditRecordDao;
import org.kiwiproject.champagne.dao.DeployableSystemDao;
import org.kiwiproject.champagne.dao.DeploymentEnvironmentDao;
import org.kiwiproject.champagne.dao.UserDao;
import org.kiwiproject.champagne.model.AuditRecord;
import org.kiwiproject.champagne.model.DeployableSystem;
Expand All @@ -45,13 +47,14 @@ class DeployableSystemResourceTest {
private static final AuditRecordDao AUDIT_RECORD_DAO = mock(AuditRecordDao.class);
private static final ApplicationErrorDao APPLICATION_ERROR_DAO = mock(ApplicationErrorDao.class);
private static final UserDao USER_DAO = mock(UserDao.class);
private static final DeploymentEnvironmentDao DEPLOYMENT_ENVIRONMENT_DAO = mock(DeploymentEnvironmentDao.class);

private static final DeployableSystemResource RESOURCE = new DeployableSystemResource(DEPLOYABLE_SYSTEM_DAO, USER_DAO, AUDIT_RECORD_DAO, APPLICATION_ERROR_DAO);
private static final DeployableSystemResource RESOURCE = new DeployableSystemResource(DEPLOYABLE_SYSTEM_DAO, USER_DAO, DEPLOYMENT_ENVIRONMENT_DAO, AUDIT_RECORD_DAO, APPLICATION_ERROR_DAO);
private static final ResourceExtension RESOURCES = JwtResourceHelper.configureJwtResource(RESOURCE);

@AfterEach
void cleanup() {
reset(DEPLOYABLE_SYSTEM_DAO, AUDIT_RECORD_DAO, USER_DAO);
reset(DEPLOYABLE_SYSTEM_DAO, AUDIT_RECORD_DAO, USER_DAO, DEPLOYMENT_ENVIRONMENT_DAO);
}

@Nested
Expand Down Expand Up @@ -131,12 +134,17 @@ void shouldReturnPagedListOfDeployableSystems() {

var systemUser = DeployableSystem.SystemUser.builder()
.userId(1L)
.displayName("Jim Bob")
.systemIdentifier("jbob")
.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));
when(DEPLOYMENT_ENVIRONMENT_DAO.getEnvironmentName(2L)).thenReturn("dev");
when(DEPLOYMENT_ENVIRONMENT_DAO.getEnvironmentName(3L)).thenReturn("test");
when(DEPLOYMENT_ENVIRONMENT_DAO.getEnvironmentName(4L)).thenReturn("prod");

var token = generateJwt(true);
var response = RESOURCES.client()
Expand All @@ -158,6 +166,9 @@ void shouldReturnPagedListOfDeployableSystems() {
verify(DEPLOYABLE_SYSTEM_DAO).findPagedDeployableSystems(0, 10);
verify(DEPLOYABLE_SYSTEM_DAO).countDeployableSystems();
verify(DEPLOYABLE_SYSTEM_DAO).findUsersForSystem(1L);
verify(DEPLOYMENT_ENVIRONMENT_DAO, times(2)).getEnvironmentName(2L);
verify(DEPLOYMENT_ENVIRONMENT_DAO).getEnvironmentName(3L);
verify(DEPLOYMENT_ENVIRONMENT_DAO).getEnvironmentName(4L);

verifyNoMoreInteractions(DEPLOYABLE_SYSTEM_DAO);
}
Expand All @@ -167,8 +178,6 @@ void shouldReturnPagedListOfReleasesWithDefaultPaging() {
var system = DeployableSystem.builder()
.id(1L)
.name("kiwi")
.devEnvironmentId(2L)
.environmentPromotionOrder("2,3,4")
.build();

var systemUser = DeployableSystem.SystemUser.builder()
Expand Down Expand Up @@ -200,6 +209,7 @@ void shouldReturnPagedListOfReleasesWithDefaultPaging() {
verify(DEPLOYABLE_SYSTEM_DAO).findUsersForSystem(1L);

verifyNoMoreInteractions(DEPLOYABLE_SYSTEM_DAO);
verifyNoInteractions(DEPLOYMENT_ENVIRONMENT_DAO);
}
}

Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/SideBar/SideBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@
</router-link>
</li>
<li class="items-center">
<router-link to="/" v-slot="{ href, navigate, isActive }">
<router-link to="/systems" v-slot="{ href, navigate, isActive }">
<a
:href="href"
@click="navigate"
Expand Down
2 changes: 2 additions & 0 deletions ui/src/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import SystemUsersView from "@/views/SystemAdmin/SystemUsersView.vue";
import UsersView from "@/views/ChampagneAdmin/UsersView.vue";
import SystemAuditsView from "@/views/SystemAdmin/SystemAuditsView.vue";
import AuditsView from "@/views/ChampagneAdmin/AuditsView.vue";
import SystemsView from "@/views/ChampagneAdmin/SystemsView.vue";

const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
Expand All @@ -35,6 +36,7 @@ const router = createRouter({
{ path: '/users', component: UsersView, name: 'users' },
{ path: '/systemAudits', component: SystemAuditsView, name: 'systemAudits' },
{ path: '/audits', component: AuditsView, name: 'audits' },
{ path: '/systems', component: SystemsView, name: 'systems' },
{ path: '/noDeployableSystem', component: NoDeployableSystemView, name: 'noDeployableSystem' }
]
},
Expand Down
Loading

0 comments on commit e873f69

Please sign in to comment.