Files
Investidor-V2/app/Http/Controllers/Api/V1/SimulationFormDraftController.php
T
nala e65cb26901
tests / ci (push) Canceled after 0s
varias alteracoes
2026-10-06 14:50:12 +02:00

189 lines
9.6 KiB
PHP

<?php
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Controller;
use App\Http\Requests\AttachSimulationToDraftRequest;
use App\Http\Requests\ClaimSimulationFormDraftRequest;
use App\Http\Requests\LinkSimulationDraftBusinessRequest;
use App\Http\Requests\StoreSimulationFormDraftRequest;
use App\Policies\SimulationPolicy;
use App\Services\SimulationExecution;
use App\Services\SimulationFormDrafts;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class SimulationFormDraftController extends Controller
{
public function store(StoreSimulationFormDraftRequest $request, SimulationFormDrafts $drafts): JsonResponse
{
$key = $request->header('Idempotency-Key');
abort_unless(is_string($key) && strlen($key) >= 16 && strlen($key) <= 128, 428, 'Idempotency-Key é obrigatório.');
$result = $drafts->create($request->snapshots(), $request, $key);
$draft = $result['draft'];
return response()->json(['data' => ['id' => $draft->id, 'version_no' => $draft->version_no, 'replayed' => $result['replayed']], 'expires_at' => $draft->expires_at?->toISOString()], $result['replayed'] ? 200 : 201)
->header('ETag', $drafts->etag($draft))->header('Cache-Control', 'no-store, private');
}
public function index(Request $request, SimulationFormDrafts $drafts): JsonResponse
{
abort_unless($request->user() !== null, 401);
return response()->json(['data' => $drafts->ownedDrafts($request->user())])->header('Cache-Control', 'no-store, private');
}
public function claimPreview(Request $request, SimulationFormDrafts $drafts): JsonResponse
{
abort_unless($request->user() !== null, 401);
return response()->json(['data' => $drafts->claimPreview($request)])->header('Cache-Control', 'no-store, private');
}
public function claim(ClaimSimulationFormDraftRequest $request, SimulationFormDrafts $drafts): JsonResponse
{
$result = $drafts->claim($request, $request->validated('choice'), $request->validated('existing_draft_id'));
$draft = $result['draft'];
return response()->json(['data' => $drafts->present($draft, true), 'choice' => $result['choice'], 'replayed' => $result['replayed'] ?? false, 'expires_at' => $draft->expires_at?->toISOString()])
->header('ETag', $drafts->etag($draft))->header('Cache-Control', 'no-store, private');
}
public function linkableBusinesses(Request $request, SimulationFormDrafts $drafts): JsonResponse
{
abort_unless($request->user()?->hasVerifiedEmail(), 403);
return response()->json(['data' => $drafts->linkableBusinesses($request->user())])->header('Cache-Control', 'no-store, private');
}
public function businessLink(Request $request, string $id, SimulationFormDrafts $drafts): JsonResponse
{
abort_unless(Str::isUuid($id) && $request->user()?->hasVerifiedEmail(), 404);
return response()->json(['data' => $drafts->activeBusinessLink($request->user(), $id)])->header('Cache-Control', 'no-store, private');
}
public function linkBusiness(LinkSimulationDraftBusinessRequest $request, string $id, SimulationFormDrafts $drafts): JsonResponse
{
abort_unless(Str::isUuid($id), 404);
$result = $drafts->linkBusiness($request->user(), $id, $request->validated('business_id'));
return response()->json(['data' => $result], isset($result['replayed']) ? 200 : 201)->header('Cache-Control', 'no-store, private');
}
public function unlinkBusiness(Request $request, string $id, SimulationFormDrafts $drafts): JsonResponse
{
abort_unless(Str::isUuid($id) && $request->user()?->hasVerifiedEmail(), 404);
return response()->json(['data' => $drafts->unlinkBusiness($request->user(), $id)])->header('Cache-Control', 'no-store, private');
}
public function current(Request $request, SimulationFormDrafts $drafts): JsonResponse
{
$draft = $drafts->current($request);
if (! $draft) {
return response()->json(['data' => null], 200)->header('Cache-Control', 'no-store, private');
}
return response()->json(['data' => $drafts->present($draft, $request->user() !== null)])->header('ETag', $drafts->etag($draft))->header('Cache-Control', 'no-store, private');
}
public function forgetCurrent(Request $request, SimulationFormDrafts $drafts): JsonResponse
{
$drafts->forgetCurrent($request);
return response()->json([], 204)->header('Cache-Control', 'no-store, private');
}
public function show(Request $request, string $id, SimulationFormDrafts $drafts): JsonResponse
{
abort_unless(Str::isUuid($id), 404);
$draft = $drafts->find($id, $request);
return response()->json(['data' => $drafts->present($draft, $request->user() !== null)])->header('ETag', $drafts->etag($draft))->header('Cache-Control', 'no-store, private');
}
public function update(StoreSimulationFormDraftRequest $request, string $id, SimulationFormDrafts $drafts): JsonResponse
{
abort_unless(Str::isUuid($id), 404);
$draft = $drafts->update($id, $request->snapshots(), $request);
return response()->json(['data' => ['id' => $draft->id, 'version_no' => $draft->version_no], 'expires_at' => $draft->expires_at?->toISOString()])
->header('ETag', $drafts->etag($draft))->header('Cache-Control', 'no-store, private');
}
public function renew(Request $request, string $id, SimulationFormDrafts $drafts): JsonResponse
{
abort_unless(Str::isUuid($id), 404);
$draft = $drafts->renew($id, $request);
return response()->json(['data' => ['id' => $draft->id, 'version_no' => $draft->version_no], 'expires_at' => $draft->expires_at?->toISOString()])
->header('ETag', $drafts->etag($draft))->header('Cache-Control', 'no-store, private');
}
public function attachSimulation(AttachSimulationToDraftRequest $request, string $id, SimulationFormDrafts $drafts, SimulationExecution $execution): JsonResponse
{
abort_unless(Str::isUuid($id), 404);
$validated = $request->validated();
$simulation = $execution->find($validated['simulation_id'], $request);
$tokenOwned = $simulation->anonymous_token_hash !== null
&& hash_equals($simulation->anonymous_token_hash, (string) $execution->tokenHash($request));
if ($request->user()) {
$policy = app(SimulationPolicy::class);
$canTransfer = $simulation->organization_id === null
? $policy->view($request->user(), $simulation)
: $policy->update($request->user(), $simulation);
abort_unless($canTransfer || $tokenOwned, 404);
} else {
abort_unless($tokenOwned, 404);
}
abort_unless($simulation->status === 'completed' && is_array($simulation->result_snapshot), 409, 'Só é possível reutilizar uma simulação concluída.');
$report = $simulation->reports->where('status', 'ready')->sortByDesc('created_at')->first();
abort_unless($report !== null && is_array($report->report_snapshot), 409, 'O relatório versionado da simulação ainda não está disponível.');
abort_unless(preg_match('/^[a-f0-9]{64}$/D', $report->report_sha256) === 1, 409,
'O relatório da simulação não tem um hash verificável.');
$canonicalSnapshots = DB::selectOne('select CAST(? AS jsonb)::text as report_canonical, CAST(? AS jsonb)::text as result_canonical', [
json_encode($report->report_snapshot, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE),
json_encode($simulation->result_snapshot, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE),
]);
abort_unless(hash_equals($report->report_sha256, hash('sha256', $canonicalSnapshots->report_canonical))
&& hash_equals($canonicalSnapshots->report_canonical, $canonicalSnapshots->result_canonical), 409,
'O relatório não corresponde ao resultado versionado da simulação.');
$inputSnapshot = array_intersect_key($simulation->input_snapshot, array_flip(['base', 'rate', 'reduction']));
$sourceSnapshot = [
'simulation_id' => $simulation->id,
'source_version_no' => $simulation->version_no,
'source_created_at' => $simulation->created_at?->toISOString(),
'source_updated_at' => $simulation->updated_at?->toISOString(),
'input_snapshot' => $inputSnapshot,
'calculation_snapshot' => $simulation->calculation_snapshot,
'result_snapshot' => $simulation->result_snapshot,
'result_sha256' => $report->report_sha256,
'report_id' => $report->id,
'report_version_no' => $report->version_no,
'report_sha256' => $report->report_sha256,
'report_snapshot' => $report->report_snapshot,
'consented_at' => now()->toISOString(),
];
$attached = $drafts->attachSimulation($id, $sourceSnapshot, $request);
$draft = $attached['draft'];
return response()->json([
'data' => $drafts->present($draft, $request->user() !== null),
'replayed' => $attached['replayed'],
'expires_at' => $draft->expires_at?->toISOString(),
])->header('ETag', $drafts->etag($draft))->header('Cache-Control', 'no-store, private');
}
public function destroy(Request $request, string $id, SimulationFormDrafts $drafts): JsonResponse
{
abort_unless(Str::isUuid($id), 404);
$drafts->delete($id, $request);
return response()->json([], 204)->header('Cache-Control', 'no-store, private');
}
}