Skip to content

Commit

Permalink
feat: ✨ show short logs about project to users
Browse files Browse the repository at this point in the history
  • Loading branch information
ArnaudTA committed Oct 3, 2024
1 parent 9d69357 commit 4888e64
Show file tree
Hide file tree
Showing 27 changed files with 660 additions and 202 deletions.
79 changes: 79 additions & 0 deletions apps/client/cypress/e2e/specs/01-logs.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import type { ProjectV2 } from '@cpn-console/shared'
import { getModel } from '../support/func.js'

const projects = getModel('project')
const betaapp = projects.find(({ name }) => name === 'betaapp') as ProjectV2

describe('Create Project', () => {
beforeEach(() => {
})

it('Should display project logs as owner', () => {
cy.kcLogin('test')
cy.intercept('GET', '/api/v1/logs?*').as('listLogs')

cy.goToProjects()
cy.getByDataTestid(`projectTile-${betaapp.name}`).click()
cy.getByDataTestid('displayLogsPanel')
.should('not.be.visible')
cy.getByDataTestid('displayLogsBtn')
.should('be.visible')
.click()
cy.getByDataTestid('displayLogsPanel')
.should('be.visible')
.within(() => {
cy.get('span').should('contain', '1 - 2 sur 2')
})
cy.getByDataTestid('replayHooksBtn')
.click()
cy.getByDataTestid('displayLogsPanel')
.should('be.visible')
.within(() => {
cy.get('span').should('contain', '1 - 3 sur 3')
})
cy.getByDataTestid('mainMenu').should('be.visible')
cy.getByDataTestid('menuMyProjects').click()
.url().should('contain', '/projects')
cy.getByDataTestid('displayLogsPanel')
.should('not.be.visible')
})

it('Should handle display project logs as manager or memebr of project', () => {
cy.kcLogin('tcolin')
cy.intercept('GET', '/api/v1/logs?*').as('listLogs')

// as owner
cy.goToProjects()
cy.getByDataTestid(`projectTile-${betaapp.name}`).click()
cy.getByDataTestid('displayLogsPanel')
.should('not.be.visible')
cy.getByDataTestid('displayLogsBtn')
.should('be.visible')
.click()
cy.getByDataTestid('displayLogsPanel')
.should('be.visible')
.within(() => {
cy.get('span').should('contain', '1 - 3 sur 3')
})
cy.getByDataTestid('menuRepos').click()
cy.getByDataTestid('displayLogsPanel')
.should('be.visible')
.within(() => {
cy.get('span').should('contain', '1 - 3 sur 3')
})

// as member
cy.getByDataTestid('menuMyProjects').click()
.url().should('contain', '/projects')

cy.goToProjects()
cy.getByDataTestid(`projectTile-candilib`).click()
cy.getByDataTestid('displayLogsPanel')
.should('not.exist')
cy.getByDataTestid('displayLogsBtn')
.should('not.exist')
cy.getByDataTestid('menuRepos').click()
cy.getByDataTestid('displayLogsBtn')
.should('not.exist')
})
})
176 changes: 176 additions & 0 deletions apps/client/src/components/LogsViewer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<script lang="ts" setup>
import { ref } from 'vue'
import type { CleanLog, Log, XOR } from '@cpn-console/shared'
// @ts-ignore 'vue3-json-viewer' missing types
import { JsonViewer } from 'vue3-json-viewer'
const props = withDefaults(defineProps<{
totalLength: number
logs: XOR<CleanLog, Log>[]
isUpdating: boolean
page: number
step: number
mode: 'full' | 'hide' | 'hideDetails'
paginationPosition: 'top' | 'bottom' | 'both'
hideTotalEvents: boolean
headerClass: string
bodyClass: string
}>(), {
totalLength: 0,
paginationPosition: 'both',
hideTotalEvents: false,
headerClass: '',
bodyClass: '',
})
const emits = defineEmits<{
movePage: [index: number]
}>()
const hideLogs = ref(props.mode === 'hide')
const hideLogDetails = ref(props.mode === 'hideDetails')
type LogModelSliced = Omit<Log['data'], 'failed'>
| Omit<Log['data'], 'failed' | 'totalExecutionTime'>
function sliceLog(log: Log | CleanLog): LogModelSliced {
const data = log.data
if (!data.failed || data.warning?.length) {
const {
totalExecutionTime: _t,
failed: _f,
warning: _w,
...logSliced
} = data
return logSliced
}
const {
totalExecutionTime: _t,
...logSliced
} = data
return logSliced
}
async function showLogs(index: number) {
emits('movePage', index)
}
</script>

