Skip to content

Commit

Permalink
Merge pull request #3044 from NationalSecurityAgency/t#1604/fixes_and…
Browse files Browse the repository at this point in the history
…_additions

T#1604/fixes and additions
  • Loading branch information
sudo-may authored Dec 17, 2024
2 parents 14ab5ce + 6a35635 commit 32292f1
Show file tree
Hide file tree
Showing 14 changed files with 215 additions and 13 deletions.
43 changes: 37 additions & 6 deletions dashboard/src/components/users/UserArchivePage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ limitations under the License.
<script setup>
import { useRoute } from 'vue-router';
import { computed, onMounted, ref } from 'vue';
import { onMounted, ref } from 'vue';
import { useSkillsAnnouncer } from '@/common-components/utilities/UseSkillsAnnouncer.js';
import { useUserInfo } from '@/components/utils/UseUserInfo.js';
import { useAppConfig } from '@/common-components/stores/UseAppConfig.js';
import { useFocusState } from '@/stores/UseFocusState.js';
import { useResponsiveBreakpoints } from '@/components/utils/misc/UseResponsiveBreakpoints.js';
import UsersService from '@/components/users/UsersService.js'
import SkillsOverlay from '@/components/utils/SkillsOverlay.vue';
import ExistingUserInput from '@/components/utils/ExistingUserInput.vue';
import NoContent2 from '@/components/utils/NoContent2.vue';
import SkillsDisplayPathAppendValues from '@/router/SkillsDisplayPathAppendValues.js';
import SkillsDataTable from '@/components/utils/table/SkillsDataTable.vue';
const announcer = useSkillsAnnouncer()
const route = useRoute()
Expand Down Expand Up @@ -74,7 +74,7 @@ const loadData = () => {
return UsersService.getArchivedUsers(route.params.projectId, params)
.then((res) => {
archivedUsers.value = res.data
options.value.pagination.totalRows = archivedUsers.value.length
options.value.pagination.totalRows = res.totalCount
options.value.busy = false
})
.finally(() => {
Expand All @@ -95,6 +95,27 @@ const restoreUser = (user) => {
})
})
}
const calculateClientDisplayRoute = (props) => {
return {
name: `SkillsDisplay${SkillsDisplayPathAppendValues.SkillsDisplayPreview}`,
params: {
projectId: route.params.projectId,
userId: props.userId,
dn: props.dn
}
}
}
const pageChanged = (pagingInfo) => {
options.value.pagination.pageSize = pagingInfo.rows
options.value.pagination.currentPage = pagingInfo.page + 1
loadData()
}
const sortField = (column) => {
// set to the first page
options.value.pagination.currentPage = 1
loadData()
}
</script>

