Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feature/saterday-night-fixes'
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenvdlinde committed Oct 21, 2024
2 parents e6e089c + d07ab41 commit 902dcec
Show file tree
Hide file tree
Showing 10 changed files with 422 additions and 34 deletions.
2 changes: 2 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@
'routes' => [
['name' => 'dashboard#page', 'url' => '/', 'verb' => 'GET'],
['name' => 'registers#objects', 'url' => '/api/registers-objects/{register}/{schema}', 'verb' => 'GET'],
['name' => 'objects#auditTrails', 'url' => '/api/audit-trails/{id}', 'verb' => 'GET'],

],
];
31 changes: 29 additions & 2 deletions lib/Controller/ObjectsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use OCA\OpenRegister\Service\SearchService;
use OCA\OpenRegister\Db\ObjectEntity;
use OCA\OpenRegister\Db\ObjectEntityMapper;
use OCA\OpenRegister\Db\AuditTrailMapper;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Http\JSONResponse;
Expand All @@ -25,7 +26,8 @@ public function __construct(
$appName,
IRequest $request,
private readonly IAppConfig $config,
private readonly ObjectEntityMapper $objectEntityMapper
private readonly ObjectEntityMapper $objectEntityMapper,
private readonly AuditTrailMapper $auditTrailMapper
)
{
parent::__construct($appName, $request);
Expand Down Expand Up @@ -64,12 +66,21 @@ public function index(ObjectService $objectService, SearchService $searchService
{
$filters = $this->request->getParams();
$fieldsToSearch = ['uuid', 'register', 'schema'];
$extend = ['schema', 'register'];

$searchParams = $searchService->createMySQLSearchParams(filters: $filters);
$searchConditions = $searchService->createMySQLSearchConditions(filters: $filters, fieldsToSearch: $fieldsToSearch);
$filters = $searchService->unsetSpecialQueryParams(filters: $filters);

return new JSONResponse(['results' => $this->objectEntityMapper->findAll(filters: $filters, searchConditions: $searchConditions, searchParams: $searchParams)]);
// @todo: figure out how to use extend here
$results = $this->objectEntityMapper->findAll(filters: $filters);

// We dont want to return the entity, but the object (and kant reley on the normal serilzier)
foreach ($results as $key => $result) {
$results[$key] = $result->getObjectArray();
}

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

/**
Expand Down Expand Up @@ -162,4 +173,20 @@ public function destroy(int $id): JSONResponse

return new JSONResponse([]);
}

/**
* Retrieves a list of logs for an object
*
* This method returns a JSON response containing the logs for a specific object.
*
* @NoAdminRequired
* @NoCSRFRequired
*
* @param string $id The ID of the object to delete
* @return JSONResponse An empty JSON response
*/
public function auditTrails(int $id): JSONResponse
{
return new JSONResponse($this->auditTrailMapper->findAll(filters: ['object' => $id]));
}
}
80 changes: 80 additions & 0 deletions lib/Db/AuditTrail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

namespace OCA\OpenRegister\Db;

use DateTime;
use JsonSerializable;
use OCP\AppFramework\Db\Entity;

class AuditTrail extends Entity implements JsonSerializable
{
protected ?string $uuid = null;
protected ?int $object = null;
protected ?string $action = null;
protected ?array $changed = null;
protected ?string $user = null;
protected ?string $userName = null;
protected ?string $session = null;
protected ?string $request = null;
protected ?string $ipAddress = null;
protected ?DateTime $created = null;

public function __construct() {
$this->addType(fieldName: 'uuid', type: 'string');
$this->addType(fieldName: 'object', type: 'integer');
$this->addType(fieldName: 'action', type: 'string');
$this->addType(fieldName: 'changed', type: 'json');
$this->addType(fieldName: 'user', type: 'string');
$this->addType(fieldName: 'userName', type: 'string');
$this->addType(fieldName: 'session', type: 'string');
$this->addType(fieldName: 'request', type: 'string');
$this->addType(fieldName: 'ipAddress', type: 'string');
$this->addType(fieldName: 'created', type: 'datetime');
}

public function getJsonFields(): array
{
return array_keys(
array_filter($this->getFieldTypes(), function ($field) {
return $field === 'json';
})
);
}

public function hydrate(array $object): self
{
$jsonFields = $this->getJsonFields();

foreach ($object as $key => $value) {
if (in_array($key, $jsonFields) === true && $value === []) {
$value = null;
}

$method = 'set'.ucfirst($key);

try {
$this->$method($value);
} catch (\Exception $exception) {
}
}

return $this;
}

public function jsonSerialize(): array
{
return [
'id' => $this->id,
'uuid' => $this->uuid,
'object' => $this->object,
'action' => $this->action,
'changed' => $this->changed,
'user' => $this->user,
'userName' => $this->userName,
'session' => $this->session,
'request' => $this->request,
'ipAddress' => $this->ipAddress,
'created' => isset($this->created) ? $this->created->format('c') : null
];
}
}
75 changes: 75 additions & 0 deletions lib/Db/AuditTrailMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

namespace OCA\OpenRegister\Db;

use OCA\OpenRegister\Db\Log;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use Symfony\Component\Uid\Uuid;

class AuditTrailMapper extends QBMapper
{
public function __construct(IDBConnection $db)
{
parent::__construct($db, 'openregister_audit_trails');
}

public function find(int $id): Log
{
$qb = $this->db->getQueryBuilder();

$qb->select('*')
->from('openregister_audit_trails')
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))
);

return $this->findEntity(query: $qb);
}

