|
@@ -7,6 +7,7 @@ namespace App\Application\Admin;
|
|
|
use App\Domain\Audit\AuditAction;
|
|
use App\Domain\Audit\AuditAction;
|
|
|
use App\Domain\Audit\AuditEmitter;
|
|
use App\Domain\Audit\AuditEmitter;
|
|
|
use App\Infrastructure\Reporter\ReporterRepository;
|
|
use App\Infrastructure\Reporter\ReporterRepository;
|
|
|
|
|
+use Doctrine\DBAL\Connection;
|
|
|
use Psr\Http\Message\ResponseInterface;
|
|
use Psr\Http\Message\ResponseInterface;
|
|
|
use Psr\Http\Message\ServerRequestInterface;
|
|
use Psr\Http\Message\ServerRequestInterface;
|
|
|
|
|
|
|
@@ -26,6 +27,7 @@ final class ReportersController
|
|
|
public function __construct(
|
|
public function __construct(
|
|
|
private readonly ReporterRepository $reporters,
|
|
private readonly ReporterRepository $reporters,
|
|
|
private readonly AuditEmitter $audit,
|
|
private readonly AuditEmitter $audit,
|
|
|
|
|
+ private readonly Connection $connection,
|
|
|
) {
|
|
) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -100,21 +102,27 @@ final class ReportersController
|
|
|
return self::validationFailed($response, $errors);
|
|
return self::validationFailed($response, $errors);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- $id = $this->reporters->create($name, $description, $trustWeight, self::actingUserId($request));
|
|
|
|
|
|
|
+ $userId = self::actingUserId($request);
|
|
|
|
|
+ $auditCtx = self::auditContext($request);
|
|
|
|
|
+ $id = $this->connection->transactional(function () use ($name, $description, $trustWeight, $userId, $auditCtx): int {
|
|
|
|
|
+ $id = $this->reporters->create($name, $description, $trustWeight, $userId);
|
|
|
|
|
+ $this->audit->emitOrThrow(
|
|
|
|
|
+ AuditAction::REPORTER_CREATED,
|
|
|
|
|
+ 'reporter',
|
|
|
|
|
+ $id,
|
|
|
|
|
+ ['name' => $name, 'trust_weight' => $trustWeight, 'description' => $description],
|
|
|
|
|
+ $auditCtx,
|
|
|
|
|
+ $name,
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ return $id;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
$created = $this->reporters->findById($id);
|
|
$created = $this->reporters->findById($id);
|
|
|
if ($created === null) {
|
|
if ($created === null) {
|
|
|
return self::error($response, 500, 'create_failed');
|
|
return self::error($response, 500, 'create_failed');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- $this->audit->emit(
|
|
|
|
|
- AuditAction::REPORTER_CREATED,
|
|
|
|
|
- 'reporter',
|
|
|
|
|
- $id,
|
|
|
|
|
- ['name' => $name, 'trust_weight' => $trustWeight, 'description' => $description],
|
|
|
|
|
- self::auditContext($request),
|
|
|
|
|
- $name,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
return self::json($response, 201, $created->toArray());
|
|
return self::json($response, 201, $created->toArray());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -195,21 +203,25 @@ final class ReportersController
|
|
|
'audit_enabled' => $existing->auditEnabled ? 1 : 0,
|
|
'audit_enabled' => $existing->auditEnabled ? 1 : 0,
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
- $this->reporters->update($id, $fields);
|
|
|
|
|
|
|
+ $auditCtx = self::auditContext($request);
|
|
|
|
|
+ $this->connection->transactional(function () use ($id, $fields, $existing, $beforeSnapshot, $auditCtx): void {
|
|
|
|
|
+ $this->reporters->update($id, $fields);
|
|
|
|
|
+ $updatedName = isset($fields['name']) ? (string) $fields['name'] : $existing->name;
|
|
|
|
|
+ $this->audit->emitOrThrow(
|
|
|
|
|
+ AuditAction::REPORTER_UPDATED,
|
|
|
|
|
+ 'reporter',
|
|
|
|
|
+ $id,
|
|
|
|
|
+ ['name' => $existing->name, 'changes' => self::diffFields($beforeSnapshot, $fields)],
|
|
|
|
|
+ $auditCtx,
|
|
|
|
|
+ $updatedName,
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
$updated = $this->reporters->findById($id);
|
|
$updated = $this->reporters->findById($id);
|
|
|
if ($updated === null) {
|
|
if ($updated === null) {
|
|
|
return self::error($response, 500, 'update_failed');
|
|
return self::error($response, 500, 'update_failed');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- $this->audit->emit(
|
|
|
|
|
- AuditAction::REPORTER_UPDATED,
|
|
|
|
|
- 'reporter',
|
|
|
|
|
- $id,
|
|
|
|
|
- ['name' => $existing->name, 'changes' => self::diffFields($beforeSnapshot, $fields)],
|
|
|
|
|
- self::auditContext($request),
|
|
|
|
|
- $updated->name,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
return self::json($response, 200, $updated->toArray());
|
|
return self::json($response, 200, $updated->toArray());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -227,17 +239,21 @@ final class ReportersController
|
|
|
return self::error($response, 404, 'not_found');
|
|
return self::error($response, 404, 'not_found');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ $auditCtx = self::auditContext($request);
|
|
|
|
|
+
|
|
|
if ($this->reporters->reportCount($id) > 0) {
|
|
if ($this->reporters->reportCount($id) > 0) {
|
|
|
// SPEC: refuse hard delete when reports exist; flip to inactive.
|
|
// SPEC: refuse hard delete when reports exist; flip to inactive.
|
|
|
- $this->reporters->softDelete($id);
|
|
|
|
|
- $this->audit->emit(
|
|
|
|
|
- AuditAction::REPORTER_DELETED,
|
|
|
|
|
- 'reporter',
|
|
|
|
|
- $id,
|
|
|
|
|
- ['name' => $existing->name, 'soft' => true, 'reason' => 'has_reports'],
|
|
|
|
|
- self::auditContext($request),
|
|
|
|
|
- $existing->name,
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ $this->connection->transactional(function () use ($id, $existing, $auditCtx): void {
|
|
|
|
|
+ $this->reporters->softDelete($id);
|
|
|
|
|
+ $this->audit->emitOrThrow(
|
|
|
|
|
+ AuditAction::REPORTER_DELETED,
|
|
|
|
|
+ 'reporter',
|
|
|
|
|
+ $id,
|
|
|
|
|
+ ['name' => $existing->name, 'soft' => true, 'reason' => 'has_reports'],
|
|
|
|
|
+ $auditCtx,
|
|
|
|
|
+ $existing->name,
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
return self::json($response, 409, [
|
|
return self::json($response, 409, [
|
|
|
'error' => 'has_reports',
|
|
'error' => 'has_reports',
|
|
@@ -245,15 +261,17 @@ final class ReportersController
|
|
|
]);
|
|
]);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- $this->reporters->softDelete($id);
|
|
|
|
|
- $this->audit->emit(
|
|
|
|
|
- AuditAction::REPORTER_DELETED,
|
|
|
|
|
- 'reporter',
|
|
|
|
|
- $id,
|
|
|
|
|
- ['name' => $existing->name, 'soft' => true],
|
|
|
|
|
- self::auditContext($request),
|
|
|
|
|
- $existing->name,
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ $this->connection->transactional(function () use ($id, $existing, $auditCtx): void {
|
|
|
|
|
+ $this->reporters->softDelete($id);
|
|
|
|
|
+ $this->audit->emitOrThrow(
|
|
|
|
|
+ AuditAction::REPORTER_DELETED,
|
|
|
|
|
+ 'reporter',
|
|
|
|
|
+ $id,
|
|
|
|
|
+ ['name' => $existing->name, 'soft' => true],
|
|
|
|
|
+ $auditCtx,
|
|
|
|
|
+ $existing->name,
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
return $response->withStatus(204);
|
|
return $response->withStatus(204);
|
|
|
}
|
|
}
|