103 lines
4.1 KiB
PHP
103 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace Modules\Core\Privacy\Jobs;
|
|
|
|
use Throwable;
|
|
use Illuminate\Bus\Queueable;
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
use Illuminate\Foundation\Bus\Dispatchable;
|
|
use Illuminate\Queue\InteractsWithQueue;
|
|
use Illuminate\Queue\SerializesModels;
|
|
use Illuminate\Support\Facades\Event;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Modules\Core\Privacy\Contracts\PersonalDataProvider;
|
|
use Modules\Core\Privacy\DTOs\CustomerSubject;
|
|
use Modules\Core\Privacy\Events\PersonalDataGathered;
|
|
use Modules\Core\Privacy\DTOs\ExportReport;
|
|
use Modules\Core\Privacy\DTOs\ProviderExportResult;
|
|
use Modules\Core\Privacy\Enums\ExportRequestStatus;
|
|
use Modules\Core\Privacy\Models\DataExportRequest;
|
|
use Modules\Core\Privacy\Services\PrivacyManager;
|
|
use Modules\Core\Privacy\DTOs\UserSubject;
|
|
|
|
/**
|
|
* Gathers every registered PersonalDataProvider's export data for one request, all
|
|
* sequentially in this single job — deliberately not fanned out into one job per
|
|
* provider. Per-subject export work is small (a handful of indexed queries per
|
|
* provider), so there's no real parallelism win, and one job means "finished" is
|
|
* just "handle() returned," with no Bus::batch()/completion-counting needed. If a
|
|
* future provider ever does something genuinely slow (an external API call, a
|
|
* generated PDF), that's the point to reconsider — not before.
|
|
*
|
|
* Calls each provider's *ForCustomer() or *ForUser() method depending on the
|
|
* request's polymorphic subject — see Modules\Core\Privacy\Services\PrivacyService and
|
|
* docs/privacy.md "User-scope vs Customer-scope".
|
|
*
|
|
* Writing the gathered data to a file is intentionally NOT done here — see
|
|
* PersonalDataGathered and Modules\Core\Privacy\Listeners\WriteExportToCsvListener,
|
|
* which keeps the export *format* swappable without touching how data is gathered.
|
|
*/
|
|
class ExportDataSubjectJob implements ShouldQueue
|
|
{
|
|
use Dispatchable;
|
|
use InteractsWithQueue;
|
|
use Queueable;
|
|
use SerializesModels;
|
|
|
|
public function __construct(
|
|
public readonly DataExportRequest $request,
|
|
) {}
|
|
|
|
public function handle(PrivacyManager $manager): void
|
|
{
|
|
if ($this->request->isForCustomer()) {
|
|
$subject = new CustomerSubject(customerId: $this->request->subject_id);
|
|
$results = array_map(
|
|
fn (PersonalDataProvider $provider) => $this->safeExport($provider, 'exportForCustomer', $subject),
|
|
$manager->providers()
|
|
);
|
|
} else {
|
|
$subject = new UserSubject(userId: $this->request->subject_id, email: $this->request->email);
|
|
$results = array_map(
|
|
fn (PersonalDataProvider $provider) => $this->safeExport($provider, 'exportForUser', $subject),
|
|
$manager->providers()
|
|
);
|
|
}
|
|
|
|
Event::dispatch(new PersonalDataGathered(
|
|
$this->request,
|
|
new ExportReport($subject, $results)
|
|
));
|
|
}
|
|
|
|
public function failed(Throwable $exception): void
|
|
{
|
|
$this->request->update(['status' => ExportRequestStatus::Failed]);
|
|
}
|
|
|
|
/**
|
|
* Catches per-provider so one provider throwing doesn't discard every
|
|
* other provider's already-gathered export data for this same request —
|
|
* without this, the whole array_map aborts, handle() never reaches
|
|
* Event::dispatch(), and failed() marks the ENTIRE request Failed even
|
|
* though most providers may have already gathered their data
|
|
* successfully. Logged via Log::error() so a thrown provider is still
|
|
* visible to staff, not just an empty/missing section in the export.
|
|
*
|
|
* @param 'exportForCustomer'|'exportForUser' $method
|
|
*/
|
|
private function safeExport(PersonalDataProvider $provider, string $method, CustomerSubject|UserSubject $subject): ProviderExportResult
|
|
{
|
|
try {
|
|
return $provider->{$method}($subject);
|
|
} catch (Throwable $e) {
|
|
Log::error("Privacy provider {$provider->name()}::{$method}() threw during export", [
|
|
'provider' => $provider->name(),
|
|
'exception' => $e,
|
|
]);
|
|
|
|
return new ProviderExportResult($provider->name(), [], $e->getMessage());
|
|
}
|
|
}
|
|
}
|