120 lines
6.0 KiB
PHP
120 lines
6.0 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\V1;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\StoreSimulationRequest;
|
|
use App\Http\Requests\UpdateSimulationScenarioRequest;
|
|
use App\Models\QuestionnaireVersion;
|
|
use App\Models\SimulationJob;
|
|
use App\Policies\SimulationPolicy;
|
|
use App\Services\SimulationExecution;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
|
|
class SimulationController extends Controller
|
|
{
|
|
public function questionnaire(string $code): JsonResponse
|
|
{
|
|
abort_unless(preg_match('/^[a-z0-9][a-z0-9-]{0,79}$/', $code) === 1, 404);
|
|
$version = QuestionnaireVersion::query()->where('code', $code)->where('locale', 'pt-MZ')->where('status', 'published')
|
|
->where(fn ($query) => $query->whereNull('valid_from')->orWhereDate('valid_from', '<=', today()))
|
|
->where(fn ($query) => $query->whereNull('valid_to')->orWhereDate('valid_to', '>', today()))
|
|
->orderByDesc('version_no')->firstOrFail();
|
|
$etag = '"questionnaire-'.$version->id.'-v'.$version->version_no.'"';
|
|
|
|
return response()->json(['data' => $version])->header('ETag', $etag)->header('Cache-Control', 'private, max-age=60');
|
|
}
|
|
|
|
public function store(StoreSimulationRequest $request, SimulationExecution $execution): JsonResponse
|
|
{
|
|
$key = $request->header('Idempotency-Key');
|
|
abort_unless(is_string($key) && strlen($key) >= 16 && strlen($key) <= 128, 428, 'Idempotency-Key é obrigatório.');
|
|
$actorId = $request->user()?->id;
|
|
$token = $actorId === null ? $request->header('X-Simulation-Token') : null;
|
|
abort_if($actorId === null && (! is_string($token) || strlen($token) < 32 || strlen($token) > 256), 422, 'X-Simulation-Token opaco gerado pelo cliente é obrigatório para execução anónima.');
|
|
$validated = $request->validated();
|
|
$data = ['base' => (string) $validated['base'], 'rate' => (string) $validated['rate'],
|
|
'reduction' => (string) ($validated['reduction'] ?? '0'), 'organization_id' => $validated['organization_id'] ?? null];
|
|
$organizationId = $data['organization_id'] ?? null;
|
|
$execution->authorizeOrganization($actorId, $organizationId);
|
|
$result = $execution->create($data, $actorId, $organizationId, $token, $key);
|
|
$simulation = $result['simulation'];
|
|
$body = ['data' => $simulation, 'replayed' => $result['replayed']];
|
|
if ($actorId === null) {
|
|
$body['access_token'] = $token;
|
|
}
|
|
|
|
return response()->json($body, $result['replayed'] ? 200 : 202)
|
|
->header('ETag', $execution->etag($simulation))
|
|
->header('Location', url('/api/v1/simulations/'.$simulation->id));
|
|
}
|
|
|
|
public function show(Request $request, string $id, SimulationExecution $execution): JsonResponse
|
|
{
|
|
abort_unless(Str::isUuid($id), 404);
|
|
$simulation = $execution->find($id, $request);
|
|
if ($request->user()) {
|
|
abort_unless(app(SimulationPolicy::class)->view($request->user(), $simulation), 404);
|
|
} else {
|
|
abort_unless($simulation->anonymous_token_hash !== null && hash_equals($simulation->anonymous_token_hash, (string) $execution->tokenHash($request)), 404);
|
|
}
|
|
|
|
return response()->json(['data' => $simulation])->header('ETag', $execution->etag($simulation));
|
|
}
|
|
|
|
public function updateScenario(UpdateSimulationScenarioRequest $request, string $id, string $scenarioId, SimulationExecution $execution): JsonResponse
|
|
{
|
|
abort_unless(Str::isUuid($id) && Str::isUuid($scenarioId), 404);
|
|
$simulation = $execution->find($id, $request);
|
|
if ($request->user()) {
|
|
abort_unless(app(SimulationPolicy::class)->update($request->user(), $simulation), 403);
|
|
} else {
|
|
abort_unless($simulation->anonymous_token_hash !== null && hash_equals($simulation->anonymous_token_hash, (string) $execution->tokenHash($request)), 404);
|
|
}
|
|
$validated = $request->validated();
|
|
$data = ['label' => (string) $validated['label'], 'base' => (string) $validated['base'], 'rate' => (string) $validated['rate'],
|
|
'reduction' => (string) ($validated['reduction'] ?? '0')];
|
|
$updated = $execution->updateScenario($id, $scenarioId, $data, $request);
|
|
|
|
return response()->json(['data' => $updated], 202)->header('ETag', $execution->etag($updated));
|
|
}
|
|
|
|
public function report(Request $request, string $id, string $reportId, SimulationExecution $execution): JsonResponse
|
|
{
|
|
abort_unless(Str::isUuid($id) && Str::isUuid($reportId), 404);
|
|
$simulation = $execution->find($id, $request);
|
|
if ($request->user()) {
|
|
abort_unless(app(SimulationPolicy::class)->view($request->user(), $simulation), 404);
|
|
} else {
|
|
abort_unless($simulation->anonymous_token_hash !== null && hash_equals($simulation->anonymous_token_hash, (string) $execution->tokenHash($request)), 404);
|
|
}
|
|
$report = $simulation->reports->firstWhere('id', $reportId);
|
|
abort_if($report === null, 404);
|
|
|
|
return response()->json(['data' => $report]);
|
|
}
|
|
|
|
public function jobs(Request $request, string $id, SimulationExecution $execution): JsonResponse
|
|
{
|
|
abort_unless(Str::isUuid($id), 404);
|
|
$simulation = $execution->find($id, $request);
|
|
if ($request->user()) {
|
|
abort_unless(app(SimulationPolicy::class)->view($request->user(), $simulation), 404);
|
|
} else {
|
|
abort_unless($simulation->anonymous_token_hash !== null && hash_equals($simulation->anonymous_token_hash, (string) $execution->tokenHash($request)), 404);
|
|
}
|
|
|
|
$jobs = DB::transaction(function () use ($request, $id, $execution) {
|
|
$execution->setContext($request->user()?->id, $execution->tokenHash($request));
|
|
|
|
return SimulationJob::query()->where('simulation_id', $id)
|
|
->orderBy('created_at')->orderBy('id')->get();
|
|
});
|
|
|
|
return response()->json(['data' => $jobs]);
|
|
}
|
|
}
|