Expand All @@ -113,16 +134,20 @@ const restoreUser = (user) => {
:loading="options.busy"
show-gridlines
striped-rows
lazy
paginator
id="userArchiveTable"
data-cy="userArchiveTable"
:total-records="options.pagination.totalRows"
:rows="options.pagination.pageSize"
:rowsPerPageOptions="options.pagination.possiblePageSizes"
@page="pageChanged"
@sort="sortField"
v-model:sort-field="sortInfo.sortBy"
v-model:sort-order="sortInfo.sortOrder">

<template #paginatorstart>
<span>Total Rows:</span> <span class="font-semibold" data-cy=skillsBTableTotalRows>{{ archivedUsers.length }}</span>
<span>Total Rows:</span> <span class="font-semibold" data-cy=skillsBTableTotalRows>{{ options.pagination.totalRows }}</span>
</template>

<Column v-for="col of options.fields" :key="col.key" :field="col.key" :sortable="col.sortable"
Expand All @@ -134,7 +159,13 @@ const restoreUser = (user) => {
<div v-if="slotProps.field === 'userIdForDisplay'" class="flex flex-row flex-wrap"
:data-cy="`archivedUser_${slotProps.data.userId}`">
<div class="flex align-items-start justify-content-start">
{{ userInfo.getUserDisplay(slotProps.data, true) }}
<router-link
:to="calculateClientDisplayRoute(slotProps.data)"
aria-label="View user details"
data-cy="usersTable_viewDetailsLink"
>
{{ userInfo.getUserDisplay(slotProps.data, true) }}
</router-link>
</div>
<div class="flex flex-grow-1 align-items-start justify-content-end">
<SkillsButton :data-cy="`restoreUser-${slotProps.data.userId}`"
Expand Down
7 changes: 7 additions & 0 deletions dashboard/src/components/users/UserPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const projectUserState = useProjectUserState()
const appConfig = useAppConfig()
const isLoading = ref(true)
const isUserArchived = ref(false)
const userTitle = ref( '')
const userIdForDisplay = ref( '')
const tags = ref( '')
Expand Down Expand Up @@ -71,6 +72,9 @@ const loadUserInfo = () => {
tags.value = processUserTags(response);
});
}
UsersService.isUserArchived(route.params.projectId, route.params.userId).then((result) => {
isUserArchived.value = result
})
const userDetails = UsersService.getUserInfo(route.params.projectId, route.params.userId)
.then((result) => {
Expand Down Expand Up @@ -116,6 +120,9 @@ const processUserTags = (userTags) =>{
<template>
<div>
<page-header :loading="isLoading" :options="headerOptions">
<template #right-of-header>
<Tag class="ml-2" data-cy="archivedUserTag" severity="danger" v-if="isUserArchived"><i class="fas fa-archive mr-1" aria-hidden="true"/>Archived</Tag>
</template>
<template #subSubTitle v-if="tags">
<span v-for="(tag, index) in tags" :key="index" data-cy="userTagHeader">
<span class="text-muted">{{tag.label}}: </span>
Expand Down
4 changes: 4 additions & 0 deletions dashboard/src/components/users/UsersService.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,9 @@ export default {
restoreArchivedUser(projectId, userId) {
return axios.post(`/admin/projects/${encodeURIComponent(projectId)}/users/${userId}/restore`)
.then((res) => res.data)
},
isUserArchived(projectId, userId) {
return axios.get(`/admin/projects/${encodeURIComponent(projectId)}/users/${userId}/isArchived`)
.then((res) => res.data.success)
}
}
24 changes: 24 additions & 0 deletions e2e-tests/cypress/e2e/users_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,30 @@ describe('Users Tests', () => {
[{ colIndex: 1, value: '[email protected]' }, { colIndex: 4, value: dateFormatter(m.clone().add(3, 'day')) }],
[{ colIndex: 1, value: '[email protected]' }, { colIndex: 4, value: dateFormatter(m.clone().add(2, 'day')) }],
], 5);

cy.get('[data-p-index="0"] [data-pc-name="rowcheckbox"]').click()

cy.get('[data-cy="archiveUsersTableBtn"]').should('be.enabled');
cy.get('[data-cy="archiveUsersTableBtn"]').click()

cy.wait('@archivedUsers');
cy.get('[data-cy="archiveUsersTableBtn"]').should('be.disabled');

// navigate to back archived users
cy.get('[data-cy="userArchiveBtn"]').should('be.enabled');
cy.get('[data-cy="userArchiveBtn"]').click()
cy.wait('@getArchivedUsers')

cy.validateTable(archivedUsersTableSelector, [
[{ colIndex: 0, value: '[email protected]' }],
], 5);


cy.get(`${archivedUsersTableSelector} [data-cy="usersTable_viewDetailsLink"]`).first().click();
cy.get('[data-cy="subPageHeader"]').contains("User's Display");
cy.contains("ID: [email protected]");
cy.get('[data-cy="skillsDisplayHome"] [data-cy="subjectTileBtn"]')
cy.get('[data-cy="archivedUserTag"]').should('be.visible');
});

it('different page sizes', () => {
Expand Down
10 changes: 10 additions & 0 deletions service/src/main/java/skills/controller/AdminController.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -1877,5 +1877,15 @@ class AdminController {
projAdminService.restoreArchiveUser(projectId, userKey)
return new RequestResult(success: true)
}


@RequestMapping(value = "/projects/{projectId}/users/{userKey}/isArchived", method = RequestMethod.GET, produces = "application/json")
RequestResult isUserArchived(@PathVariable("projectId") String projectId,
@PathVariable("userKey") String userKey) {
SkillsValidator.isNotBlank(projectId, "Project Id")
SkillsValidator.isNotBlank(userKey, "userKey")

return new RequestResult(success: projAdminService.isUserArchived(projectId, userKey))
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,10 @@ class ProjAdminService {
))
}

@Transactional(readOnly = true)
boolean isUserArchived(String projectId, String userId) {
return archivedUserRepo.existsByProjectIdAndUserId(projectId, userId)
}

TableResult findAllArchivedUsers(String projectId, PageRequest pageRequest) {
Page<ArchivedUsersRepo.ArchivedUserWithAttrs> results = archivedUserRepo.findAllByProjectId(projectId, pageRequest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ class RankingLoader {
}).collect { skills.controller.result.model.LevelDefinitionRes levelMeta ->
Integer numUsers
if (tagKey && tagFilter) {
numUsers = achievedLevelRepository.countByProjectIdAndSkillIdAndLevelAndUserTag(projectId, levelMeta.level, tagKey, tagFilter)
numUsers = achievedLevelRepository.countByProjectIdAndLevelAndUserTag(projectId, levelMeta.level, tagKey, tagFilter)
} else if (subjectId) {
numUsers = achievedLevelRepository.countByProjectIdAndSkillIdAndLevel(projectId, subjectId, levelMeta.level)
} else {
Expand All @@ -319,7 +319,7 @@ class RankingLoader {
if (includeZeroLevel) {
Integer numUsers
if (tagKey && tagFilter) {
numUsers = achievedLevelRepository.countByProjectIdAndSkillIdAndLevelAndUserTag(projectId, 0, tagKey, tagFilter)
numUsers = achievedLevelRepository.countByProjectIdAndLevelAndUserTag(projectId, 0, tagKey, tagFilter)
} else {
numUsers = achievedLevelRepository.countByProjectIdAndSkillIdAndLevel(projectId, subjectId, 0)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@ interface ArchivedUsersRepo extends CrudRepository<ArchivedUser, Long> {

@Nullable
ArchivedUser findByProjectIdAndUserId(String projectId, String userId)

Boolean existsByProjectIdAndUserId(String projectId, String userId)
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,10 @@ interface UserAchievedLevelRepo extends CrudRepository<UserAchievement, Integer>
ua.level = ?2 AND
ut.key = ?3 AND
ut.value = ?4 AND
ua.skillId is null AND
not exists (select 1 from ArchivedUser au where au.userId = ua.userId and au.projectId = ?1)
''')
Integer countByProjectIdAndSkillIdAndLevelAndUserTag(String projectId, int level, String userTagKey, String userTagValue)
Integer countByProjectIdAndLevelAndUserTag(String projectId, int level, String userTagKey, String userTagValue)

void deleteAllByProjectIdAndUserId(String projectId, String userId)
void deleteByProjectIdAndSkillId(String projectId, String skillId)
Expand Down Expand Up @@ -833,7 +834,8 @@ interface UserAchievedLevelRepo extends CrudRepository<UserAchievement, Integer>
from UserAchievement as ua
where
ua.skillId = :skillId and
ua.projectId = :projectId
ua.projectId = :projectId and
not exists (select 1 from ArchivedUser au where au.userId = ua.userId and au.projectId = :projectId)
group by ua.achievedOn, ua.level
''')
List<SkillLevelDayUserCount> countNumUsersOverTimeAndLevelByProjectIdAndSkillId(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,8 @@ interface UserPointsRepo extends CrudRepository<UserPoints, Integer> {
AND up.project_id = ?1
AND up.skill_id IS NULL
AND ut.key = ?2
AND ut.value = ?3''', nativeQuery = true)
AND ut.value = ?3
AND not exists (select 1 from archived_users au where au.user_id = up.user_id and au.project_id = ?1)''', nativeQuery = true)
Long countDistinctUserIdByProjectIdAndUserTag(String projectId, String userTagKey, String userTagValue)

@Query(value = '''SELECT COUNT(*)
Expand All @@ -636,7 +637,9 @@ interface UserPointsRepo extends CrudRepository<UserPoints, Integer> {
usr.project_id = ?1 and
usr.skill_id is null and
(lower(CONCAT(usattr.first_name, ' ', usattr.last_name, ' (', usattr.user_id_for_display, ')')) like lower(CONCAT('%', ?4, '%')) OR
lower(usattr.user_id_for_display) like lower(CONCAT('%', ?4, '%'))))
lower(usattr.user_id_for_display) like lower(CONCAT('%', ?4, '%')))
AND not exists (select 1 from archived_users au where au.user_id = usr.user_id and au.project_id = ?1)
)
AS temp''',
nativeQuery = true)
Long countDistinctUserIdByProjectIdAndUserTagAndUserIdLike(String projectId, String userTagKey, String userTagValue, String userId)
Expand Down Expand Up @@ -831,6 +834,7 @@ interface UserPointsRepo extends CrudRepository<UserPoints, Integer> {
lower(ua.user_id_for_display) like lower(CONCAT('%', ?5, '%'))
) and
up.skill_id is null
AND not exists (select 1 from archived_users au where au.user_id = up.user_id and au.project_id = ?1)
GROUP BY up.user_id''', nativeQuery = true)
List<ProjectUser> findDistinctProjectUsersByProjectIdAndUserTagAndUserIdLike(String projectId, String usersTableAdditionalUserTagKey, String userTagKey, String userTagValue, String userId, Pageable pageable)

Expand Down
12 changes: 11 additions & 1 deletion service/src/test/java/skills/intTests/AdminEditSpecs.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -1446,7 +1446,7 @@ class AdminEditSpecs extends DefaultIntSpec {
updatedSkill.pointIncrement == 500
}
def "archiving a user filter that user from user tables"() {
def "archiving a user filters that user from user tables"() {
def project = createProject()
def subject = createSubject()
def skill1 = createSkill(1, 1, 1, 0, 5)
Expand All @@ -1466,6 +1466,10 @@ class AdminEditSpecs extends DefaultIntSpec {
def user1 = users[0]
def user2 = users[1]
SkillsService rootUser = createRootSkillService()
rootUser.saveUserTag(user1, 'someTag', ["ABC"])
rootUser.saveUserTag(user2, 'someTag', ["ABC"])
skillsService.addSkill(skill1, user1, new Date().minus(5))
skillsService.addSkill(skill1, user1, new Date().minus(1))
skillsService.addSkill(skill1, user2, new Date())
Expand All @@ -1486,6 +1490,9 @@ class AdminEditSpecs extends DefaultIntSpec {
def badgeUsers = skillsService.getBadgeUsers(project.projectId, badgeId)
assert badgeUsers.data.find { it.userId == user1 && it.totalPoints == 100 }
assert badgeUsers.data.find { it.userId == user2 && it.totalPoints == 60 }
def userTagUsers = skillsService.getUserTagUsers(project.projectId, 'someTag', 'ABC')
assert userTagUsers.data.find { it.userId == user1 && it.totalPoints == 100 }
assert userTagUsers.data.find { it.userId == user2 && it.totalPoints == 60 }
when:
Expand All @@ -1496,6 +1503,7 @@ class AdminEditSpecs extends DefaultIntSpec {
def skill1UsersAfterArchive = skillsService.getSkillUsers(project.projectId, skill1.skillId)
def skill2UsersAfterArchive = skillsService.getSkillUsers(project.projectId, skill2.skillId)
def badgeUsersAfterArchive = skillsService.getBadgeUsers(project.projectId, badgeId)
def userTagUsersAfterArchive = skillsService.getUserTagUsers(project.projectId, 'someTag', 'ABC')
then:
!projectUsersAfterArchive.data.find { it.userId == user1 }
Expand All @@ -1508,5 +1516,7 @@ class AdminEditSpecs extends DefaultIntSpec {
skill2UsersAfterArchive.data.find { it.userId == user2 && it.totalPoints == 10 }
!badgeUsersAfterArchive.data.find { it.userId == user1 }
badgeUsersAfterArchive.data.find { it.userId == user2 && it.totalPoints == 60 }
!userTagUsersAfterArchive.data.find { it.userId == user1 }
userTagUsersAfterArchive.data.find { it.userId == user2 && it.totalPoints == 60 }
}
}
Loading

0 comments on commit 32292f1

Please sign in to comment.