Skip to content

Commit

Permalink
Merge pull request #43 from ConductionNL/feature/EGISTERS-40/object-a…
Browse files Browse the repository at this point in the history
…udit-trails

object audit trails
  • Loading branch information
RalkeyOfficial authored Oct 23, 2024
2 parents e66f4c4 + bc416fe commit 6fe787a
Show file tree
Hide file tree
Showing 12 changed files with 364 additions and 89 deletions.
10 changes: 5 additions & 5 deletions lib/Controller/ObjectsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public function index(ObjectService $objectService, SearchService $searchService
foreach ($results as $key => $result) {
$results[$key] = $result->getObjectArray();
}

return new JSONResponse(['results' => $results]);
}

Expand All @@ -101,7 +101,7 @@ public function index(ObjectService $objectService, SearchService $searchService
public function show(string $id): JSONResponse
{
try {
return new JSONResponse($this->objectEntityMapper->find(id: (int) $id));
return new JSONResponse($this->objectEntityMapper->find(idOrUuid: (int) $id)->getObjectArray());
} catch (DoesNotExistException $exception) {
return new JSONResponse(data: ['error' => 'Not Found'], statusCode: 404);
}
Expand Down Expand Up @@ -138,7 +138,7 @@ public function create(): JSONResponse

$this->auditTrailMapper->createAuditTrail(new: $objectEntity);

return new JSONResponse($objectEntity);
return new JSONResponse($objectEntity->getObjectArray());
}

/**
Expand Down Expand Up @@ -172,7 +172,7 @@ public function update(int $id): JSONResponse

$this->auditTrailMapper->createAuditTrail(new: $objectEntity, old: $oldObject);

return new JSONResponse($objectEntity);
return new JSONResponse($objectEntity->getOBjectArray());
}

/**
Expand Down Expand Up @@ -201,7 +201,7 @@ public function destroy(int $id): JSONResponse
* Retrieves a list of logs for an object
*
* This method returns a JSON response containing the logs for a specific object.
*
*
* @NoAdminRequired
* @NoCSRFRequired
*
Expand Down
39 changes: 39 additions & 0 deletions src/entities/auditTrail/auditTrail.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { AuditTrail } from './auditTrail'
import { TAuditTrail } from './auditTrail.types'

export const mockAuditTrailData = (): TAuditTrail[] => [
{
id: '1234a1e5-b54d-43ad-abd1-4b5bff5fcd3f',
uuid: 'uuid-1234a1e5-b54d-43ad-abd1-4b5bff5fcd3f',
schema: 1,
register: 1,
object: 1,
action: 'create',
changed: JSON.stringify({ key: 'value' }),
user: 'user1',
userName: 'User One',
session: 'session1',
request: 'request1',
ipAddress: '127.0.0.1',
version: '1.0',
created: new Date().toISOString(),
},
{
id: '5678a1e5-b54d-43ad-abd1-4b5bff5fcd3f',
uuid: 'uuid-5678a1e5-b54d-43ad-abd1-4b5bff5fcd3f',
schema: 2,
register: 2,
object: 2,
action: 'update',
changed: JSON.stringify({ key: 'value' }),
user: 'user2',
userName: 'User Two',
session: 'session2',
request: 'request2',
ipAddress: '127.0.0.2',
version: '1.1',
created: new Date().toISOString(),
},
]

export const mockAuditTrail = (data: TAuditTrail[] = mockAuditTrailData()): TAuditTrail[] => data.map(item => new AuditTrail(item))
45 changes: 45 additions & 0 deletions src/entities/auditTrail/auditTrail.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { AuditTrail } from './auditTrail'
import { mockAuditTrailData } from './auditTrail.mock'

describe('AuditTrail Entity', () => {
it('should create an AuditTrail entity with full data', () => {
const auditTrail = new AuditTrail(mockAuditTrailData()[0])

expect(auditTrail).toBeInstanceOf(AuditTrail)
expect(auditTrail).toEqual(mockAuditTrailData()[0])
expect(auditTrail.validate().success).toBe(true)
})

it('should create an AuditTrail entity with partial data', () => {
const auditTrail = new AuditTrail(mockAuditTrailData()[0])

expect(auditTrail).toBeInstanceOf(AuditTrail)
expect(auditTrail.id).toBe(null)
expect(auditTrail.uuid).toBe(mockAuditTrailData()[0].uuid)
expect(auditTrail.register).toBe(mockAuditTrailData()[0].register)
expect(auditTrail.schema).toBe(mockAuditTrailData()[0].schema)
expect(auditTrail.object).toBe(mockAuditTrailData()[0].object)
expect(auditTrail.action).toBe(mockAuditTrailData()[0].action)
expect(auditTrail.changed).toBe(mockAuditTrailData()[0].changed)
expect(auditTrail.user).toBe(mockAuditTrailData()[0].user)
expect(auditTrail.userName).toBe(mockAuditTrailData()[0].userName)
expect(auditTrail.session).toBe(mockAuditTrailData()[0].session)
expect(auditTrail.request).toBe(mockAuditTrailData()[0].request)
expect(auditTrail.ipAddress).toBe(mockAuditTrailData()[0].ipAddress)
expect(auditTrail.version).toBe(mockAuditTrailData()[0].version)
expect(auditTrail.created).toBe(mockAuditTrailData()[0].created)
expect(auditTrail.validate().success).toBe(true)
})

it('should fail validation with invalid data', () => {
const auditTrail = new AuditTrail(mockAuditTrailData()[1])

expect(auditTrail).toBeInstanceOf(AuditTrail)
expect(auditTrail.validate().success).toBe(false)
expect(auditTrail.validate().error?.issues).toContainEqual(expect.objectContaining({
path: ['id'],
message: 'Expected string, received null',
}))
})
})
59 changes: 59 additions & 0 deletions src/entities/auditTrail/auditTrail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { SafeParseReturnType, z } from 'zod'
import { TAuditTrail } from './auditTrail.types'

export class AuditTrail implements TAuditTrail {

public id: string
public uuid: string
public schema: number
public register: number
public object: number
public action: string
public changed: string
public user: string
public userName: string
public session: string
public request: string
public ipAddress: string
public version: string
public created: string

constructor(auditTrail: TAuditTrail) {
this.id = auditTrail.id || null
this.uuid = auditTrail.uuid || null
this.schema = auditTrail.schema || 0
this.register = auditTrail.register || 0
this.object = auditTrail.object || 0
this.action = auditTrail.action || ''
this.changed = auditTrail.changed || ''
this.user = auditTrail.user || ''
this.userName = auditTrail.userName || ''
this.session = auditTrail.session || ''
this.request = auditTrail.request || ''
this.ipAddress = auditTrail.ipAddress || ''
this.version = auditTrail.version || ''
this.created = auditTrail.created || ''
}

public validate(): SafeParseReturnType<TAuditTrail, unknown> {
const schema = z.object({
id: z.string().nullable(),
uuid: z.string().uuid().nullable(),
schema: z.number(),
register: z.number(),
object: z.number(),
action: z.string(),
changed: z.string(),
user: z.string(),
userName: z.string(),
session: z.string(),
request: z.string(),
ipAddress: z.string(),
version: z.string(),
created: z.string(),
})

return schema.safeParse(this)
}

}
16 changes: 16 additions & 0 deletions src/entities/auditTrail/auditTrail.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export type TAuditTrail = {
id: string
uuid: string
schema: number // schema ID
register: number // register ID
object: number // object ID
action: string
changed: string // JSON object
user: string
userName: string
session: string
request: string
ipAddress: string
version: string
created: string
}
4 changes: 4 additions & 0 deletions src/entities/auditTrail/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './auditTrail.ts'
export * from './auditTrail.types.ts'
export * from './auditTrail.mock.ts'

1 change: 1 addition & 0 deletions src/entities/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './schema/index.js'
export * from './register/index.js'
export * from './source/index.js'
export * from './object/index.js'
export * from './auditTrail/index.js'
7 changes: 7 additions & 0 deletions src/modals/Modals.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
<script setup>
import { navigationStore } from '../store/store.js'
</script>

<template>
<!-- Placeholder Div -->
<div>
Expand All @@ -11,6 +15,7 @@
<DeleteSource />
<EditObject />
<DeleteObject />
<ViewObjectAuditTrail v-if="navigationStore.modal === 'viewObjectAuditTrail'" />
</div>
</template>

Expand All @@ -25,6 +30,7 @@ import EditSource from './source/EditSource.vue'
import DeleteSource from './source/DeleteSource.vue'
import EditObject from './object/EditObject.vue'
import DeleteObject from './object/DeleteObject.vue'
import ViewObjectAuditTrail from './objectAuditTrail/ViewObjectAuditTrail.vue'
export default {
name: 'Modals',
Expand All @@ -39,6 +45,7 @@ export default {
DeleteSource,
EditObject,
DeleteObject,
ViewObjectAuditTrail,
},
}
</script>
102 changes: 102 additions & 0 deletions src/modals/objectAuditTrail/ViewObjectAuditTrail.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<script setup>
import { objectStore, navigationStore } from '../../store/store.js'
</script>

<template>
<NcModal label-id="View Object Audit Trail modal"
@close="closeDialog">
<div class="modal__content">
<div class="audit-item">
<h3>Audit Trail ID: {{ auditTrail.id }}</h3>

<p><strong>Action:</strong> {{ auditTrail.action }}</p>
<p><strong>User:</strong> {{ auditTrail.userName }} ({{ auditTrail.user }})</p>
<p><strong>Session:</strong> {{ auditTrail.session }}</p>
<p><strong>IP Address:</strong> {{ auditTrail.ipAddress }}</p>
<p><strong>Created:</strong> {{ new Date(auditTrail.created).toLocaleString() }}</p>

<div v-if="auditTrail.changed">
<h4>Changes:</h4>
<ul>
<li v-for="(change, key) in auditTrail.changed" :key="key">
<strong>{{ key }}:</strong><br>
<span>Old: {{ change.old ?? 'N/A' }}</span><br>
<span>New: {{ change.new ?? 'N/A' }}</span>
</li>
</ul>
</div>

<div class="navigation-buttons">
<NcButton>
Go to linked Schema
</NcButton>
<NcButton>
Go to linked Register
</NcButton>
<NcButton>
Go to linked Object
</NcButton>
</div>
</div>

<NcButton @click="closeDialog">
<template #icon>
<Cancel :size="20" />
</template>
Close
</NcButton>
</div>
</NcModal>
</template>

<script>
import {
NcModal,
NcButton,
} from '@nextcloud/vue'
import Cancel from 'vue-material-design-icons/Cancel.vue'
export default {
name: 'ViewObjectAuditTrail',
components: {
NcModal,
NcButton,
Cancel,
},
data() {
return {
auditTrail: {}, // Initialize with an empty object
}
},
mounted() {
// Assuming objectStore.auditTrailItem is a single audit trail object
this.auditTrail = objectStore.auditTrailItem || {}
},
methods: {
closeDialog() {
navigationStore.setModal(null)
objectStore.setAuditTrailItem(null)
},
},
}
</script>

<style scoped>
.modal__content {
margin: 0.8rem;
}
.audit-item {
border-bottom: 1px solid #ccc;
padding: 0 0 10px 0;
margin: 0 0 10px 0;
}
.navigation-buttons {
margin-top: 10px;
display: flex;
gap: 10px;
justify-content: center;
}
</style>
Loading

0 comments on commit 6fe787a

Please sign in to comment.