public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array
{
$qb = $this->db->getQueryBuilder();

$qb->select('*')
->from('openregister_audit_trails')
->setMaxResults($limit)
->setFirstResult($offset);

foreach ($filters as $filter => $value) {
if ($value === 'IS NOT NULL') {
$qb->andWhere($qb->expr()->isNotNull($filter));
} elseif ($value === 'IS NULL') {
$qb->andWhere($qb->expr()->isNull($filter));
} else {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
}
}

if (!empty($searchConditions)) {
$qb->andWhere('(' . implode(' OR ', $searchConditions) . ')');
foreach ($searchParams as $param => $value) {
$qb->setParameter($param, $value);
}
}

return $this->findEntities(query: $qb);
}

public function createFromArray(array $object): Log
{
$log = new Log();
$log->hydrate(object: $object);

// Set uuid if not provided
if ($log->getUuid() === null) {
$log->setUuid(Uuid::v4());
}

return $this->insert(entity: $log);
}

// We dont need update as we dont change the log
}
29 changes: 18 additions & 11 deletions lib/Db/ObjectEntity.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class ObjectEntity extends Entity implements JsonSerializable
protected ?string $schema = null;
protected ?string $version = null;
protected ?array $object = [];
protected ?string $textRepresentation = null;
protected ?DateTime $updated = null;
protected ?DateTime $created = null;

Expand All @@ -22,6 +23,7 @@ public function __construct() {
$this->addType(fieldName:'schema', type: 'string');
$this->addType(fieldName: 'version', type: 'string');
$this->addType(fieldName:'object', type: 'json');
$this->addType(fieldName:'textRepresentation', type: 'text');
$this->addType(fieldName:'updated', type: 'datetime');
$this->addType(fieldName:'created', type: 'datetime');
}
Expand Down Expand Up @@ -62,17 +64,22 @@ public function hydrate(array $object): self

public function jsonSerialize(): array
{
// return [
// 'id' => $this->id,
// 'uuid' => $this->uuid,
// 'version' => $this->version,
// 'register' => $this->register,
// 'schema' => $this->schema,
// 'object' => $this->object,
// 'updated' => isset($this->updated) ? $this->updated->format('c') : null,
// 'created' => isset($this->created) ? $this->created->format('c') : null
// ];

return $this->object;
}

public function getObjectArray(): array
{
return [
'id' => $this->id,
'uuid' => $this->uuid,
'version' => $this->version,
'register' => $this->register,
'schema' => $this->schema,
'object' => $this->object,
'textRepresentation' => $this->textRepresentation,
'updated' => isset($this->updated) ? $this->updated->format('c') : null,
'created' => isset($this->created) ? $this->created->format('c') : null
];
}

}
13 changes: 8 additions & 5 deletions lib/Db/ObjectEntityMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,25 @@ public function __construct(IDBConnection $db, MySQLJsonService $mySQLJsonServic
}

/**
* Find an object by ID
* Find an object by ID or UUID
*
* @param int $id The ID of the object to find
* @param int|string $idOrUuid The ID or UUID of the object to find
* @return ObjectEntity The ObjectEntity
*/
public function find(int $id): ObjectEntity
public function find($idOrUuid): ObjectEntity
{
$qb = $this->db->getQueryBuilder();

$qb->select('*')
->from('openregister_objects')
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))
$qb->expr()->orX(
$qb->expr()->eq('id', $qb->createNamedParameter($idOrUuid, IQueryBuilder::PARAM_INT)),
$qb->expr()->eq('uuid', $qb->createNamedParameter($idOrUuid, IQueryBuilder::PARAM_STR))
)
);

return $this->findEntity(query: $qb);
return $this->findEntity($qb);
}

/**
Expand Down
74 changes: 74 additions & 0 deletions lib/Migration/Version1Date20241020231700.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\OpenRegister\Migration;

use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

class Version1Date20241020231700 extends SimpleMigrationStep {

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
*/
public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
}

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();

// create the openregister_logs table
if (!$schema->hasTable('openregister_audit_trails')) {
$table = $schema->createTable('openregister_audit_trails');
$table->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'notnull' => true]);
$table->addColumn('uuid', Types::STRING, ['notnull' => false, 'length' => 255]);
$table->addColumn('object', Types::INTEGER, ['notnull' => true]);
$table->addColumn('action', Types::STRING, ['notnull' => true, 'default' => 'update']);
$table->addColumn('changed', Types::JSON, ['notnull' => true]);
$table->addColumn('user', Types::STRING, ['notnull' => true, 'length' => 255]);
$table->addColumn('user_name', Types::STRING, ['notnull' => true, 'length' => 255]);
$table->addColumn('session', Types::STRING, ['notnull' => true, 'length' => 255]);
$table->addColumn('request', Types::STRING, ['notnull' => false, 'length' => 255]);
$table->addColumn('ip_address', Types::STRING, ['notnull' => false, 'length' => 255]);
$table->addColumn('created', Types::DATETIME, ['notnull' => true]);

$table->setPrimaryKey(['id']);
$table->addIndex(['user'], 'openregister_logs_user_index');
$table->addIndex(['uuid'], 'openregister_logs_uuid_index');
}

///Update the openregister_objects table
$table = $schema->getTable('openregister_objects');
if (!$table->hasColumn('text_representation')) {
$table->addColumn(name: 'text_representation', typeName: Types::TEXT, options: ['notnull' => false]);
}

return $schema;
}

/**
* @param IOutput $output
* @param Closure(): ISchemaWrapper $schemaClosure
* @param array $options
*/
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
}
}
Loading

0 comments on commit 902dcec

Please sign in to comment.