<template>
<div
:class="`flex justify-between ${headerClass}`"
>
<DsfrAlert
v-if="!isUpdating && !hideTotalEvents"
:description="!totalLength ? 'Aucun événement à afficher' : `Total : ${totalLength} événements`"
data-testid="logCountInfo"
type="info"
small
/>
<div
class="flex gap-2 w-min"
>
<DsfrButton
v-if="mode !== 'hide'"
data-testid="logsDetailsBtn"
:title="hideLogDetails ? 'Afficher les logs en entier' : 'Masquer les clés non essentielles des logs'"
secondary
icon-only
:icon="hideLogDetails ? 'ri:filter-off-fill' : 'ri:filter-fill'"
@click="hideLogDetails = !hideLogDetails"
/>
<DsfrButton
v-if="mode !== 'hide'"
data-testid="showLogsBtn"
:title="hideLogs ? 'Afficher les logs' : 'Masquer les logs'"
secondary
icon-only
:icon="hideLogs ? 'ri:eye-off-fill' : 'ri:eye-fill'"
@click="hideLogs = !hideLogs"
/>
<DsfrButton
class="shrink h-min"
data-testid="refreshBtn"
title="Renouveler l'appel"
secondary
icon-only
:disabled="isUpdating"
:icon="{ name: 'ri:refresh-fill', animation: isUpdating ? 'spin' : '' }"
@click="showLogs(page)"
/>
</div>
</div>
<div
:class="bodyClass || 'mt-5'"
>
<PaginationCt
v-if="['top', 'both'].includes(paginationPosition)"
:length="totalLength"
:page="page"
:step="step"
@set-page="showLogs($event)"
/>
<div
v-for="log in logs"
:key="log.id"
:class="`log-box my-5 border-solid ${log.data?.warning?.length ? 'log-box--warning' : log.data?.failed ? 'log-box--error' : 'log-box--success'}`"
>
<div
class="flex flex-wrap justify-between"
>
<DsfrBadge
:label="log.action"
:type="log.data?.failed ? 'error' : log.data?.warning?.length ? 'warning' : 'success'"
/>
<div
class="flex gap-2"
>
<DsfrBadge
v-if="log?.data?.totalExecutionTime"
:label="`${log.data.totalExecutionTime} ms`"
no-icon
/>
<DsfrBadge
:label="(new Date(log.createdAt)).toLocaleString()"
no-icon
/>
</div>
</div>
<JsonViewer
v-if="!hideLogs"
:data-testid="`${log.id}-json`"
:value="hideLogDetails ? sliceLog(log) : log"
class="json-box !my-0"
copyable
/>
<div
style="display: none;"
class="flex flex-wrap justify-between"
>
<DsfrBadge
:label="`Log ID: ${log.id}`"
type="new"
no-icon
/>
<DsfrBadge
:label="`user ID: ${log.userId}`"
type="new"
no-icon
/>
<DsfrBadge
v-if="log.requestId"
:label="`Request ID: ${log.requestId}`"
type="new"
no-icon
/>
</div>
</div>
<PaginationCt
v-if="['bottom', 'both'].includes(paginationPosition)"
:length="totalLength"
:page="page"
:step="step"
@set-page="showLogs($event)"
/>
</div>
</template>
16 changes: 7 additions & 9 deletions apps/client/src/components/PaginationCt.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ const props = withDefaults(defineProps<{
length: number
step: number
page: number
isUpdating: boolean
}>(), {
length: 0,
step: 10,
page: 0,
isUpdating: false,
})
const emit = defineEmits(['setPage'])
Expand All @@ -30,41 +28,41 @@ const currentStart = computed(() => Math.floor(props.page * props.step))
icon="ri:arrow-left-double-fill"
:icon-only="true"
title="Voir la première page"
:disabled="props.isUpdating || props.page <= 0"
:disabled="page <= 0"
data-testid="seeFirstPageBtn"
@click="emit('setPage', 0)"
/>
<DsfrButton
icon="ri:arrow-drop-left-line"
:icon-only="true"
title="Voir la page précédente"
:disabled="props.isUpdating || props.page <= 0"
:disabled="page <= 0"
data-testid="seePreviousPageBtn"
@click="emit('setPage', Math.max(props.page - 1, 0))"
/>
</div>
<p
<span
class="flex items-center"
data-testid="positionInfo"
>
{{ `${currentStart + 1} - ${Math.min(currentStart + props.step, props.length)} sur ${props.length}` }}
</p>
{{ length ? `${currentStart + 1} - ${Math.min(currentStart + props.step, props.length)} sur ${props.length}` : `0 - 0 sur 0` }}
</span>
<div
class="flex gap-2"
>
<DsfrButton
icon="ri:arrow-drop-right-line"
:icon-only="true"
title="Voir la page suivante"
:disabled="props.isUpdating || props.page >= maxPage"
:disabled="(page * step + step) >= length"
data-testid="seeNextPageBtn"
@click="emit('setPage', Math.min(maxPage, page + 1))"
/>
<DsfrButton
icon="ri:arrow-right-double-line"
:icon-only="true"
title="Voir la dernière page"
:disabled="props.isUpdating || props.page >= maxPage"
:disabled="(page * step + step) >= length"
data-testid="seeLastPageBtn"
@click="emit('setPage', maxPage)"
/>
Expand Down
Loading

0 comments on commit 4888e64

Please sign in to comment.