feat: import

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-06-28 18:06:12 -04:00
parent 0880bba063
commit b7e12621cd
16 changed files with 968 additions and 11 deletions
+8 -4
View File
@@ -23,6 +23,8 @@ use KTXF\Resource\Identifier\ResourceIdentifier;
use KTXF\Resource\Identifier\ResourceIdentifiers;
use KTXF\Resource\Identifier\ServiceIdentifier;
use KTXF\Routing\Attributes\AuthenticatedRoute;
use KTXM\ChronoManager\Import\ImportOptions;
use KTXM\ChronoManager\Import\ImportService;
use KTXM\ChronoManager\Manager;
use KTXM\ChronoManager\Stream\ExpectedTotal;
use Psr\Log\LoggerInterface;
@@ -50,6 +52,7 @@ class DefaultController extends ControllerAbstract {
private readonly SessionTenant $tenantIdentity,
private readonly SessionIdentity $userIdentity,
private readonly Manager $manager,
private readonly ImportService $importService,
private readonly LoggerInterface $logger,
) {}
@@ -152,6 +155,7 @@ class DefaultController extends ControllerAbstract {
'entity.update' => $this->entityUpdate($tenantId, $userId, $data),
'entity.delete' => $this->entityDelete($tenantId, $userId, $data),
'entity.move' => $this->entityMove($tenantId, $userId, $data),
'entity.import' => $this->entityImport($tenantId, $userId, $data, $version, $transaction),
default => throw new InvalidArgumentException(self::ERR_INVALID_OPERATION . $operation)
};
@@ -554,9 +558,9 @@ class DefaultController extends ControllerAbstract {
}
/**
* Import vCards into a collection, streaming one NDJSON event per contact.
* Import iCalendar events into a calendar, streaming one NDJSON event per event.
*
* Request data: { target: "provider:service:collection", data: "<raw vCard string>", options?: {...} }
* Request data: { target: "provider:service:collection", data: "<raw iCalendar string>", options?: {...} }
*/
private function entityImport(string $tenantId, string $userId, array $data, int $version, string $transaction): StreamedNdJsonResponse {
@@ -578,7 +582,7 @@ class DefaultController extends ControllerAbstract {
$options = ImportOptions::fromArray($data['options'] ?? []);
// Spill the payload to a temp file and release the in-memory copy before the
// (potentially long) streaming parse, so peak memory stays at one contact object.
// (potentially long) parse and create pass.
$tempFile = tmpfile();
if ($tempFile === false) {
throw new \RuntimeException('Unable to allocate temporary file for import');
@@ -803,4 +807,4 @@ class DefaultController extends ControllerAbstract {
return $this->manager->entityMove($tenantId, $userId, $target, ...$sources->all());
}
}
}